blob: c5d435b76139743620f7728658fd2ba134363bb9 [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
19import android.annotation.Nullable;
Wei Wang6d811182014-05-22 12:10:25 -070020import android.bluetooth.BluetoothAdapter;
21import android.bluetooth.BluetoothDevice;
Wei Wangadf6aff2014-05-20 06:30:20 +000022import android.os.Parcel;
23import android.os.ParcelUuid;
24import android.os.Parcelable;
25
Eugene Susla36e866b2017-02-23 18:24:39 -080026import com.android.internal.util.BitUtils;
27
Wei Wangadf6aff2014-05-20 06:30:20 +000028import java.util.Arrays;
29import java.util.List;
30import java.util.Objects;
31import java.util.UUID;
32
33/**
Wei Wang685c17582014-07-16 22:02:03 -070034 * Criteria for filtering result from Bluetooth LE scans. A {@link ScanFilter} allows clients to
35 * restrict scan results to only those that are of interest to them.
Wei Wangadf6aff2014-05-20 06:30:20 +000036 * <p>
37 * Current filtering on the following fields are supported:
38 * <li>Service UUIDs which identify the bluetooth gatt services running on the device.
39 * <li>Name of remote Bluetooth LE device.
40 * <li>Mac address of the remote device.
Wei Wangadf6aff2014-05-20 06:30:20 +000041 * <li>Service data which is the data associated with a service.
42 * <li>Manufacturer specific data which is the data associated with a particular manufacturer.
43 *
Wei Wang685c17582014-07-16 22:02:03 -070044 * @see ScanResult
Wei Wangadf6aff2014-05-20 06:30:20 +000045 * @see BluetoothLeScanner
46 */
Wei Wang6d811182014-05-22 12:10:25 -070047public final class ScanFilter implements Parcelable {
Wei Wangadf6aff2014-05-20 06:30:20 +000048
49 @Nullable
Wei Wangaf74e662014-07-09 14:03:42 -070050 private final String mDeviceName;
Wei Wangadf6aff2014-05-20 06:30:20 +000051
52 @Nullable
Wei Wangaf74e662014-07-09 14:03:42 -070053 private final String mDeviceAddress;
Wei Wangadf6aff2014-05-20 06:30:20 +000054
55 @Nullable
56 private final ParcelUuid mServiceUuid;
57 @Nullable
58 private final ParcelUuid mServiceUuidMask;
59
60 @Nullable
Nitin Shivpure1555eae2018-04-02 13:45:45 +053061 private final ParcelUuid mServiceSolicitationUuid;
62 @Nullable
63 private final ParcelUuid mServiceSolicitationUuidMask;
64
65 @Nullable
Wei Wang685c17582014-07-16 22:02:03 -070066 private final ParcelUuid mServiceDataUuid;
67 @Nullable
Wei Wangadf6aff2014-05-20 06:30:20 +000068 private final byte[] mServiceData;
69 @Nullable
70 private final byte[] mServiceDataMask;
71
72 private final int mManufacturerId;
73 @Nullable
74 private final byte[] mManufacturerData;
75 @Nullable
76 private final byte[] mManufacturerDataMask;
Eugene Susla6ed45d82017-01-22 13:52:51 -080077
78 /** @hide */
Jack Hea355e5e2017-08-22 16:06:54 -070079 public static final ScanFilter EMPTY = new ScanFilter.Builder().build();
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -070080
Wei Wangadf6aff2014-05-20 06:30:20 +000081
Wei Wangaf74e662014-07-09 14:03:42 -070082 private ScanFilter(String name, String deviceAddress, ParcelUuid uuid,
Nitin Shivpure1555eae2018-04-02 13:45:45 +053083 ParcelUuid uuidMask, ParcelUuid solicitationUuid,
84 ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid,
Wei Wang685c17582014-07-16 22:02:03 -070085 byte[] serviceData, byte[] serviceDataMask,
86 int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) {
Wei Wangaf74e662014-07-09 14:03:42 -070087 mDeviceName = name;
Wei Wangadf6aff2014-05-20 06:30:20 +000088 mServiceUuid = uuid;
89 mServiceUuidMask = uuidMask;
Nitin Shivpure1555eae2018-04-02 13:45:45 +053090 mServiceSolicitationUuid = solicitationUuid;
91 mServiceSolicitationUuidMask = solicitationUuidMask;
Wei Wangaf74e662014-07-09 14:03:42 -070092 mDeviceAddress = deviceAddress;
Wei Wang685c17582014-07-16 22:02:03 -070093 mServiceDataUuid = serviceDataUuid;
Wei Wangadf6aff2014-05-20 06:30:20 +000094 mServiceData = serviceData;
95 mServiceDataMask = serviceDataMask;
96 mManufacturerId = manufacturerId;
97 mManufacturerData = manufacturerData;
98 mManufacturerDataMask = manufacturerDataMask;
Wei Wangadf6aff2014-05-20 06:30:20 +000099 }
100
101 @Override
102 public int describeContents() {
103 return 0;
104 }
105
106 @Override
107 public void writeToParcel(Parcel dest, int flags) {
Wei Wangaf74e662014-07-09 14:03:42 -0700108 dest.writeInt(mDeviceName == null ? 0 : 1);
109 if (mDeviceName != null) {
110 dest.writeString(mDeviceName);
Wei Wangadf6aff2014-05-20 06:30:20 +0000111 }
Wei Wangaf74e662014-07-09 14:03:42 -0700112 dest.writeInt(mDeviceAddress == null ? 0 : 1);
113 if (mDeviceAddress != null) {
114 dest.writeString(mDeviceAddress);
Wei Wangadf6aff2014-05-20 06:30:20 +0000115 }
116 dest.writeInt(mServiceUuid == null ? 0 : 1);
117 if (mServiceUuid != null) {
118 dest.writeParcelable(mServiceUuid, flags);
Wei Wang6d811182014-05-22 12:10:25 -0700119 dest.writeInt(mServiceUuidMask == null ? 0 : 1);
120 if (mServiceUuidMask != null) {
121 dest.writeParcelable(mServiceUuidMask, flags);
122 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000123 }
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530124 dest.writeInt(mServiceSolicitationUuid == null ? 0 : 1);
125 if (mServiceSolicitationUuid != null) {
126 dest.writeParcelable(mServiceSolicitationUuid, flags);
127 dest.writeInt(mServiceSolicitationUuidMask == null ? 0 : 1);
128 if (mServiceSolicitationUuidMask != null) {
129 dest.writeParcelable(mServiceSolicitationUuidMask, flags);
130 }
131 }
Wei Wangab2ed622014-07-25 15:14:55 -0700132 dest.writeInt(mServiceDataUuid == null ? 0 : 1);
Wei Wang685c17582014-07-16 22:02:03 -0700133 if (mServiceDataUuid != null) {
134 dest.writeParcelable(mServiceDataUuid, flags);
Wei Wang04624682014-07-28 21:46:44 -0700135 dest.writeInt(mServiceData == null ? 0 : 1);
Wei Wang685c17582014-07-16 22:02:03 -0700136 if (mServiceData != null) {
Wei Wang04624682014-07-28 21:46:44 -0700137 dest.writeInt(mServiceData.length);
Wei Wang685c17582014-07-16 22:02:03 -0700138 dest.writeByteArray(mServiceData);
Wei Wang04624682014-07-28 21:46:44 -0700139
140 dest.writeInt(mServiceDataMask == null ? 0 : 1);
Wei Wang685c17582014-07-16 22:02:03 -0700141 if (mServiceDataMask != null) {
Wei Wang04624682014-07-28 21:46:44 -0700142 dest.writeInt(mServiceDataMask.length);
Wei Wang685c17582014-07-16 22:02:03 -0700143 dest.writeByteArray(mServiceDataMask);
144 }
Wei Wang6d811182014-05-22 12:10:25 -0700145 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000146 }
147 dest.writeInt(mManufacturerId);
Wei Wang04624682014-07-28 21:46:44 -0700148 dest.writeInt(mManufacturerData == null ? 0 : 1);
Wei Wangadf6aff2014-05-20 06:30:20 +0000149 if (mManufacturerData != null) {
Wei Wang04624682014-07-28 21:46:44 -0700150 dest.writeInt(mManufacturerData.length);
Wei Wangadf6aff2014-05-20 06:30:20 +0000151 dest.writeByteArray(mManufacturerData);
Wei Wang04624682014-07-28 21:46:44 -0700152
153 dest.writeInt(mManufacturerDataMask == null ? 0 : 1);
Wei Wang6d811182014-05-22 12:10:25 -0700154 if (mManufacturerDataMask != null) {
Wei Wang04624682014-07-28 21:46:44 -0700155 dest.writeInt(mManufacturerDataMask.length);
Wei Wang6d811182014-05-22 12:10:25 -0700156 dest.writeByteArray(mManufacturerDataMask);
157 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000158 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000159 }
160
161 /**
Wei Wang685c17582014-07-16 22:02:03 -0700162 * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel.
Wei Wangadf6aff2014-05-20 06:30:20 +0000163 */
Jack He2992cd02017-08-22 21:21:23 -0700164 public static final Creator<ScanFilter> CREATOR =
165 new Creator<ScanFilter>() {
Wei Wangadf6aff2014-05-20 06:30:20 +0000166
Jack Hea355e5e2017-08-22 16:06:54 -0700167 @Override
168 public ScanFilter[] newArray(int size) {
169 return new ScanFilter[size];
170 }
171
172 @Override
173 public ScanFilter createFromParcel(Parcel in) {
174 Builder builder = new Builder();
175 if (in.readInt() == 1) {
176 builder.setDeviceName(in.readString());
177 }
178 if (in.readInt() == 1) {
179 builder.setDeviceAddress(in.readString());
180 }
181 if (in.readInt() == 1) {
182 ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader());
183 builder.setServiceUuid(uuid);
184 if (in.readInt() == 1) {
185 ParcelUuid uuidMask = in.readParcelable(
186 ParcelUuid.class.getClassLoader());
187 builder.setServiceUuid(uuid, uuidMask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000188 }
Jack Hea355e5e2017-08-22 16:06:54 -0700189 }
190 if (in.readInt() == 1) {
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530191 ParcelUuid solicitationUuid = in.readParcelable(
192 ParcelUuid.class.getClassLoader());
193 builder.setServiceSolicitationUuid(solicitationUuid);
194 if (in.readInt() == 1) {
195 ParcelUuid solicitationUuidMask = in.readParcelable(
196 ParcelUuid.class.getClassLoader());
197 builder.setServiceSolicitationUuid(solicitationUuid,
198 solicitationUuidMask);
199 }
200 }
201 if (in.readInt() == 1) {
Jack Hea355e5e2017-08-22 16:06:54 -0700202 ParcelUuid servcieDataUuid =
203 in.readParcelable(ParcelUuid.class.getClassLoader());
204 if (in.readInt() == 1) {
205 int serviceDataLength = in.readInt();
206 byte[] serviceData = new byte[serviceDataLength];
207 in.readByteArray(serviceData);
208 if (in.readInt() == 0) {
209 builder.setServiceData(servcieDataUuid, serviceData);
210 } else {
211 int serviceDataMaskLength = in.readInt();
212 byte[] serviceDataMask = new byte[serviceDataMaskLength];
213 in.readByteArray(serviceDataMask);
214 builder.setServiceData(
215 servcieDataUuid, serviceData, serviceDataMask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000216 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000217 }
Jack Hea355e5e2017-08-22 16:06:54 -0700218 }
219
220 int manufacturerId = in.readInt();
221 if (in.readInt() == 1) {
222 int manufacturerDataLength = in.readInt();
223 byte[] manufacturerData = new byte[manufacturerDataLength];
224 in.readByteArray(manufacturerData);
225 if (in.readInt() == 0) {
226 builder.setManufacturerData(manufacturerId, manufacturerData);
227 } else {
228 int manufacturerDataMaskLength = in.readInt();
229 byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
230 in.readByteArray(manufacturerDataMask);
231 builder.setManufacturerData(manufacturerId, manufacturerData,
232 manufacturerDataMask);
233 }
234 }
235
236 return builder.build();
237 }
238 };
Wei Wangadf6aff2014-05-20 06:30:20 +0000239
240 /**
Wei Wangaf74e662014-07-09 14:03:42 -0700241 * Returns the filter set the device name field of Bluetooth advertisement data.
Wei Wangadf6aff2014-05-20 06:30:20 +0000242 */
243 @Nullable
Wei Wangaf74e662014-07-09 14:03:42 -0700244 public String getDeviceName() {
245 return mDeviceName;
Wei Wangadf6aff2014-05-20 06:30:20 +0000246 }
247
Wei Wang6d811182014-05-22 12:10:25 -0700248 /**
249 * Returns the filter set on the service uuid.
250 */
251 @Nullable
Wei Wangadf6aff2014-05-20 06:30:20 +0000252 public ParcelUuid getServiceUuid() {
253 return mServiceUuid;
254 }
255
256 @Nullable
257 public ParcelUuid getServiceUuidMask() {
258 return mServiceUuidMask;
259 }
260
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530261 /**
262 * Returns the filter set on the service Solicitation uuid.
263 */
264 @Nullable
265 public ParcelUuid getServiceSolicitationUuid() {
266 return mServiceSolicitationUuid;
267 }
268
269 /**
270 * Returns the filter set on the service Solicitation uuid mask.
271 */
272 @Nullable
273 public ParcelUuid getServiceSolicitationUuidMask() {
274 return mServiceSolicitationUuidMask;
275 }
276
Wei Wangadf6aff2014-05-20 06:30:20 +0000277 @Nullable
278 public String getDeviceAddress() {
Wei Wangaf74e662014-07-09 14:03:42 -0700279 return mDeviceAddress;
Wei Wangadf6aff2014-05-20 06:30:20 +0000280 }
281
282 @Nullable
283 public byte[] getServiceData() {
284 return mServiceData;
285 }
286
287 @Nullable
288 public byte[] getServiceDataMask() {
289 return mServiceDataMask;
290 }
291
Wei Wangab2ed622014-07-25 15:14:55 -0700292 @Nullable
293 public ParcelUuid getServiceDataUuid() {
294 return mServiceDataUuid;
295 }
296
297 /**
Wei Wangadf6aff2014-05-20 06:30:20 +0000298 * Returns the manufacturer id. -1 if the manufacturer filter is not set.
299 */
300 public int getManufacturerId() {
301 return mManufacturerId;
302 }
303
304 @Nullable
305 public byte[] getManufacturerData() {
306 return mManufacturerData;
307 }
308
309 @Nullable
310 public byte[] getManufacturerDataMask() {
311 return mManufacturerDataMask;
312 }
313
314 /**
Wei Wangadf6aff2014-05-20 06:30:20 +0000315 * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match
316 * if it matches all the field filters.
317 */
318 public boolean matches(ScanResult scanResult) {
319 if (scanResult == null) {
320 return false;
321 }
322 BluetoothDevice device = scanResult.getDevice();
323 // Device match.
Wei Wang685c17582014-07-16 22:02:03 -0700324 if (mDeviceAddress != null
325 && (device == null || !mDeviceAddress.equals(device.getAddress()))) {
Wei Wangadf6aff2014-05-20 06:30:20 +0000326 return false;
327 }
328
Wei Wang685c17582014-07-16 22:02:03 -0700329 ScanRecord scanRecord = scanResult.getScanRecord();
Wei Wangadf6aff2014-05-20 06:30:20 +0000330
331 // Scan record is null but there exist filters on it.
332 if (scanRecord == null
Wei Wangaf74e662014-07-09 14:03:42 -0700333 && (mDeviceName != null || mServiceUuid != null || mManufacturerData != null
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530334 || mServiceData != null || mServiceSolicitationUuid != null)) {
Wei Wangadf6aff2014-05-20 06:30:20 +0000335 return false;
336 }
337
338 // Local name match.
Wei Wangaf74e662014-07-09 14:03:42 -0700339 if (mDeviceName != null && !mDeviceName.equals(scanRecord.getDeviceName())) {
Wei Wangadf6aff2014-05-20 06:30:20 +0000340 return false;
341 }
342
343 // UUID match.
344 if (mServiceUuid != null && !matchesServiceUuids(mServiceUuid, mServiceUuidMask,
345 scanRecord.getServiceUuids())) {
346 return false;
347 }
348
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530349 // solicitation UUID match.
350 if (mServiceSolicitationUuid != null && !matchesServiceSolicitationUuids(
351 mServiceSolicitationUuid, mServiceSolicitationUuidMask,
352 scanRecord.getServiceSolicitationUuids())) {
353 return false;
354 }
355
Wei Wangadf6aff2014-05-20 06:30:20 +0000356 // Service data match
Wei Wang6bf513d2014-08-01 11:12:37 -0700357 if (mServiceDataUuid != null) {
358 if (!matchesPartialData(mServiceData, mServiceDataMask,
359 scanRecord.getServiceData(mServiceDataUuid))) {
Wei Wangab2ed622014-07-25 15:14:55 -0700360 return false;
361 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000362 }
363
364 // Manufacturer data match.
Wei Wang6bf513d2014-08-01 11:12:37 -0700365 if (mManufacturerId >= 0) {
366 if (!matchesPartialData(mManufacturerData, mManufacturerDataMask,
367 scanRecord.getManufacturerSpecificData(mManufacturerId))) {
Wei Wangab2ed622014-07-25 15:14:55 -0700368 return false;
369 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000370 }
371 // All filters match.
372 return true;
373 }
374
Eugene Susla6ed45d82017-01-22 13:52:51 -0800375 /**
376 * Check if the uuid pattern is contained in a list of parcel uuids.
377 *
378 * @hide
379 */
380 public static boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask,
Wei Wangadf6aff2014-05-20 06:30:20 +0000381 List<ParcelUuid> uuids) {
382 if (uuid == null) {
383 return true;
384 }
385 if (uuids == null) {
386 return false;
387 }
388
389 for (ParcelUuid parcelUuid : uuids) {
390 UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid();
391 if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) {
392 return true;
393 }
394 }
395 return false;
396 }
397
398 // Check if the uuid pattern matches the particular service uuid.
Eugene Susla6ed45d82017-01-22 13:52:51 -0800399 private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) {
Eugene Susla36e866b2017-02-23 18:24:39 -0800400 return BitUtils.maskedEquals(data, uuid, mask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000401 }
402
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530403 /**
404 * Check if the solicitation uuid pattern is contained in a list of parcel uuids.
405 *
406 */
407 private static boolean matchesServiceSolicitationUuids(ParcelUuid solicitationUuid,
408 ParcelUuid parcelSolicitationUuidMask, List<ParcelUuid> solicitationUuids) {
409 if (solicitationUuid == null) {
410 return true;
411 }
412 if (solicitationUuids == null) {
413 return false;
414 }
415
416 for (ParcelUuid parcelSolicitationUuid : solicitationUuids) {
417 UUID solicitationUuidMask = parcelSolicitationUuidMask == null
418 ? null : parcelSolicitationUuidMask.getUuid();
419 if (matchesServiceUuid(solicitationUuid.getUuid(), solicitationUuidMask,
420 parcelSolicitationUuid.getUuid())) {
421 return true;
422 }
423 }
424 return false;
425 }
426
427 // Check if the solicitation uuid pattern matches the particular service solicitation uuid.
428 private static boolean matchesServiceSolicitationUuid(UUID solicitationUuid,
429 UUID solicitationUuidMask, UUID data) {
430 return BitUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask);
431 }
432
Wei Wangadf6aff2014-05-20 06:30:20 +0000433 // Check whether the data pattern matches the parsed data.
434 private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) {
Wei Wang04624682014-07-28 21:46:44 -0700435 if (parsedData == null || parsedData.length < data.length) {
Wei Wangadf6aff2014-05-20 06:30:20 +0000436 return false;
437 }
Wei Wang04624682014-07-28 21:46:44 -0700438 if (dataMask == null) {
439 for (int i = 0; i < data.length; ++i) {
440 if (parsedData[i] != data[i]) {
441 return false;
442 }
443 }
444 return true;
445 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000446 for (int i = 0; i < data.length; ++i) {
447 if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) {
448 return false;
449 }
450 }
451 return true;
452 }
453
454 @Override
455 public String toString() {
Wei Wang685c17582014-07-16 22:02:03 -0700456 return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress="
457 + mDeviceAddress
Wei Wangab2ed622014-07-25 15:14:55 -0700458 + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530459 + ", mServiceSolicitationUuid=" + mServiceSolicitationUuid
460 + ", mServiceSolicitationUuidMask=" + mServiceSolicitationUuidMask
Wei Wangab2ed622014-07-25 15:14:55 -0700461 + ", mServiceDataUuid=" + Objects.toString(mServiceDataUuid) + ", mServiceData="
Wei Wangadf6aff2014-05-20 06:30:20 +0000462 + Arrays.toString(mServiceData) + ", mServiceDataMask="
463 + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId
464 + ", mManufacturerData=" + Arrays.toString(mManufacturerData)
Wei Wang685c17582014-07-16 22:02:03 -0700465 + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + "]";
Wei Wangadf6aff2014-05-20 06:30:20 +0000466 }
467
468 @Override
469 public int hashCode() {
Pavlin Radoslavov2f463d42016-05-06 12:05:47 -0700470 return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId,
Jack Hea355e5e2017-08-22 16:06:54 -0700471 Arrays.hashCode(mManufacturerData),
472 Arrays.hashCode(mManufacturerDataMask),
473 mServiceDataUuid,
474 Arrays.hashCode(mServiceData),
475 Arrays.hashCode(mServiceDataMask),
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530476 mServiceUuid, mServiceUuidMask,
477 mServiceSolicitationUuid, mServiceSolicitationUuidMask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000478 }
479
480 @Override
481 public boolean equals(Object obj) {
482 if (this == obj) {
483 return true;
484 }
485 if (obj == null || getClass() != obj.getClass()) {
486 return false;
487 }
Wei Wang6d811182014-05-22 12:10:25 -0700488 ScanFilter other = (ScanFilter) obj;
Jack He2992cd02017-08-22 21:21:23 -0700489 return Objects.equals(mDeviceName, other.mDeviceName)
490 && Objects.equals(mDeviceAddress, other.mDeviceAddress)
491 && mManufacturerId == other.mManufacturerId
492 && Objects.deepEquals(mManufacturerData, other.mManufacturerData)
493 && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask)
494 && Objects.equals(mServiceDataUuid, other.mServiceDataUuid)
495 && Objects.deepEquals(mServiceData, other.mServiceData)
496 && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask)
497 && Objects.equals(mServiceUuid, other.mServiceUuid)
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530498 && Objects.equals(mServiceUuidMask, other.mServiceUuidMask)
499 && Objects.equals(mServiceSolicitationUuid, other.mServiceSolicitationUuid)
500 && Objects.equals(mServiceSolicitationUuidMask,
501 other.mServiceSolicitationUuidMask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000502 }
503
504 /**
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700505 * Checks if the scanfilter is empty
Jack Hea355e5e2017-08-22 16:06:54 -0700506 *
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700507 * @hide
508 */
509 public boolean isAllFieldsEmpty() {
510 return EMPTY.equals(this);
511 }
512
513 /**
Wei Wang6d811182014-05-22 12:10:25 -0700514 * Builder class for {@link ScanFilter}.
Wei Wangadf6aff2014-05-20 06:30:20 +0000515 */
Wei Wang6d811182014-05-22 12:10:25 -0700516 public static final class Builder {
Wei Wangadf6aff2014-05-20 06:30:20 +0000517
Wei Wangaf74e662014-07-09 14:03:42 -0700518 private String mDeviceName;
519 private String mDeviceAddress;
Wei Wangadf6aff2014-05-20 06:30:20 +0000520
521 private ParcelUuid mServiceUuid;
522 private ParcelUuid mUuidMask;
523
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530524 private ParcelUuid mServiceSolicitationUuid;
525 private ParcelUuid mServiceSolicitationUuidMask;
526
Wei Wang685c17582014-07-16 22:02:03 -0700527 private ParcelUuid mServiceDataUuid;
Wei Wangadf6aff2014-05-20 06:30:20 +0000528 private byte[] mServiceData;
529 private byte[] mServiceDataMask;
530
531 private int mManufacturerId = -1;
532 private byte[] mManufacturerData;
533 private byte[] mManufacturerDataMask;
534
Wei Wangadf6aff2014-05-20 06:30:20 +0000535 /**
Wei Wangaf74e662014-07-09 14:03:42 -0700536 * Set filter on device name.
Wei Wangadf6aff2014-05-20 06:30:20 +0000537 */
Wei Wangaf74e662014-07-09 14:03:42 -0700538 public Builder setDeviceName(String deviceName) {
539 mDeviceName = deviceName;
Wei Wangadf6aff2014-05-20 06:30:20 +0000540 return this;
541 }
542
543 /**
Wei Wangaf74e662014-07-09 14:03:42 -0700544 * Set filter on device address.
Wei Wangadf6aff2014-05-20 06:30:20 +0000545 *
Wei Wang685c17582014-07-16 22:02:03 -0700546 * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
Jack Hea355e5e2017-08-22 16:06:54 -0700547 * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
548 * BluetoothAdapter#checkBluetoothAddress}.
Wei Wangaf74e662014-07-09 14:03:42 -0700549 * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
Wei Wangadf6aff2014-05-20 06:30:20 +0000550 */
Wei Wangaf74e662014-07-09 14:03:42 -0700551 public Builder setDeviceAddress(String deviceAddress) {
552 if (deviceAddress != null && !BluetoothAdapter.checkBluetoothAddress(deviceAddress)) {
553 throw new IllegalArgumentException("invalid device address " + deviceAddress);
Wei Wangadf6aff2014-05-20 06:30:20 +0000554 }
Wei Wangaf74e662014-07-09 14:03:42 -0700555 mDeviceAddress = deviceAddress;
Wei Wangadf6aff2014-05-20 06:30:20 +0000556 return this;
557 }
558
559 /**
Wei Wang6d811182014-05-22 12:10:25 -0700560 * Set filter on service uuid.
Wei Wangadf6aff2014-05-20 06:30:20 +0000561 */
Wei Wang6d811182014-05-22 12:10:25 -0700562 public Builder setServiceUuid(ParcelUuid serviceUuid) {
Wei Wangadf6aff2014-05-20 06:30:20 +0000563 mServiceUuid = serviceUuid;
Wei Wang6d811182014-05-22 12:10:25 -0700564 mUuidMask = null; // clear uuid mask
Wei Wangadf6aff2014-05-20 06:30:20 +0000565 return this;
566 }
567
568 /**
Wei Wang6d811182014-05-22 12:10:25 -0700569 * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the
570 * {@code serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the
571 * bit in {@code serviceUuid}, and 0 to ignore that bit.
572 *
Jack Hea355e5e2017-08-22 16:06:54 -0700573 * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but {@code
574 * uuidMask} is not {@code null}.
Wei Wangadf6aff2014-05-20 06:30:20 +0000575 */
Wei Wang6d811182014-05-22 12:10:25 -0700576 public Builder setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask) {
577 if (mUuidMask != null && mServiceUuid == null) {
578 throw new IllegalArgumentException("uuid is null while uuidMask is not null!");
579 }
580 mServiceUuid = serviceUuid;
Wei Wangadf6aff2014-05-20 06:30:20 +0000581 mUuidMask = uuidMask;
582 return this;
583 }
584
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530585
586 /**
587 * Set filter on service solicitation uuid.
588 */
589 public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid) {
590 mServiceSolicitationUuid = serviceSolicitationUuid;
591 return this;
592 }
593
594
595 /**
596 * Set filter on partial service Solicitation uuid. The {@code SolicitationUuidMask} is the
597 * bit mask for the {@code serviceSolicitationUuid}. Set any bit in the mask to 1 to
598 * indicate a match is needed for the bit in {@code serviceSolicitationUuid}, and 0 to
599 * ignore that bit.
600 *
601 * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but
602 * {@code serviceSolicitationUuidMask} is not {@code null}.
603 */
604 public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid,
605 ParcelUuid solicitationUuidMask) {
606 if (mServiceSolicitationUuidMask != null && mServiceSolicitationUuid == null) {
607 throw new IllegalArgumentException(
608 "SolicitationUuid is null while SolicitationUuidMask is not null!");
609 }
610 mServiceSolicitationUuid = serviceSolicitationUuid;
611 mServiceSolicitationUuidMask = solicitationUuidMask;
612 return this;
613 }
614
Wei Wangadf6aff2014-05-20 06:30:20 +0000615 /**
Wei Wang6d811182014-05-22 12:10:25 -0700616 * Set filtering on service data.
Wei Wang685c17582014-07-16 22:02:03 -0700617 *
618 * @throws IllegalArgumentException If {@code serviceDataUuid} is null.
Wei Wangadf6aff2014-05-20 06:30:20 +0000619 */
Wei Wang685c17582014-07-16 22:02:03 -0700620 public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
621 if (serviceDataUuid == null) {
622 throw new IllegalArgumentException("serviceDataUuid is null");
623 }
624 mServiceDataUuid = serviceDataUuid;
Wei Wangadf6aff2014-05-20 06:30:20 +0000625 mServiceData = serviceData;
Wei Wang6d811182014-05-22 12:10:25 -0700626 mServiceDataMask = null; // clear service data mask
Wei Wangadf6aff2014-05-20 06:30:20 +0000627 return this;
628 }
629
630 /**
Wei Wang6d811182014-05-22 12:10:25 -0700631 * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to
632 * match the one in service data, otherwise set it to 0 to ignore that bit.
Wei Wangadf6aff2014-05-20 06:30:20 +0000633 * <p>
Wei Wang6d811182014-05-22 12:10:25 -0700634 * The {@code serviceDataMask} must have the same length of the {@code serviceData}.
Wei Wangadf6aff2014-05-20 06:30:20 +0000635 *
Jack Hea355e5e2017-08-22 16:06:54 -0700636 * @throws IllegalArgumentException If {@code serviceDataUuid} is null or {@code
637 * serviceDataMask} is {@code null} while {@code serviceData} is not or {@code
638 * serviceDataMask} and {@code serviceData} has different length.
Wei Wangadf6aff2014-05-20 06:30:20 +0000639 */
Wei Wang685c17582014-07-16 22:02:03 -0700640 public Builder setServiceData(ParcelUuid serviceDataUuid,
641 byte[] serviceData, byte[] serviceDataMask) {
642 if (serviceDataUuid == null) {
643 throw new IllegalArgumentException("serviceDataUuid is null");
644 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000645 if (mServiceDataMask != null) {
646 if (mServiceData == null) {
647 throw new IllegalArgumentException(
648 "serviceData is null while serviceDataMask is not null");
649 }
650 // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two
651 // byte array need to be the same.
652 if (mServiceData.length != mServiceDataMask.length) {
653 throw new IllegalArgumentException(
654 "size mismatch for service data and service data mask");
655 }
656 }
Wei Wangab2ed622014-07-25 15:14:55 -0700657 mServiceDataUuid = serviceDataUuid;
Wei Wang6d811182014-05-22 12:10:25 -0700658 mServiceData = serviceData;
659 mServiceDataMask = serviceDataMask;
660 return this;
661 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000662
Wei Wang6d811182014-05-22 12:10:25 -0700663 /**
664 * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id.
665 * <p>
666 * Note the first two bytes of the {@code manufacturerData} is the manufacturerId.
667 *
668 * @throws IllegalArgumentException If the {@code manufacturerId} is invalid.
669 */
670 public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData) {
671 if (manufacturerData != null && manufacturerId < 0) {
672 throw new IllegalArgumentException("invalid manufacture id");
673 }
674 mManufacturerId = manufacturerId;
675 mManufacturerData = manufacturerData;
676 mManufacturerDataMask = null; // clear manufacturer data mask
677 return this;
678 }
679
680 /**
Wei Wang685c17582014-07-16 22:02:03 -0700681 * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it needs
682 * to match the one in manufacturer data, otherwise set it to 0.
Wei Wang6d811182014-05-22 12:10:25 -0700683 * <p>
684 * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}.
685 *
Jack Hea355e5e2017-08-22 16:06:54 -0700686 * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or {@code
687 * manufacturerData} is null while {@code manufacturerDataMask} is not, or {@code
688 * manufacturerData} and {@code manufacturerDataMask} have different length.
Wei Wang6d811182014-05-22 12:10:25 -0700689 */
690 public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData,
691 byte[] manufacturerDataMask) {
692 if (manufacturerData != null && manufacturerId < 0) {
693 throw new IllegalArgumentException("invalid manufacture id");
694 }
Wei Wangadf6aff2014-05-20 06:30:20 +0000695 if (mManufacturerDataMask != null) {
696 if (mManufacturerData == null) {
697 throw new IllegalArgumentException(
698 "manufacturerData is null while manufacturerDataMask is not null");
699 }
700 // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths
701 // of the two byte array need to be the same.
702 if (mManufacturerData.length != mManufacturerDataMask.length) {
703 throw new IllegalArgumentException(
704 "size mismatch for manufacturerData and manufacturerDataMask");
705 }
706 }
Wei Wang6d811182014-05-22 12:10:25 -0700707 mManufacturerId = manufacturerId;
708 mManufacturerData = manufacturerData;
709 mManufacturerDataMask = manufacturerDataMask;
710 return this;
711 }
712
713 /**
Wei Wang6d811182014-05-22 12:10:25 -0700714 * Build {@link ScanFilter}.
715 *
716 * @throws IllegalArgumentException If the filter cannot be built.
717 */
718 public ScanFilter build() {
Wei Wangaf74e662014-07-09 14:03:42 -0700719 return new ScanFilter(mDeviceName, mDeviceAddress,
Nitin Shivpure1555eae2018-04-02 13:45:45 +0530720 mServiceUuid, mUuidMask, mServiceSolicitationUuid,
721 mServiceSolicitationUuidMask,
Wei Wang685c17582014-07-16 22:02:03 -0700722 mServiceDataUuid, mServiceData, mServiceDataMask,
723 mManufacturerId, mManufacturerData, mManufacturerDataMask);
Wei Wangadf6aff2014-05-20 06:30:20 +0000724 }
725 }
726}