1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package android.bluetooth.le; 18 19 import android.Manifest; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.app.ActivityThread; 25 import android.app.PendingIntent; 26 import android.bluetooth.BluetoothAdapter; 27 import android.bluetooth.BluetoothGatt; 28 import android.bluetooth.IBluetoothGatt; 29 import android.bluetooth.IBluetoothManager; 30 import android.os.Handler; 31 import android.os.Looper; 32 import android.os.RemoteException; 33 import android.os.WorkSource; 34 import android.util.Log; 35 36 import java.util.ArrayList; 37 import java.util.HashMap; 38 import java.util.List; 39 import java.util.Map; 40 41 /** 42 * This class provides methods to perform scan related operations for Bluetooth LE devices. An 43 * application can scan for a particular type of Bluetooth LE devices using {@link ScanFilter}. It 44 * can also request different types of callbacks for delivering the result. 45 * <p> 46 * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of 47 * {@link BluetoothLeScanner}. 48 * <p> 49 * <b>Note:</b> Most of the scan methods here require 50 * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. 51 * 52 * @see ScanFilter 53 */ 54 public final class BluetoothLeScanner { 55 56 private static final String TAG = "BluetoothLeScanner"; 57 private static final boolean DBG = true; 58 private static final boolean VDBG = false; 59 60 /** 61 * Extra containing a list of ScanResults. It can have one or more results if there was no 62 * error. In case of error, {@link #EXTRA_ERROR_CODE} will contain the error code and this 63 * extra will not be available. 64 */ 65 public static final String EXTRA_LIST_SCAN_RESULT = 66 "android.bluetooth.le.extra.LIST_SCAN_RESULT"; 67 68 /** 69 * Optional extra indicating the error code, if any. The error code will be one of the 70 * SCAN_FAILED_* codes in {@link ScanCallback}. 71 */ 72 public static final String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE"; 73 74 /** 75 * Optional extra indicating the callback type, which will be one of 76 * CALLBACK_TYPE_* constants in {@link ScanSettings}. 77 * 78 * @see ScanCallback#onScanResult(int, ScanResult) 79 */ 80 public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE"; 81 82 private final IBluetoothManager mBluetoothManager; 83 private final Handler mHandler; 84 private BluetoothAdapter mBluetoothAdapter; 85 private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients; 86 87 /** 88 * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. 89 * 90 * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. 91 * @hide 92 */ BluetoothLeScanner(IBluetoothManager bluetoothManager)93 public BluetoothLeScanner(IBluetoothManager bluetoothManager) { 94 mBluetoothManager = bluetoothManager; 95 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 96 mHandler = new Handler(Looper.getMainLooper()); 97 mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>(); 98 } 99 100 /** 101 * Start Bluetooth LE scan with default parameters and no filters. The scan results will be 102 * delivered through {@code callback}. For unfiltered scans, scanning is stopped on screen 103 * off to save power. Scanning is resumed when screen is turned on again. To avoid this, use 104 * {@link #startScan(List, ScanSettings, ScanCallback)} with desired {@link ScanFilter}. 105 * <p> 106 * An app must hold 107 * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or 108 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission 109 * in order to get results. 110 * 111 * @param callback Callback used to deliver scan results. 112 * @throws IllegalArgumentException If {@code callback} is null. 113 */ 114 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) startScan(final ScanCallback callback)115 public void startScan(final ScanCallback callback) { 116 startScan(null, new ScanSettings.Builder().build(), callback); 117 } 118 119 /** 120 * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}. 121 * For unfiltered scans, scanning is stopped on screen off to save power. Scanning is 122 * resumed when screen is turned on again. To avoid this, do filetered scanning by 123 * using proper {@link ScanFilter}. 124 * <p> 125 * An app must hold 126 * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or 127 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission 128 * in order to get results. 129 * 130 * @param filters {@link ScanFilter}s for finding exact BLE devices. 131 * @param settings Settings for the scan. 132 * @param callback Callback used to deliver scan results. 133 * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. 134 */ 135 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) startScan(List<ScanFilter> filters, ScanSettings settings, final ScanCallback callback)136 public void startScan(List<ScanFilter> filters, ScanSettings settings, 137 final ScanCallback callback) { 138 startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null); 139 } 140 141 /** 142 * Start Bluetooth LE scan using a {@link PendingIntent}. The scan results will be delivered via 143 * the PendingIntent. Use this method of scanning if your process is not always running and it 144 * should be started when scan results are available. 145 * <p> 146 * An app must hold 147 * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or 148 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission 149 * in order to get results. 150 * <p> 151 * When the PendingIntent is delivered, the Intent passed to the receiver or activity 152 * will contain one or more of the extras {@link #EXTRA_CALLBACK_TYPE}, 153 * {@link #EXTRA_ERROR_CODE} and {@link #EXTRA_LIST_SCAN_RESULT} to indicate the result of 154 * the scan. 155 * 156 * @param filters Optional list of ScanFilters for finding exact BLE devices. 157 * @param settings Optional settings for the scan. 158 * @param callbackIntent The PendingIntent to deliver the result to. 159 * @return Returns 0 for success or an error code from {@link ScanCallback} if the scan request 160 * could not be sent. 161 * @see #stopScan(PendingIntent) 162 */ 163 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) startScan(@ullable List<ScanFilter> filters, @Nullable ScanSettings settings, @NonNull PendingIntent callbackIntent)164 public int startScan(@Nullable List<ScanFilter> filters, @Nullable ScanSettings settings, 165 @NonNull PendingIntent callbackIntent) { 166 return startScan(filters, 167 settings != null ? settings : new ScanSettings.Builder().build(), 168 null, null, callbackIntent, null); 169 } 170 171 /** 172 * Start Bluetooth LE scan. Same as {@link #startScan(ScanCallback)} but allows the caller to 173 * specify on behalf of which application(s) the work is being done. 174 * 175 * @param workSource {@link WorkSource} identifying the application(s) for which to blame for 176 * the scan. 177 * @param callback Callback used to deliver scan results. 178 * @hide 179 */ 180 @SystemApi 181 @RequiresPermission(allOf = { 182 Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS}) startScanFromSource(final WorkSource workSource, final ScanCallback callback)183 public void startScanFromSource(final WorkSource workSource, final ScanCallback callback) { 184 startScanFromSource(null, new ScanSettings.Builder().build(), workSource, callback); 185 } 186 187 /** 188 * Start Bluetooth LE scan. Same as {@link #startScan(List, ScanSettings, ScanCallback)} but 189 * allows the caller to specify on behalf of which application(s) the work is being done. 190 * 191 * @param filters {@link ScanFilter}s for finding exact BLE devices. 192 * @param settings Settings for the scan. 193 * @param workSource {@link WorkSource} identifying the application(s) for which to blame for 194 * the scan. 195 * @param callback Callback used to deliver scan results. 196 * @hide 197 */ 198 @SystemApi 199 @RequiresPermission(allOf = { 200 Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS}) startScanFromSource(List<ScanFilter> filters, ScanSettings settings, final WorkSource workSource, final ScanCallback callback)201 public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings, 202 final WorkSource workSource, final ScanCallback callback) { 203 startScan(filters, settings, workSource, callback, null, null); 204 } 205 startScan(List<ScanFilter> filters, ScanSettings settings, final WorkSource workSource, final ScanCallback callback, final PendingIntent callbackIntent, List<List<ResultStorageDescriptor>> resultStorages)206 private int startScan(List<ScanFilter> filters, ScanSettings settings, 207 final WorkSource workSource, final ScanCallback callback, 208 final PendingIntent callbackIntent, 209 List<List<ResultStorageDescriptor>> resultStorages) { 210 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); 211 if (callback == null && callbackIntent == null) { 212 throw new IllegalArgumentException("callback is null"); 213 } 214 if (settings == null) { 215 throw new IllegalArgumentException("settings is null"); 216 } 217 synchronized (mLeScanClients) { 218 if (callback != null && mLeScanClients.containsKey(callback)) { 219 return postCallbackErrorOrReturn(callback, 220 ScanCallback.SCAN_FAILED_ALREADY_STARTED); 221 } 222 IBluetoothGatt gatt; 223 try { 224 gatt = mBluetoothManager.getBluetoothGatt(); 225 } catch (RemoteException e) { 226 gatt = null; 227 } 228 if (gatt == null) { 229 return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); 230 } 231 if (!isSettingsConfigAllowedForScan(settings)) { 232 return postCallbackErrorOrReturn(callback, 233 ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); 234 } 235 if (!isHardwareResourcesAvailableForScan(settings)) { 236 return postCallbackErrorOrReturn(callback, 237 ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES); 238 } 239 if (!isSettingsAndFilterComboAllowed(settings, filters)) { 240 return postCallbackErrorOrReturn(callback, 241 ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); 242 } 243 if (callback != null) { 244 BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, 245 settings, workSource, callback, resultStorages); 246 wrapper.startRegistration(); 247 } else { 248 try { 249 gatt.startScanForIntent(callbackIntent, settings, filters, 250 ActivityThread.currentOpPackageName()); 251 } catch (RemoteException e) { 252 return ScanCallback.SCAN_FAILED_INTERNAL_ERROR; 253 } 254 } 255 } 256 return ScanCallback.NO_ERROR; 257 } 258 259 /** 260 * Stops an ongoing Bluetooth LE scan. 261 * 262 * @param callback 263 */ 264 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) stopScan(ScanCallback callback)265 public void stopScan(ScanCallback callback) { 266 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); 267 synchronized (mLeScanClients) { 268 BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback); 269 if (wrapper == null) { 270 if (DBG) Log.d(TAG, "could not find callback wrapper"); 271 return; 272 } 273 wrapper.stopLeScan(); 274 } 275 } 276 277 /** 278 * Stops an ongoing Bluetooth LE scan started using a PendingIntent. When creating the 279 * PendingIntent parameter, please do not use the FLAG_CANCEL_CURRENT flag. Otherwise, the stop 280 * scan may have no effect. 281 * 282 * @param callbackIntent The PendingIntent that was used to start the scan. 283 * @see #startScan(List, ScanSettings, PendingIntent) 284 */ 285 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) stopScan(PendingIntent callbackIntent)286 public void stopScan(PendingIntent callbackIntent) { 287 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); 288 IBluetoothGatt gatt; 289 try { 290 gatt = mBluetoothManager.getBluetoothGatt(); 291 gatt.stopScanForIntent(callbackIntent, ActivityThread.currentOpPackageName()); 292 } catch (RemoteException e) { 293 } 294 } 295 296 /** 297 * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth 298 * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data 299 * will be delivered through the {@code callback}. 300 * 301 * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one 302 * used to start scan. 303 */ flushPendingScanResults(ScanCallback callback)304 public void flushPendingScanResults(ScanCallback callback) { 305 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); 306 if (callback == null) { 307 throw new IllegalArgumentException("callback cannot be null!"); 308 } 309 synchronized (mLeScanClients) { 310 BleScanCallbackWrapper wrapper = mLeScanClients.get(callback); 311 if (wrapper == null) { 312 return; 313 } 314 wrapper.flushPendingBatchResults(); 315 } 316 } 317 318 /** 319 * Start truncated scan. 320 * 321 * @hide 322 */ 323 @SystemApi startTruncatedScan(List<TruncatedFilter> truncatedFilters, ScanSettings settings, final ScanCallback callback)324 public void startTruncatedScan(List<TruncatedFilter> truncatedFilters, ScanSettings settings, 325 final ScanCallback callback) { 326 int filterSize = truncatedFilters.size(); 327 List<ScanFilter> scanFilters = new ArrayList<ScanFilter>(filterSize); 328 List<List<ResultStorageDescriptor>> scanStorages = 329 new ArrayList<List<ResultStorageDescriptor>>(filterSize); 330 for (TruncatedFilter filter : truncatedFilters) { 331 scanFilters.add(filter.getFilter()); 332 scanStorages.add(filter.getStorageDescriptors()); 333 } 334 startScan(scanFilters, settings, null, callback, null, scanStorages); 335 } 336 337 /** 338 * Cleans up scan clients. Should be called when bluetooth is down. 339 * 340 * @hide 341 */ cleanup()342 public void cleanup() { 343 mLeScanClients.clear(); 344 } 345 346 /** 347 * Bluetooth GATT interface callbacks 348 */ 349 private class BleScanCallbackWrapper extends IScannerCallback.Stub { 350 private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000; 351 352 private final ScanCallback mScanCallback; 353 private final List<ScanFilter> mFilters; 354 private final WorkSource mWorkSource; 355 private ScanSettings mSettings; 356 private IBluetoothGatt mBluetoothGatt; 357 private List<List<ResultStorageDescriptor>> mResultStorages; 358 359 // mLeHandle 0: not registered 360 // -2: registration failed because app is scanning to frequently 361 // -1: scan stopped or registration failed 362 // > 0: registered and scan started 363 private int mScannerId; 364 BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, List<ScanFilter> filters, ScanSettings settings, WorkSource workSource, ScanCallback scanCallback, List<List<ResultStorageDescriptor>> resultStorages)365 public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, 366 List<ScanFilter> filters, ScanSettings settings, 367 WorkSource workSource, ScanCallback scanCallback, 368 List<List<ResultStorageDescriptor>> resultStorages) { 369 mBluetoothGatt = bluetoothGatt; 370 mFilters = filters; 371 mSettings = settings; 372 mWorkSource = workSource; 373 mScanCallback = scanCallback; 374 mScannerId = 0; 375 mResultStorages = resultStorages; 376 } 377 startRegistration()378 public void startRegistration() { 379 synchronized (this) { 380 // Scan stopped. 381 if (mScannerId == -1 || mScannerId == -2) return; 382 try { 383 mBluetoothGatt.registerScanner(this, mWorkSource); 384 wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); 385 } catch (InterruptedException | RemoteException e) { 386 Log.e(TAG, "application registeration exception", e); 387 postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); 388 } 389 if (mScannerId > 0) { 390 mLeScanClients.put(mScanCallback, this); 391 } else { 392 // Registration timed out or got exception, reset RscannerId to -1 so no 393 // subsequent operations can proceed. 394 if (mScannerId == 0) mScannerId = -1; 395 396 // If scanning too frequently, don't report anything to the app. 397 if (mScannerId == -2) return; 398 399 postCallbackError(mScanCallback, 400 ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED); 401 } 402 } 403 } 404 stopLeScan()405 public void stopLeScan() { 406 synchronized (this) { 407 if (mScannerId <= 0) { 408 Log.e(TAG, "Error state, mLeHandle: " + mScannerId); 409 return; 410 } 411 try { 412 mBluetoothGatt.stopScan(mScannerId); 413 mBluetoothGatt.unregisterScanner(mScannerId); 414 } catch (RemoteException e) { 415 Log.e(TAG, "Failed to stop scan and unregister", e); 416 } 417 mScannerId = -1; 418 } 419 } 420 flushPendingBatchResults()421 void flushPendingBatchResults() { 422 synchronized (this) { 423 if (mScannerId <= 0) { 424 Log.e(TAG, "Error state, mLeHandle: " + mScannerId); 425 return; 426 } 427 try { 428 mBluetoothGatt.flushPendingBatchResults(mScannerId); 429 } catch (RemoteException e) { 430 Log.e(TAG, "Failed to get pending scan results", e); 431 } 432 } 433 } 434 435 /** 436 * Application interface registered - app is ready to go 437 */ 438 @Override onScannerRegistered(int status, int scannerId)439 public void onScannerRegistered(int status, int scannerId) { 440 Log.d(TAG, "onScannerRegistered() - status=" + status 441 + " scannerId=" + scannerId + " mScannerId=" + mScannerId); 442 synchronized (this) { 443 if (status == BluetoothGatt.GATT_SUCCESS) { 444 try { 445 if (mScannerId == -1) { 446 // Registration succeeds after timeout, unregister scanner. 447 mBluetoothGatt.unregisterScanner(scannerId); 448 } else { 449 mScannerId = scannerId; 450 mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, 451 mResultStorages, 452 ActivityThread.currentOpPackageName()); 453 } 454 } catch (RemoteException e) { 455 Log.e(TAG, "fail to start le scan: " + e); 456 mScannerId = -1; 457 } 458 } else if (status == ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY) { 459 // applicaiton was scanning too frequently 460 mScannerId = -2; 461 } else { 462 // registration failed 463 mScannerId = -1; 464 } 465 notifyAll(); 466 } 467 } 468 469 /** 470 * Callback reporting an LE scan result. 471 * 472 * @hide 473 */ 474 @Override onScanResult(final ScanResult scanResult)475 public void onScanResult(final ScanResult scanResult) { 476 if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); 477 478 // Check null in case the scan has been stopped 479 synchronized (this) { 480 if (mScannerId <= 0) return; 481 } 482 Handler handler = new Handler(Looper.getMainLooper()); 483 handler.post(new Runnable() { 484 @Override 485 public void run() { 486 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); 487 } 488 }); 489 } 490 491 @Override onBatchScanResults(final List<ScanResult> results)492 public void onBatchScanResults(final List<ScanResult> results) { 493 Handler handler = new Handler(Looper.getMainLooper()); 494 handler.post(new Runnable() { 495 @Override 496 public void run() { 497 mScanCallback.onBatchScanResults(results); 498 } 499 }); 500 } 501 502 @Override onFoundOrLost(final boolean onFound, final ScanResult scanResult)503 public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) { 504 if (VDBG) { 505 Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + " " + scanResult.toString()); 506 } 507 508 // Check null in case the scan has been stopped 509 synchronized (this) { 510 if (mScannerId <= 0) { 511 return; 512 } 513 } 514 Handler handler = new Handler(Looper.getMainLooper()); 515 handler.post(new Runnable() { 516 @Override 517 public void run() { 518 if (onFound) { 519 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, 520 scanResult); 521 } else { 522 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, 523 scanResult); 524 } 525 } 526 }); 527 } 528 529 @Override onScanManagerErrorCallback(final int errorCode)530 public void onScanManagerErrorCallback(final int errorCode) { 531 if (VDBG) { 532 Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode); 533 } 534 synchronized (this) { 535 if (mScannerId <= 0) { 536 return; 537 } 538 } 539 postCallbackError(mScanCallback, errorCode); 540 } 541 } 542 postCallbackErrorOrReturn(final ScanCallback callback, final int errorCode)543 private int postCallbackErrorOrReturn(final ScanCallback callback, final int errorCode) { 544 if (callback == null) { 545 return errorCode; 546 } else { 547 postCallbackError(callback, errorCode); 548 return ScanCallback.NO_ERROR; 549 } 550 } 551 postCallbackError(final ScanCallback callback, final int errorCode)552 private void postCallbackError(final ScanCallback callback, final int errorCode) { 553 mHandler.post(new Runnable() { 554 @Override 555 public void run() { 556 callback.onScanFailed(errorCode); 557 } 558 }); 559 } 560 isSettingsConfigAllowedForScan(ScanSettings settings)561 private boolean isSettingsConfigAllowedForScan(ScanSettings settings) { 562 if (mBluetoothAdapter.isOffloadedFilteringSupported()) { 563 return true; 564 } 565 final int callbackType = settings.getCallbackType(); 566 // Only support regular scan if no offloaded filter support. 567 if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES 568 && settings.getReportDelayMillis() == 0) { 569 return true; 570 } 571 return false; 572 } 573 isSettingsAndFilterComboAllowed(ScanSettings settings, List<ScanFilter> filterList)574 private boolean isSettingsAndFilterComboAllowed(ScanSettings settings, 575 List<ScanFilter> filterList) { 576 final int callbackType = settings.getCallbackType(); 577 // If onlost/onfound is requested, a non-empty filter is expected 578 if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH 579 | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) { 580 if (filterList == null) { 581 return false; 582 } 583 for (ScanFilter filter : filterList) { 584 if (filter.isAllFieldsEmpty()) { 585 return false; 586 } 587 } 588 } 589 return true; 590 } 591 isHardwareResourcesAvailableForScan(ScanSettings settings)592 private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) { 593 final int callbackType = settings.getCallbackType(); 594 if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0 595 || (callbackType & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) { 596 // For onlost/onfound, we required hw support be available 597 return (mBluetoothAdapter.isOffloadedFilteringSupported() 598 && mBluetoothAdapter.isHardwareTrackingFiltersAvailable()); 599 } 600 return true; 601 } 602 } 603