1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.companion;
18 
19 import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal;
20 import static android.companion.BluetoothDeviceFilterUtils.matchesAddress;
21 import static android.companion.BluetoothDeviceFilterUtils.matchesName;
22 import static android.companion.BluetoothDeviceFilterUtils.matchesServiceUuids;
23 import static android.companion.BluetoothDeviceFilterUtils.patternFromString;
24 import static android.companion.BluetoothDeviceFilterUtils.patternToString;
25 
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.bluetooth.BluetoothDevice;
29 import android.compat.annotation.UnsupportedAppUsage;
30 import android.os.Build;
31 import android.os.Parcel;
32 import android.os.ParcelUuid;
33 import android.provider.OneTimeUseBuilder;
34 
35 import com.android.internal.util.ArrayUtils;
36 import com.android.internal.util.CollectionUtils;
37 
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.Objects;
41 import java.util.regex.Pattern;
42 
43 /**
44  * A filter for Bluetooth(non-LE) devices
45  */
46 public final class BluetoothDeviceFilter implements DeviceFilter<BluetoothDevice> {
47 
48     private final Pattern mNamePattern;
49     private final String mAddress;
50     private final List<ParcelUuid> mServiceUuids;
51     private final List<ParcelUuid> mServiceUuidMasks;
52 
BluetoothDeviceFilter( Pattern namePattern, String address, List<ParcelUuid> serviceUuids, List<ParcelUuid> serviceUuidMasks)53     private BluetoothDeviceFilter(
54             Pattern namePattern,
55             String address,
56             List<ParcelUuid> serviceUuids,
57             List<ParcelUuid> serviceUuidMasks) {
58         mNamePattern = namePattern;
59         mAddress = address;
60         mServiceUuids = CollectionUtils.emptyIfNull(serviceUuids);
61         mServiceUuidMasks = CollectionUtils.emptyIfNull(serviceUuidMasks);
62     }
63 
BluetoothDeviceFilter(Parcel in)64     private BluetoothDeviceFilter(Parcel in) {
65         this(
66             patternFromString(in.readString()),
67             in.readString(),
68             readUuids(in),
69             readUuids(in));
70     }
71 
readUuids(Parcel in)72     private static List<ParcelUuid> readUuids(Parcel in) {
73         return in.readParcelableList(new ArrayList<>(), ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
74     }
75 
76     /** @hide */
77     @Override
matches(BluetoothDevice device)78     public boolean matches(BluetoothDevice device) {
79         return matchesAddress(mAddress, device)
80                 && matchesServiceUuids(mServiceUuids, mServiceUuidMasks, device)
81                 && matchesName(getNamePattern(), device);
82     }
83 
84     /** @hide */
85     @Override
getDeviceDisplayName(BluetoothDevice device)86     public String getDeviceDisplayName(BluetoothDevice device) {
87         return getDeviceDisplayNameInternal(device);
88     }
89 
90     /** @hide */
91     @Override
getMediumType()92     public int getMediumType() {
93         return DeviceFilter.MEDIUM_TYPE_BLUETOOTH;
94     }
95 
96     /** @hide */
97     @Nullable
getNamePattern()98     public Pattern getNamePattern() {
99         return mNamePattern;
100     }
101 
102     /** @hide */
103     @Nullable
104     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getAddress()105     public String getAddress() {
106         return mAddress;
107     }
108 
109     /** @hide */
110     @NonNull
getServiceUuids()111     public List<ParcelUuid> getServiceUuids() {
112         return mServiceUuids;
113     }
114 
115     /** @hide */
116     @NonNull
getServiceUuidMasks()117     public List<ParcelUuid> getServiceUuidMasks() {
118         return mServiceUuidMasks;
119     }
120 
121     @Override
writeToParcel(Parcel dest, int flags)122     public void writeToParcel(Parcel dest, int flags) {
123         dest.writeString(patternToString(getNamePattern()));
124         dest.writeString(mAddress);
125         dest.writeParcelableList(mServiceUuids, flags);
126         dest.writeParcelableList(mServiceUuidMasks, flags);
127     }
128 
129     @Override
equals(@ullable Object o)130     public boolean equals(@Nullable Object o) {
131         if (this == o) return true;
132         if (o == null || getClass() != o.getClass()) return false;
133         BluetoothDeviceFilter that = (BluetoothDeviceFilter) o;
134         return Objects.equals(mNamePattern, that.mNamePattern) &&
135                 Objects.equals(mAddress, that.mAddress) &&
136                 Objects.equals(mServiceUuids, that.mServiceUuids) &&
137                 Objects.equals(mServiceUuidMasks, that.mServiceUuidMasks);
138     }
139 
140     @Override
hashCode()141     public int hashCode() {
142         return Objects.hash(mNamePattern, mAddress, mServiceUuids, mServiceUuidMasks);
143     }
144 
145     @Override
toString()146     public String toString() {
147         return "BluetoothDeviceFilter{"
148                 + "mNamePattern=" + mNamePattern
149                 + ", mAddress='" + mAddress + '\''
150                 + ", mServiceUuids=" + mServiceUuids
151                 + ", mServiceUuidMasks=" + mServiceUuidMasks
152                 + '}';
153     }
154 
155     @Override
describeContents()156     public int describeContents() {
157         return 0;
158     }
159 
160     public static final @android.annotation.NonNull Creator<BluetoothDeviceFilter> CREATOR
161             = new Creator<BluetoothDeviceFilter>() {
162         @Override
163         public BluetoothDeviceFilter createFromParcel(Parcel in) {
164             return new BluetoothDeviceFilter(in);
165         }
166 
167         @Override
168         public BluetoothDeviceFilter[] newArray(int size) {
169             return new BluetoothDeviceFilter[size];
170         }
171     };
172 
173     /**
174      * A builder for {@link BluetoothDeviceFilter}
175      */
176     public static final class Builder extends OneTimeUseBuilder<BluetoothDeviceFilter> {
177         private Pattern mNamePattern;
178         private String mAddress;
179         private ArrayList<ParcelUuid> mServiceUuid;
180         private ArrayList<ParcelUuid> mServiceUuidMask;
181 
182         /**
183          * @param regex if set, only devices with {@link BluetoothDevice#getName name} matching the
184          *              given regular expression will be shown
185          */
setNamePattern(@ullable Pattern regex)186         public Builder setNamePattern(@Nullable Pattern regex) {
187             checkNotUsed();
188             mNamePattern = regex;
189             return this;
190         }
191 
192         /**
193          * @param address if set, only devices with MAC address exactly matching the given one will
194          *                pass the filter
195          */
196         @NonNull
setAddress(@ullable String address)197         public Builder setAddress(@Nullable String address) {
198             checkNotUsed();
199             mAddress = address;
200             return this;
201         }
202 
203         /**
204          * Add filtering by certain bits of {@link BluetoothDevice#getUuids()}
205          *
206          * A device with any uuid matching the given bits is considered passing
207          *
208          * @param serviceUuid the values for the bits to match
209          * @param serviceUuidMask if provided, only those bits would have to match.
210          */
211         @NonNull
addServiceUuid( @ullable ParcelUuid serviceUuid, @Nullable ParcelUuid serviceUuidMask)212         public Builder addServiceUuid(
213                 @Nullable ParcelUuid serviceUuid, @Nullable ParcelUuid serviceUuidMask) {
214             checkNotUsed();
215             mServiceUuid = ArrayUtils.add(mServiceUuid, serviceUuid);
216             mServiceUuidMask = ArrayUtils.add(mServiceUuidMask, serviceUuidMask);
217             return this;
218         }
219 
220         /** @inheritDoc */
221         @Override
222         @NonNull
build()223         public BluetoothDeviceFilter build() {
224             markUsed();
225             return new BluetoothDeviceFilter(
226                     mNamePattern, mAddress, mServiceUuid, mServiceUuidMask);
227         }
228     }
229 }
230