blob: 038994fb55357d3eb2c999dd609392e679df139f [file] [log] [blame]
Wei Wangadf6aff2014-05-20 06:30:20 +00001/*
2 * Copyright (C) 2014 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
Wei Wang6d811182014-05-22 12:10:25 -070017package android.bluetooth.le;
Wei Wangadf6aff2014-05-20 06:30:20 +000018
Zach Johnsondf512f12019-04-19 14:13:03 -070019import android.annotation.NonNull;
Wei Wangadf6aff2014-05-20 06:30:20 +000020import android.annotation.Nullable;
Wei Wang6d811182014-05-22 12:10:25 -070021import android.bluetooth.BluetoothAdapter;
22import android.bluetooth.BluetoothDevice;
Wei Wangadf6aff2014-05-20 06:30:20 +000023import android.os.Parcel;
24import android.os.ParcelUuid;
25import android.os.Parcelable;
26
Eugene Susla36e866b2017-02-23 18:24:39 -080027import com.android.internal.util.BitUtils;
28
Wei Wangadf6aff2014-05-20 06:30:20 +000029import java.util.Arrays;
30import java.util.List;
31import java.util.Objects;
32import java.util.UUID;
33
34/**
Wei Wang685c17582014-07-16 22:02:03 -070035 * Criteria for filtering result from Bluetooth LE scans. A {@link ScanFilter} allows clients to
36 * restrict scan results to only those that are of interest to them.
Wei Wangadf6aff2014-05-20 06:30:20 +000037 * <p>
38 * Current filtering on the following fields are supported:
39 * <li>Service UUIDs which identify the bluetooth gatt services running on the device.
40 * <li>Name of remote Bluetooth LE device.
41 * <li>Mac address of the remote device.
Wei Wangadf6aff2014-05-20 06:30:20 +000042 * <li>Service data which is the data associated with a service.
43 * <li>Manufacturer specific data which is the data associated with a particular manufacturer.
44 *
Wei Wang685c17582014-07-16 22:02:03 -070045 * @see ScanResult
Wei Wangadf6aff2014-05-20 06:30:20 +000046 * @see BluetoothLeScanner
47 */
Wei Wang6d811182014-05-22 12:10:25 -070048public final class ScanFilter implements Parcelable {
Wei Wangadf6aff2014-05-20 06:30:20 +000049
50 @Nullable
Wei Wangaf74e662014-07-09 14:03:42 -070051 private final String mDeviceName;
Wei Wangadf6aff2014-05-20 06:30:20 +000052
53 @Nullable
Wei Wangaf74e662014-07-09 14:03:42 -070054 private final String mDeviceAddress;
Wei Wangadf6aff2014-05-20 06:30:20 +000055
56 @Nullable
57 private final ParcelUuid mServiceUuid;
58 @Nullable
59 private final ParcelUuid mServiceUuidMask;
60
61 @Nullable
Nitin Shivpure1555eae2018-04-02 13:45:45 +053062 private final ParcelUuid mServiceSolicitationUuid;
63 @Nullable
64 private final ParcelUuid mServiceSolicitationUuidMask;
65
66 @Nullable
Wei Wang685c17582014-07-16 22:02:03 -070067 private final ParcelUuid mServiceDataUuid;
68 @Nullable
Wei Wangadf6aff2014-05-20 06:30:20 +000069 private final byte[] mServiceData;
70 @Nullable
71 private final byte[] mServiceDataMask;
72
73 private final int mManufacturerId;
74 @Nullable
75 private final byte[] mManufacturerData;
76 @Nullable
77 private final byte[] mManufacturerDataMask;
Eugene Susla6ed45d82017-01-22 13:52:51 -080078
79 /** @hide */
Jack Hea355e5e2017-08-22 16:06:54 -070080 public static final ScanFilter EMPTY = new ScanFilter.Builder().build();
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -070081
Wei Wangadf6aff2014-05-20 06:30:20 +000082
Wei Wangaf74e662014-07-09 14:03:42 -070083 private ScanFilter(String name, String deviceAddress, ParcelUuid uuid,
Nitin Shivpure1555eae2018-04-02 13:45:45 +053084 ParcelUuid uuidMask, ParcelUuid solicitationUuid,
85 ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid,
Wei Wang685c17582014-07-16 22:02:03 -070086 byte[] serviceData, byte[] serviceDataMask,
87 int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) {
Wei Wangaf74e662014-07-09 14:03:42 -070088 mDeviceName = name;
Wei Wangadf6aff2014-05-20 06:30:20 +000089 mServiceUuid = uuid;
90 mServiceUuidMask = uuidMask;
Nitin Shivpure1555eae2018-04-02 13:45:45 +053091 mServiceSolicitationUuid = solicitationUuid;
92 mServiceSolicitationUuidMask = solicitationUuidMask;
Wei Wangaf74e662014-07-09 14:03:42 -070093 mDeviceAddress = deviceAddress;
Wei Wang685c17582014-07-16 22:02:03 -070094 mServiceDataUuid = serviceDataUuid;
Wei Wangadf6aff2014-05-20 06:30:20 +000095 mServiceData = serviceData;
96 mServiceDataMask = serviceDataMask;
97 mManufacturerId = manufacturerId;
98 mManufacturerData = manufacturerData;
99 mManufacturerDataMask = manufacturerDataMask;
Wei Wangadf6aff2014-05-20 06:30:20 +0000100 }
101
102 @Override
103 public int describeContents() {
104 return 0;
105 }
106
107 @Override
108 public void writeToParcel(Parcel dest, int flags) {
Wei Wangaf74e662014-07-09 14:03:42 -0700109 dest.writeInt(mDeviceName == null ? 0 : 1);
110 if (mDeviceName != null) {
111 dest.writeString(mDeviceName);
Wei Wangadf6aff2014-05-20 06:30:20 +0000112 }
Wei Wangaf74e662014-07-09 14:03:42 -0700113 dest.writeInt(mDeviceAddress == null ? 0 : 1);
114 if (mDeviceAddress != null) {
115 dest.writeString(mDeviceAddress);
Wei Wangadf6aff2014-05-20 06:30:20 +0000116 }
117 dest.writeInt(mServiceUuid == null ? 0 : 1);
118 if (mServiceUuid != null) {
119 dest.writeParcelable(mServiceUuid, flags);
Wei Wang6d811182014-05-22 12:10:25 -0700120 dest.writeInt(mServiceUuidMask == null ? 0 : 1);
121 if (mServiceUuidMask != null) {
122 dest.writeParcelable(mServiceUuidMask, flags);
123 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000124 }
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530125 dest.writeInt(mServiceSolicitationUuid == null ? 0 : 1);
126 if (mServiceSolicitationUuid != null) {
127 dest.writeParcelable(mServiceSolicitationUuid, flags);
128 dest.writeInt(mServiceSolicitationUuidMask == null ? 0 : 1);
129 if (mServiceSolicitationUuidMask != null) {
130 dest.writeParcelable(mServiceSolicitationUuidMask, flags);
131 }
132 }
Wei Wangab2ed622014-07-25 15:14:55 -0700133 dest.writeInt(mServiceDataUuid == null ? 0 : 1);
Wei Wang685c17582014-07-16 22:02:03 -0700134 if (mServiceDataUuid != null) {
135 dest.writeParcelable(mServiceDataUuid, flags);
Wei Wang04624682014-07-28 21:46:44 -0700136 dest.writeInt(mServiceData == null ? 0 : 1);
Wei Wang685c17582014-07-16 22:02:03 -0700137 if (mServiceData != null) {
Wei Wang04624682014-07-28 21:46:44 -0700138 dest.writeInt(mServiceData.length);
Wei Wang685c17582014-07-16 22:02:03 -0700139 dest.writeByteArray(mServiceData);
Wei Wang04624682014-07-28 21:46:44 -0700140
141 dest.writeInt(mServiceDataMask == null ? 0 : 1);
Wei Wang685c17582014-07-16 22:02:03 -0700142 if (mServiceDataMask != null) {
Wei Wang04624682014-07-28 21:46:44 -0700143 dest.writeInt(mServiceDataMask.length);
Wei Wang685c17582014-07-16 22:02:03 -0700144 dest.writeByteArray(mServiceDataMask);
145 }
Wei Wang6d811182014-05-22 12:10:25 -0700146 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000147 }
148 dest.writeInt(mManufacturerId);
Wei Wang04624682014-07-28 21:46:44 -0700149 dest.writeInt(mManufacturerData == null ? 0 : 1);
Wei Wangadf6aff2014-05-20 06:30:20 +0000150 if (mManufacturerData != null) {
Wei Wang04624682014-07-28 21:46:44 -0700151 dest.writeInt(mManufacturerData.length);
Wei Wangadf6aff2014-05-20 06:30:20 +0000152 dest.writeByteArray(mManufacturerData);
Wei Wang04624682014-07-28 21:46:44 -0700153
154 dest.writeInt(mManufacturerDataMask == null ? 0 : 1);
Wei Wang6d811182014-05-22 12:10:25 -0700155 if (mManufacturerDataMask != null) {
Wei Wang04624682014-07-28 21:46:44 -0700156 dest.writeInt(mManufacturerDataMask.length);
Wei Wang6d811182014-05-22 12:10:25 -0700157 dest.writeByteArray(mManufacturerDataMask);
158 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000159 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000160 }
161
162 /**
Wei Wang685c17582014-07-16 22:02:03 -0700163 * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel.
Wei Wangadf6aff2014-05-20 06:30:20 +0000164 */
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700165 public static final @android.annotation.NonNull Creator<ScanFilter> CREATOR =
Jack He2992cd02017-08-22 21:21:23 -0700166 new Creator<ScanFilter>() {
Wei Wangadf6aff2014-05-20 06:30:20 +0000167
Jack Hea355e5e2017-08-22 16:06:54 -0700168 @Override
169 public ScanFilter[] newArray(int size) {
170 return new ScanFilter[size];
171 }
172
173 @Override
174 public ScanFilter createFromParcel(Parcel in) {
175 Builder builder = new Builder();
176 if (in.readInt() == 1) {
177 builder.setDeviceName(in.readString());
178 }
179 if (in.readInt() == 1) {
180 builder.setDeviceAddress(in.readString());
181 }
182 if (in.readInt() == 1) {
183 ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader());
184 builder.setServiceUuid(uuid);
185 if (in.readInt() == 1) {
186 ParcelUuid uuidMask = in.readParcelable(
187 ParcelUuid.class.getClassLoader());
188 builder.setServiceUuid(uuid, uuidMask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000189 }
Jack Hea355e5e2017-08-22 16:06:54 -0700190 }
191 if (in.readInt() == 1) {
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530192 ParcelUuid solicitationUuid = in.readParcelable(
193 ParcelUuid.class.getClassLoader());
194 builder.setServiceSolicitationUuid(solicitationUuid);
195 if (in.readInt() == 1) {
196 ParcelUuid solicitationUuidMask = in.readParcelable(
197 ParcelUuid.class.getClassLoader());
198 builder.setServiceSolicitationUuid(solicitationUuid,
199 solicitationUuidMask);
200 }
201 }
202 if (in.readInt() == 1) {
Jack Hea355e5e2017-08-22 16:06:54 -0700203 ParcelUuid servcieDataUuid =
204 in.readParcelable(ParcelUuid.class.getClassLoader());
205 if (in.readInt() == 1) {
206 int serviceDataLength = in.readInt();
207 byte[] serviceData = new byte[serviceDataLength];
208 in.readByteArray(serviceData);
209 if (in.readInt() == 0) {
210 builder.setServiceData(servcieDataUuid, serviceData);
211 } else {
212 int serviceDataMaskLength = in.readInt();
213 byte[] serviceDataMask = new byte[serviceDataMaskLength];
214 in.readByteArray(serviceDataMask);
215 builder.setServiceData(
216 servcieDataUuid, serviceData, serviceDataMask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000217 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000218 }
Jack Hea355e5e2017-08-22 16:06:54 -0700219 }
220
221 int manufacturerId = in.readInt();
222 if (in.readInt() == 1) {
223 int manufacturerDataLength = in.readInt();
224 byte[] manufacturerData = new byte[manufacturerDataLength];
225 in.readByteArray(manufacturerData);
226 if (in.readInt() == 0) {
227 builder.setManufacturerData(manufacturerId, manufacturerData);
228 } else {
229 int manufacturerDataMaskLength = in.readInt();
230 byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
231 in.readByteArray(manufacturerDataMask);
232 builder.setManufacturerData(manufacturerId, manufacturerData,
233 manufacturerDataMask);
234 }
235 }
236
237 return builder.build();
238 }
239 };
Wei Wangadf6aff2014-05-20 06:30:20 +0000240
241 /**
Wei Wangaf74e662014-07-09 14:03:42 -0700242 * Returns the filter set the device name field of Bluetooth advertisement data.
Wei Wangadf6aff2014-05-20 06:30:20 +0000243 */
244 @Nullable
Wei Wangaf74e662014-07-09 14:03:42 -0700245 public String getDeviceName() {
246 return mDeviceName;
Wei Wangadf6aff2014-05-20 06:30:20 +0000247 }
248
Wei Wang6d811182014-05-22 12:10:25 -0700249 /**
250 * Returns the filter set on the service uuid.
251 */
252 @Nullable
Wei Wangadf6aff2014-05-20 06:30:20 +0000253 public ParcelUuid getServiceUuid() {
254 return mServiceUuid;
255 }
256
257 @Nullable
258 public ParcelUuid getServiceUuidMask() {
259 return mServiceUuidMask;
260 }
261
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530262 /**
263 * Returns the filter set on the service Solicitation uuid.
264 */
265 @Nullable
266 public ParcelUuid getServiceSolicitationUuid() {
267 return mServiceSolicitationUuid;
268 }
269
270 /**
271 * Returns the filter set on the service Solicitation uuid mask.
272 */
273 @Nullable
274 public ParcelUuid getServiceSolicitationUuidMask() {
275 return mServiceSolicitationUuidMask;
276 }
277
Wei Wangadf6aff2014-05-20 06:30:20 +0000278 @Nullable
279 public String getDeviceAddress() {
Wei Wangaf74e662014-07-09 14:03:42 -0700280 return mDeviceAddress;
Wei Wangadf6aff2014-05-20 06:30:20 +0000281 }
282
283 @Nullable
284 public byte[] getServiceData() {
285 return mServiceData;
286 }
287
288 @Nullable
289 public byte[] getServiceDataMask() {
290 return mServiceDataMask;
291 }
292
Wei Wangab2ed622014-07-25 15:14:55 -0700293 @Nullable
294 public ParcelUuid getServiceDataUuid() {
295 return mServiceDataUuid;
296 }
297
298 /**
Wei Wangadf6aff2014-05-20 06:30:20 +0000299 * Returns the manufacturer id. -1 if the manufacturer filter is not set.
300 */
301 public int getManufacturerId() {
302 return mManufacturerId;
303 }
304
305 @Nullable
306 public byte[] getManufacturerData() {
307 return mManufacturerData;
308 }
309
310 @Nullable
311 public byte[] getManufacturerDataMask() {
312 return mManufacturerDataMask;
313 }
314
315 /**
Wei Wangadf6aff2014-05-20 06:30:20 +0000316 * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match
317 * if it matches all the field filters.
318 */
319 public boolean matches(ScanResult scanResult) {
320 if (scanResult == null) {
321 return false;
322 }
323 BluetoothDevice device = scanResult.getDevice();
324 // Device match.
Wei Wang685c17582014-07-16 22:02:03 -0700325 if (mDeviceAddress != null
326 && (device == null || !mDeviceAddress.equals(device.getAddress()))) {
Wei Wangadf6aff2014-05-20 06:30:20 +0000327 return false;
328 }
329
Wei Wang685c17582014-07-16 22:02:03 -0700330 ScanRecord scanRecord = scanResult.getScanRecord();
Wei Wangadf6aff2014-05-20 06:30:20 +0000331
332 // Scan record is null but there exist filters on it.
333 if (scanRecord == null
Wei Wangaf74e662014-07-09 14:03:42 -0700334 && (mDeviceName != null || mServiceUuid != null || mManufacturerData != null
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530335 || mServiceData != null || mServiceSolicitationUuid != null)) {
Wei Wangadf6aff2014-05-20 06:30:20 +0000336 return false;
337 }
338
339 // Local name match.
Wei Wangaf74e662014-07-09 14:03:42 -0700340 if (mDeviceName != null && !mDeviceName.equals(scanRecord.getDeviceName())) {
Wei Wangadf6aff2014-05-20 06:30:20 +0000341 return false;
342 }
343
344 // UUID match.
345 if (mServiceUuid != null && !matchesServiceUuids(mServiceUuid, mServiceUuidMask,
346 scanRecord.getServiceUuids())) {
347 return false;
348 }
349
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530350 // solicitation UUID match.
351 if (mServiceSolicitationUuid != null && !matchesServiceSolicitationUuids(
352 mServiceSolicitationUuid, mServiceSolicitationUuidMask,
353 scanRecord.getServiceSolicitationUuids())) {
354 return false;
355 }
356
Wei Wangadf6aff2014-05-20 06:30:20 +0000357 // Service data match
Wei Wang6bf513d2014-08-01 11:12:37 -0700358 if (mServiceDataUuid != null) {
359 if (!matchesPartialData(mServiceData, mServiceDataMask,
360 scanRecord.getServiceData(mServiceDataUuid))) {
Wei Wangab2ed622014-07-25 15:14:55 -0700361 return false;
362 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000363 }
364
365 // Manufacturer data match.
Wei Wang6bf513d2014-08-01 11:12:37 -0700366 if (mManufacturerId >= 0) {
367 if (!matchesPartialData(mManufacturerData, mManufacturerDataMask,
368 scanRecord.getManufacturerSpecificData(mManufacturerId))) {
Wei Wangab2ed622014-07-25 15:14:55 -0700369 return false;
370 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000371 }
372 // All filters match.
373 return true;
374 }
375
Eugene Susla6ed45d82017-01-22 13:52:51 -0800376 /**
377 * Check if the uuid pattern is contained in a list of parcel uuids.
378 *
379 * @hide
380 */
381 public static boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask,
Wei Wangadf6aff2014-05-20 06:30:20 +0000382 List<ParcelUuid> uuids) {
383 if (uuid == null) {
384 return true;
385 }
386 if (uuids == null) {
387 return false;
388 }
389
390 for (ParcelUuid parcelUuid : uuids) {
391 UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid();
392 if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) {
393 return true;
394 }
395 }
396 return false;
397 }
398
399 // Check if the uuid pattern matches the particular service uuid.
Eugene Susla6ed45d82017-01-22 13:52:51 -0800400 private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) {
Eugene Susla36e866b2017-02-23 18:24:39 -0800401 return BitUtils.maskedEquals(data, uuid, mask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000402 }
403
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530404 /**
405 * Check if the solicitation uuid pattern is contained in a list of parcel uuids.
406 *
407 */
408 private static boolean matchesServiceSolicitationUuids(ParcelUuid solicitationUuid,
409 ParcelUuid parcelSolicitationUuidMask, List<ParcelUuid> solicitationUuids) {
410 if (solicitationUuid == null) {
411 return true;
412 }
413 if (solicitationUuids == null) {
414 return false;
415 }
416
417 for (ParcelUuid parcelSolicitationUuid : solicitationUuids) {
418 UUID solicitationUuidMask = parcelSolicitationUuidMask == null
419 ? null : parcelSolicitationUuidMask.getUuid();
420 if (matchesServiceUuid(solicitationUuid.getUuid(), solicitationUuidMask,
421 parcelSolicitationUuid.getUuid())) {
422 return true;
423 }
424 }
425 return false;
426 }
427
428 // Check if the solicitation uuid pattern matches the particular service solicitation uuid.
429 private static boolean matchesServiceSolicitationUuid(UUID solicitationUuid,
430 UUID solicitationUuidMask, UUID data) {
431 return BitUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask);
432 }
433
Wei Wangadf6aff2014-05-20 06:30:20 +0000434 // Check whether the data pattern matches the parsed data.
435 private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) {
Wei Wang04624682014-07-28 21:46:44 -0700436 if (parsedData == null || parsedData.length < data.length) {
Wei Wangadf6aff2014-05-20 06:30:20 +0000437 return false;
438 }
Wei Wang04624682014-07-28 21:46:44 -0700439 if (dataMask == null) {
440 for (int i = 0; i < data.length; ++i) {
441 if (parsedData[i] != data[i]) {
442 return false;
443 }
444 }
445 return true;
446 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000447 for (int i = 0; i < data.length; ++i) {
448 if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) {
449 return false;
450 }
451 }
452 return true;
453 }
454
455 @Override
456 public String toString() {
Wei Wang685c17582014-07-16 22:02:03 -0700457 return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress="
458 + mDeviceAddress
Wei Wangab2ed622014-07-25 15:14:55 -0700459 + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530460 + ", mServiceSolicitationUuid=" + mServiceSolicitationUuid
461 + ", mServiceSolicitationUuidMask=" + mServiceSolicitationUuidMask
Wei Wangab2ed622014-07-25 15:14:55 -0700462 + ", mServiceDataUuid=" + Objects.toString(mServiceDataUuid) + ", mServiceData="
Wei Wangadf6aff2014-05-20 06:30:20 +0000463 + Arrays.toString(mServiceData) + ", mServiceDataMask="
464 + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId
465 + ", mManufacturerData=" + Arrays.toString(mManufacturerData)
Wei Wang685c17582014-07-16 22:02:03 -0700466 + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + "]";
Wei Wangadf6aff2014-05-20 06:30:20 +0000467 }
468
469 @Override
470 public int hashCode() {
Pavlin Radoslavov2f463d42016-05-06 12:05:47 -0700471 return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId,
Jack Hea355e5e2017-08-22 16:06:54 -0700472 Arrays.hashCode(mManufacturerData),
473 Arrays.hashCode(mManufacturerDataMask),
474 mServiceDataUuid,
475 Arrays.hashCode(mServiceData),
476 Arrays.hashCode(mServiceDataMask),
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530477 mServiceUuid, mServiceUuidMask,
478 mServiceSolicitationUuid, mServiceSolicitationUuidMask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000479 }
480
481 @Override
482 public boolean equals(Object obj) {
483 if (this == obj) {
484 return true;
485 }
486 if (obj == null || getClass() != obj.getClass()) {
487 return false;
488 }
Wei Wang6d811182014-05-22 12:10:25 -0700489 ScanFilter other = (ScanFilter) obj;
Jack He2992cd02017-08-22 21:21:23 -0700490 return Objects.equals(mDeviceName, other.mDeviceName)
491 && Objects.equals(mDeviceAddress, other.mDeviceAddress)
492 && mManufacturerId == other.mManufacturerId
493 && Objects.deepEquals(mManufacturerData, other.mManufacturerData)
494 && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask)
495 && Objects.equals(mServiceDataUuid, other.mServiceDataUuid)
496 && Objects.deepEquals(mServiceData, other.mServiceData)
497 && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask)
498 && Objects.equals(mServiceUuid, other.mServiceUuid)
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530499 && Objects.equals(mServiceUuidMask, other.mServiceUuidMask)
500 && Objects.equals(mServiceSolicitationUuid, other.mServiceSolicitationUuid)
501 && Objects.equals(mServiceSolicitationUuidMask,
502 other.mServiceSolicitationUuidMask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000503 }
504
505 /**
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700506 * Checks if the scanfilter is empty
Jack Hea355e5e2017-08-22 16:06:54 -0700507 *
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700508 * @hide
509 */
510 public boolean isAllFieldsEmpty() {
511 return EMPTY.equals(this);
512 }
513
514 /**
Wei Wang6d811182014-05-22 12:10:25 -0700515 * Builder class for {@link ScanFilter}.
Wei Wangadf6aff2014-05-20 06:30:20 +0000516 */
Wei Wang6d811182014-05-22 12:10:25 -0700517 public static final class Builder {
Wei Wangadf6aff2014-05-20 06:30:20 +0000518
Wei Wangaf74e662014-07-09 14:03:42 -0700519 private String mDeviceName;
520 private String mDeviceAddress;
Wei Wangadf6aff2014-05-20 06:30:20 +0000521
522 private ParcelUuid mServiceUuid;
523 private ParcelUuid mUuidMask;
524
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530525 private ParcelUuid mServiceSolicitationUuid;
526 private ParcelUuid mServiceSolicitationUuidMask;
527
Wei Wang685c17582014-07-16 22:02:03 -0700528 private ParcelUuid mServiceDataUuid;
Wei Wangadf6aff2014-05-20 06:30:20 +0000529 private byte[] mServiceData;
530 private byte[] mServiceDataMask;
531
532 private int mManufacturerId = -1;
533 private byte[] mManufacturerData;
534 private byte[] mManufacturerDataMask;
535
Wei Wangadf6aff2014-05-20 06:30:20 +0000536 /**
Wei Wangaf74e662014-07-09 14:03:42 -0700537 * Set filter on device name.
Wei Wangadf6aff2014-05-20 06:30:20 +0000538 */
Wei Wangaf74e662014-07-09 14:03:42 -0700539 public Builder setDeviceName(String deviceName) {
540 mDeviceName = deviceName;
Wei Wangadf6aff2014-05-20 06:30:20 +0000541 return this;
542 }
543
544 /**
Wei Wangaf74e662014-07-09 14:03:42 -0700545 * Set filter on device address.
Wei Wangadf6aff2014-05-20 06:30:20 +0000546 *
Wei Wang685c17582014-07-16 22:02:03 -0700547 * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
Jack Hea355e5e2017-08-22 16:06:54 -0700548 * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
549 * BluetoothAdapter#checkBluetoothAddress}.
Wei Wangaf74e662014-07-09 14:03:42 -0700550 * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
Wei Wangadf6aff2014-05-20 06:30:20 +0000551 */
Wei Wangaf74e662014-07-09 14:03:42 -0700552 public Builder setDeviceAddress(String deviceAddress) {
553 if (deviceAddress != null && !BluetoothAdapter.checkBluetoothAddress(deviceAddress)) {
554 throw new IllegalArgumentException("invalid device address " + deviceAddress);
Wei Wangadf6aff2014-05-20 06:30:20 +0000555 }
Wei Wangaf74e662014-07-09 14:03:42 -0700556 mDeviceAddress = deviceAddress;
Wei Wangadf6aff2014-05-20 06:30:20 +0000557 return this;
558 }
559
560 /**
Wei Wang6d811182014-05-22 12:10:25 -0700561 * Set filter on service uuid.
Wei Wangadf6aff2014-05-20 06:30:20 +0000562 */
Wei Wang6d811182014-05-22 12:10:25 -0700563 public Builder setServiceUuid(ParcelUuid serviceUuid) {
Wei Wangadf6aff2014-05-20 06:30:20 +0000564 mServiceUuid = serviceUuid;
Wei Wang6d811182014-05-22 12:10:25 -0700565 mUuidMask = null; // clear uuid mask
Wei Wangadf6aff2014-05-20 06:30:20 +0000566 return this;
567 }
568
569 /**
Wei Wang6d811182014-05-22 12:10:25 -0700570 * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the
571 * {@code serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the
572 * bit in {@code serviceUuid}, and 0 to ignore that bit.
573 *
Jack Hea355e5e2017-08-22 16:06:54 -0700574 * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but {@code
575 * uuidMask} is not {@code null}.
Wei Wangadf6aff2014-05-20 06:30:20 +0000576 */
Wei Wang6d811182014-05-22 12:10:25 -0700577 public Builder setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask) {
578 if (mUuidMask != null && mServiceUuid == null) {
579 throw new IllegalArgumentException("uuid is null while uuidMask is not null!");
580 }
581 mServiceUuid = serviceUuid;
Wei Wangadf6aff2014-05-20 06:30:20 +0000582 mUuidMask = uuidMask;
583 return this;
584 }
585
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530586
587 /**
588 * Set filter on service solicitation uuid.
589 */
Zach Johnsondf512f12019-04-19 14:13:03 -0700590 public @NonNull Builder setServiceSolicitationUuid(
591 @Nullable ParcelUuid serviceSolicitationUuid) {
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530592 mServiceSolicitationUuid = serviceSolicitationUuid;
Martin Brabham2b3b1422019-05-21 17:15:44 -0700593 if (serviceSolicitationUuid == null) {
594 mServiceSolicitationUuidMask = null;
595 }
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530596 return this;
597 }
598
599
600 /**
601 * Set filter on partial service Solicitation uuid. The {@code SolicitationUuidMask} is the
602 * bit mask for the {@code serviceSolicitationUuid}. Set any bit in the mask to 1 to
603 * indicate a match is needed for the bit in {@code serviceSolicitationUuid}, and 0 to
604 * ignore that bit.
605 *
Martin Brabham2b3b1422019-05-21 17:15:44 -0700606 * @param serviceSolicitationUuid can only be null if solicitationUuidMask is null.
607 * @param solicitationUuidMask can be null or a mask with no restriction.
608 *
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530609 * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but
610 * {@code serviceSolicitationUuidMask} is not {@code null}.
611 */
Zach Johnsondf512f12019-04-19 14:13:03 -0700612 public @NonNull Builder setServiceSolicitationUuid(
613 @Nullable ParcelUuid serviceSolicitationUuid,
614 @Nullable ParcelUuid solicitationUuidMask) {
Martin Brabham2b3b1422019-05-21 17:15:44 -0700615 if (solicitationUuidMask != null && serviceSolicitationUuid == null) {
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530616 throw new IllegalArgumentException(
617 "SolicitationUuid is null while SolicitationUuidMask is not null!");
618 }
619 mServiceSolicitationUuid = serviceSolicitationUuid;
620 mServiceSolicitationUuidMask = solicitationUuidMask;
621 return this;
622 }
623
Wei Wangadf6aff2014-05-20 06:30:20 +0000624 /**
Wei Wang6d811182014-05-22 12:10:25 -0700625 * Set filtering on service data.
Wei Wang685c17582014-07-16 22:02:03 -0700626 *
627 * @throws IllegalArgumentException If {@code serviceDataUuid} is null.
Wei Wangadf6aff2014-05-20 06:30:20 +0000628 */
Wei Wang685c17582014-07-16 22:02:03 -0700629 public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
630 if (serviceDataUuid == null) {
631 throw new IllegalArgumentException("serviceDataUuid is null");
632 }
633 mServiceDataUuid = serviceDataUuid;
Wei Wangadf6aff2014-05-20 06:30:20 +0000634 mServiceData = serviceData;
Wei Wang6d811182014-05-22 12:10:25 -0700635 mServiceDataMask = null; // clear service data mask
Wei Wangadf6aff2014-05-20 06:30:20 +0000636 return this;
637 }
638
639 /**
Wei Wang6d811182014-05-22 12:10:25 -0700640 * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to
641 * match the one in service data, otherwise set it to 0 to ignore that bit.
Wei Wangadf6aff2014-05-20 06:30:20 +0000642 * <p>
Wei Wang6d811182014-05-22 12:10:25 -0700643 * The {@code serviceDataMask} must have the same length of the {@code serviceData}.
Wei Wangadf6aff2014-05-20 06:30:20 +0000644 *
Jack Hea355e5e2017-08-22 16:06:54 -0700645 * @throws IllegalArgumentException If {@code serviceDataUuid} is null or {@code
646 * serviceDataMask} is {@code null} while {@code serviceData} is not or {@code
647 * serviceDataMask} and {@code serviceData} has different length.
Wei Wangadf6aff2014-05-20 06:30:20 +0000648 */
Wei Wang685c17582014-07-16 22:02:03 -0700649 public Builder setServiceData(ParcelUuid serviceDataUuid,
650 byte[] serviceData, byte[] serviceDataMask) {
651 if (serviceDataUuid == null) {
652 throw new IllegalArgumentException("serviceDataUuid is null");
653 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000654 if (mServiceDataMask != null) {
655 if (mServiceData == null) {
656 throw new IllegalArgumentException(
657 "serviceData is null while serviceDataMask is not null");
658 }
659 // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two
660 // byte array need to be the same.
661 if (mServiceData.length != mServiceDataMask.length) {
662 throw new IllegalArgumentException(
663 "size mismatch for service data and service data mask");
664 }
665 }
Wei Wangab2ed622014-07-25 15:14:55 -0700666 mServiceDataUuid = serviceDataUuid;
Wei Wang6d811182014-05-22 12:10:25 -0700667 mServiceData = serviceData;
668 mServiceDataMask = serviceDataMask;
669 return this;
670 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000671
Wei Wang6d811182014-05-22 12:10:25 -0700672 /**
673 * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id.
674 * <p>
675 * Note the first two bytes of the {@code manufacturerData} is the manufacturerId.
676 *
677 * @throws IllegalArgumentException If the {@code manufacturerId} is invalid.
678 */
679 public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData) {
680 if (manufacturerData != null && manufacturerId < 0) {
681 throw new IllegalArgumentException("invalid manufacture id");
682 }
683 mManufacturerId = manufacturerId;
684 mManufacturerData = manufacturerData;
685 mManufacturerDataMask = null; // clear manufacturer data mask
686 return this;
687 }
688
689 /**
Wei Wang685c17582014-07-16 22:02:03 -0700690 * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it needs
691 * to match the one in manufacturer data, otherwise set it to 0.
Wei Wang6d811182014-05-22 12:10:25 -0700692 * <p>
693 * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}.
694 *
Jack Hea355e5e2017-08-22 16:06:54 -0700695 * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or {@code
696 * manufacturerData} is null while {@code manufacturerDataMask} is not, or {@code
697 * manufacturerData} and {@code manufacturerDataMask} have different length.
Wei Wang6d811182014-05-22 12:10:25 -0700698 */
699 public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData,
700 byte[] manufacturerDataMask) {
701 if (manufacturerData != null && manufacturerId < 0) {
702 throw new IllegalArgumentException("invalid manufacture id");
703 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000704 if (mManufacturerDataMask != null) {
705 if (mManufacturerData == null) {
706 throw new IllegalArgumentException(
707 "manufacturerData is null while manufacturerDataMask is not null");
708 }
709 // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths
710 // of the two byte array need to be the same.
711 if (mManufacturerData.length != mManufacturerDataMask.length) {
712 throw new IllegalArgumentException(
713 "size mismatch for manufacturerData and manufacturerDataMask");
714 }
715 }
Wei Wang6d811182014-05-22 12:10:25 -0700716 mManufacturerId = manufacturerId;
717 mManufacturerData = manufacturerData;
718 mManufacturerDataMask = manufacturerDataMask;
719 return this;
720 }
721
722 /**
Wei Wang6d811182014-05-22 12:10:25 -0700723 * Build {@link ScanFilter}.
724 *
725 * @throws IllegalArgumentException If the filter cannot be built.
726 */
727 public ScanFilter build() {
Wei Wangaf74e662014-07-09 14:03:42 -0700728 return new ScanFilter(mDeviceName, mDeviceAddress,
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530729 mServiceUuid, mUuidMask, mServiceSolicitationUuid,
730 mServiceSolicitationUuidMask,
Wei Wang685c17582014-07-16 22:02:03 -0700731 mServiceDataUuid, mServiceData, mServiceDataMask,
732 mManufacturerId, mManufacturerData, mManufacturerDataMask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000733 }
734 }
735}