Home | History | Annotate | Download | only in le
      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      */
     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)
    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)
    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)
    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})
    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})
    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 
    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)
    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.
    279      *
    280      * @param callbackIntent The PendingIntent that was used to start the scan.
    281      * @see #startScan(List, ScanSettings, PendingIntent)
    282      */
    283     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
    284     public void stopScan(PendingIntent callbackIntent) {
    285         BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
    286         IBluetoothGatt gatt;
    287         try {
    288             gatt = mBluetoothManager.getBluetoothGatt();
    289             gatt.stopScanForIntent(callbackIntent, ActivityThread.currentOpPackageName());
    290         } catch (RemoteException e) {
    291         }
    292     }
    293 
    294     /**
    295      * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth
    296      * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data
    297      * will be delivered through the {@code callback}.
    298      *
    299      * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
    300      * used to start scan.
    301      */
    302     public void flushPendingScanResults(ScanCallback callback) {
    303         BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
    304         if (callback == null) {
    305             throw new IllegalArgumentException("callback cannot be null!");
    306         }
    307         synchronized (mLeScanClients) {
    308             BleScanCallbackWrapper wrapper = mLeScanClients.get(callback);
    309             if (wrapper == null) {
    310                 return;
    311             }
    312             wrapper.flushPendingBatchResults();
    313         }
    314     }
    315 
    316     /**
    317      * Start truncated scan.
    318      *
    319      * @hide
    320      */
    321     @SystemApi
    322     public void startTruncatedScan(List<TruncatedFilter> truncatedFilters, ScanSettings settings,
    323             final ScanCallback callback) {
    324         int filterSize = truncatedFilters.size();
    325         List<ScanFilter> scanFilters = new ArrayList<ScanFilter>(filterSize);
    326         List<List<ResultStorageDescriptor>> scanStorages =
    327                 new ArrayList<List<ResultStorageDescriptor>>(filterSize);
    328         for (TruncatedFilter filter : truncatedFilters) {
    329             scanFilters.add(filter.getFilter());
    330             scanStorages.add(filter.getStorageDescriptors());
    331         }
    332         startScan(scanFilters, settings, null, callback, null, scanStorages);
    333     }
    334 
    335     /**
    336      * Cleans up scan clients. Should be called when bluetooth is down.
    337      *
    338      * @hide
    339      */
    340     public void cleanup() {
    341         mLeScanClients.clear();
    342     }
    343 
    344     /**
    345      * Bluetooth GATT interface callbacks
    346      */
    347     private class BleScanCallbackWrapper extends IScannerCallback.Stub {
    348         private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000;
    349 
    350         private final ScanCallback mScanCallback;
    351         private final List<ScanFilter> mFilters;
    352         private final WorkSource mWorkSource;
    353         private ScanSettings mSettings;
    354         private IBluetoothGatt mBluetoothGatt;
    355         private List<List<ResultStorageDescriptor>> mResultStorages;
    356 
    357         // mLeHandle 0: not registered
    358         // -2: registration failed because app is scanning to frequently
    359         // -1: scan stopped or registration failed
    360         // > 0: registered and scan started
    361         private int mScannerId;
    362 
    363         public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
    364                 List<ScanFilter> filters, ScanSettings settings,
    365                 WorkSource workSource, ScanCallback scanCallback,
    366                 List<List<ResultStorageDescriptor>> resultStorages) {
    367             mBluetoothGatt = bluetoothGatt;
    368             mFilters = filters;
    369             mSettings = settings;
    370             mWorkSource = workSource;
    371             mScanCallback = scanCallback;
    372             mScannerId = 0;
    373             mResultStorages = resultStorages;
    374         }
    375 
    376         public void startRegistration() {
    377             synchronized (this) {
    378                 // Scan stopped.
    379                 if (mScannerId == -1 || mScannerId == -2) return;
    380                 try {
    381                     mBluetoothGatt.registerScanner(this, mWorkSource);
    382                     wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS);
    383                 } catch (InterruptedException | RemoteException e) {
    384                     Log.e(TAG, "application registeration exception", e);
    385                     postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
    386                 }
    387                 if (mScannerId > 0) {
    388                     mLeScanClients.put(mScanCallback, this);
    389                 } else {
    390                     // Registration timed out or got exception, reset RscannerId to -1 so no
    391                     // subsequent operations can proceed.
    392                     if (mScannerId == 0) mScannerId = -1;
    393 
    394                     // If scanning too frequently, don't report anything to the app.
    395                     if (mScannerId == -2) return;
    396 
    397                     postCallbackError(mScanCallback,
    398                             ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
    399                 }
    400             }
    401         }
    402 
    403         public void stopLeScan() {
    404             synchronized (this) {
    405                 if (mScannerId <= 0) {
    406                     Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
    407                     return;
    408                 }
    409                 try {
    410                     mBluetoothGatt.stopScan(mScannerId);
    411                     mBluetoothGatt.unregisterScanner(mScannerId);
    412                 } catch (RemoteException e) {
    413                     Log.e(TAG, "Failed to stop scan and unregister", e);
    414                 }
    415                 mScannerId = -1;
    416             }
    417         }
    418 
    419         void flushPendingBatchResults() {
    420             synchronized (this) {
    421                 if (mScannerId <= 0) {
    422                     Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
    423                     return;
    424                 }
    425                 try {
    426                     mBluetoothGatt.flushPendingBatchResults(mScannerId);
    427                 } catch (RemoteException e) {
    428                     Log.e(TAG, "Failed to get pending scan results", e);
    429                 }
    430             }
    431         }
    432 
    433         /**
    434          * Application interface registered - app is ready to go
    435          */
    436         @Override
    437         public void onScannerRegistered(int status, int scannerId) {
    438             Log.d(TAG, "onScannerRegistered() - status=" + status
    439                     + " scannerId=" + scannerId + " mScannerId=" + mScannerId);
    440             synchronized (this) {
    441                 if (status == BluetoothGatt.GATT_SUCCESS) {
    442                     try {
    443                         if (mScannerId == -1) {
    444                             // Registration succeeds after timeout, unregister client.
    445                             mBluetoothGatt.unregisterClient(scannerId);
    446                         } else {
    447                             mScannerId = scannerId;
    448                             mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
    449                                     mResultStorages,
    450                                     ActivityThread.currentOpPackageName());
    451                         }
    452                     } catch (RemoteException e) {
    453                         Log.e(TAG, "fail to start le scan: " + e);
    454                         mScannerId = -1;
    455                     }
    456                 } else if (status == ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY) {
    457                     // applicaiton was scanning too frequently
    458                     mScannerId = -2;
    459                 } else {
    460                     // registration failed
    461                     mScannerId = -1;
    462                 }
    463                 notifyAll();
    464             }
    465         }
    466 
    467         /**
    468          * Callback reporting an LE scan result.
    469          *
    470          * @hide
    471          */
    472         @Override
    473         public void onScanResult(final ScanResult scanResult) {
    474             if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
    475 
    476             // Check null in case the scan has been stopped
    477             synchronized (this) {
    478                 if (mScannerId <= 0) return;
    479             }
    480             Handler handler = new Handler(Looper.getMainLooper());
    481             handler.post(new Runnable() {
    482                 @Override
    483                 public void run() {
    484                     mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
    485                 }
    486             });
    487         }
    488 
    489         @Override
    490         public void onBatchScanResults(final List<ScanResult> results) {
    491             Handler handler = new Handler(Looper.getMainLooper());
    492             handler.post(new Runnable() {
    493                 @Override
    494                 public void run() {
    495                     mScanCallback.onBatchScanResults(results);
    496                 }
    497             });
    498         }
    499 
    500         @Override
    501         public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) {
    502             if (VDBG) {
    503                 Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + " " + scanResult.toString());
    504             }
    505 
    506             // Check null in case the scan has been stopped
    507             synchronized (this) {
    508                 if (mScannerId <= 0) {
    509                     return;
    510                 }
    511             }
    512             Handler handler = new Handler(Looper.getMainLooper());
    513             handler.post(new Runnable() {
    514                 @Override
    515                 public void run() {
    516                     if (onFound) {
    517                         mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH,
    518                                 scanResult);
    519                     } else {
    520                         mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST,
    521                                 scanResult);
    522                     }
    523                 }
    524             });
    525         }
    526 
    527         @Override
    528         public void onScanManagerErrorCallback(final int errorCode) {
    529             if (VDBG) {
    530                 Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode);
    531             }
    532             synchronized (this) {
    533                 if (mScannerId <= 0) {
    534                     return;
    535                 }
    536             }
    537             postCallbackError(mScanCallback, errorCode);
    538         }
    539     }
    540 
    541     private int postCallbackErrorOrReturn(final ScanCallback callback, final int errorCode) {
    542         if (callback == null) {
    543             return errorCode;
    544         } else {
    545             postCallbackError(callback, errorCode);
    546             return ScanCallback.NO_ERROR;
    547         }
    548     }
    549 
    550     private void postCallbackError(final ScanCallback callback, final int errorCode) {
    551         mHandler.post(new Runnable() {
    552             @Override
    553             public void run() {
    554                 callback.onScanFailed(errorCode);
    555             }
    556         });
    557     }
    558 
    559     private boolean isSettingsConfigAllowedForScan(ScanSettings settings) {
    560         if (mBluetoothAdapter.isOffloadedFilteringSupported()) {
    561             return true;
    562         }
    563         final int callbackType = settings.getCallbackType();
    564         // Only support regular scan if no offloaded filter support.
    565         if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
    566                 && settings.getReportDelayMillis() == 0) {
    567             return true;
    568         }
    569         return false;
    570     }
    571 
    572     private boolean isSettingsAndFilterComboAllowed(ScanSettings settings,
    573             List<ScanFilter> filterList) {
    574         final int callbackType = settings.getCallbackType();
    575         // If onlost/onfound is requested, a non-empty filter is expected
    576         if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH
    577                 | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) {
    578             if (filterList == null) {
    579                 return false;
    580             }
    581             for (ScanFilter filter : filterList) {
    582                 if (filter.isAllFieldsEmpty()) {
    583                     return false;
    584                 }
    585             }
    586         }
    587         return true;
    588     }
    589 
    590     private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) {
    591         final int callbackType = settings.getCallbackType();
    592         if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
    593                 || (callbackType & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
    594             // For onlost/onfound, we required hw support be available
    595             return (mBluetoothAdapter.isOffloadedFilteringSupported()
    596                     && mBluetoothAdapter.isHardwareTrackingFiltersAvailable());
    597         }
    598         return true;
    599     }
    600 }
    601