Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 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 | |
Tor Norbye | 2d49752 | 2015-04-23 17:10:21 -0700 | [diff] [blame] | 19 | import android.Manifest; |
| 20 | import android.annotation.RequiresPermission; |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 21 | import android.annotation.SystemApi; |
Fyodor Kupolov | a179030 | 2015-06-19 15:35:11 -0700 | [diff] [blame] | 22 | import android.app.ActivityThread; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 23 | import android.bluetooth.BluetoothAdapter; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 24 | import android.bluetooth.BluetoothGatt; |
| 25 | import android.bluetooth.IBluetoothGatt; |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 26 | import android.bluetooth.IBluetoothManager; |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 27 | import android.bluetooth.le.IScannerCallback; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 28 | import android.os.Handler; |
| 29 | import android.os.Looper; |
| 30 | import android.os.ParcelUuid; |
| 31 | import android.os.RemoteException; |
Adam Lesinski | 6771d62 | 2016-01-15 18:14:47 -0800 | [diff] [blame] | 32 | import android.os.WorkSource; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 33 | import android.util.Log; |
| 34 | |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 35 | import java.util.ArrayList; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 36 | import java.util.HashMap; |
| 37 | import java.util.List; |
| 38 | import java.util.Map; |
| 39 | import java.util.UUID; |
| 40 | |
| 41 | /** |
| 42 | * This class provides methods to perform scan related operations for Bluetooth LE devices. An |
Scott Kennedy | e7b0363 | 2015-04-10 10:25:34 -0700 | [diff] [blame] | 43 | * application can scan for a particular type of Bluetooth LE devices using {@link ScanFilter}. It |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 44 | * can also request different types of callbacks for delivering the result. |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 45 | * <p> |
| 46 | * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of |
| 47 | * {@link BluetoothLeScanner}. |
| 48 | * <p> |
Wei Wang | af74e66 | 2014-07-09 14:03:42 -0700 | [diff] [blame] | 49 | * <b>Note:</b> Most of the scan methods here require |
| 50 | * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 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; |
Wei Wang | 020bd7b | 2014-10-16 19:39:52 -0700 | [diff] [blame] | 58 | private static final boolean VDBG = false; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 59 | |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 60 | private final IBluetoothManager mBluetoothManager; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 61 | private final Handler mHandler; |
Prerepa Viswanadham | 8e5270f | 2014-07-08 16:33:34 -0700 | [diff] [blame] | 62 | private BluetoothAdapter mBluetoothAdapter; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 63 | private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients; |
| 64 | |
| 65 | /** |
Wei Wang | af74e66 | 2014-07-09 14:03:42 -0700 | [diff] [blame] | 66 | * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 67 | * |
| 68 | * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 69 | * @hide |
| 70 | */ |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 71 | public BluetoothLeScanner(IBluetoothManager bluetoothManager) { |
| 72 | mBluetoothManager = bluetoothManager; |
Prerepa Viswanadham | 8e5270f | 2014-07-08 16:33:34 -0700 | [diff] [blame] | 73 | mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 74 | mHandler = new Handler(Looper.getMainLooper()); |
| 75 | mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>(); |
| 76 | } |
| 77 | |
| 78 | /** |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 79 | * Start Bluetooth LE scan with default parameters and no filters. The scan results will be |
| 80 | * delivered through {@code callback}. |
Wei Wang | af74e66 | 2014-07-09 14:03:42 -0700 | [diff] [blame] | 81 | * <p> |
| 82 | * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. |
Fyodor Kupolov | 7bd8be0 | 2015-07-16 19:40:13 -0700 | [diff] [blame] | 83 | * An app must hold |
| 84 | * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or |
| 85 | * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission |
| 86 | * in order to get results. |
Wei Wang | af74e66 | 2014-07-09 14:03:42 -0700 | [diff] [blame] | 87 | * |
| 88 | * @param callback Callback used to deliver scan results. |
| 89 | * @throws IllegalArgumentException If {@code callback} is null. |
| 90 | */ |
Tor Norbye | 2d49752 | 2015-04-23 17:10:21 -0700 | [diff] [blame] | 91 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) |
Wei Wang | af74e66 | 2014-07-09 14:03:42 -0700 | [diff] [blame] | 92 | public void startScan(final ScanCallback callback) { |
Wei Wang | 833559d | 2014-08-29 10:26:13 -0700 | [diff] [blame] | 93 | startScan(null, new ScanSettings.Builder().build(), callback); |
Wei Wang | af74e66 | 2014-07-09 14:03:42 -0700 | [diff] [blame] | 94 | } |
| 95 | |
| 96 | /** |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 97 | * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}. |
| 98 | * <p> |
| 99 | * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. |
Fyodor Kupolov | 7bd8be0 | 2015-07-16 19:40:13 -0700 | [diff] [blame] | 100 | * An app must hold |
| 101 | * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or |
| 102 | * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission |
| 103 | * in order to get results. |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 104 | * |
| 105 | * @param filters {@link ScanFilter}s for finding exact BLE devices. |
Wei Wang | af74e66 | 2014-07-09 14:03:42 -0700 | [diff] [blame] | 106 | * @param settings Settings for the scan. |
| 107 | * @param callback Callback used to deliver scan results. |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 108 | * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. |
| 109 | */ |
Tor Norbye | 2d49752 | 2015-04-23 17:10:21 -0700 | [diff] [blame] | 110 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 111 | public void startScan(List<ScanFilter> filters, ScanSettings settings, |
| 112 | final ScanCallback callback) { |
Adam Lesinski | 6771d62 | 2016-01-15 18:14:47 -0800 | [diff] [blame] | 113 | startScan(filters, settings, null, callback, null); |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * Start Bluetooth LE scan. Same as {@link #startScan(ScanCallback)} but allows the caller to |
| 118 | * specify on behalf of which application(s) the work is being done. |
| 119 | * |
| 120 | * @param workSource {@link WorkSource} identifying the application(s) for which to blame for |
| 121 | * the scan. |
| 122 | * @param callback Callback used to deliver scan results. |
| 123 | * @hide |
| 124 | */ |
| 125 | @SystemApi |
| 126 | @RequiresPermission(allOf = { |
| 127 | Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS }) |
| 128 | public void startScanFromSource(final WorkSource workSource, final ScanCallback callback) { |
| 129 | startScanFromSource(null, new ScanSettings.Builder().build(), workSource, callback); |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | * Start Bluetooth LE scan. Same as {@link #startScan(List, ScanSettings, ScanCallback)} but |
| 134 | * allows the caller to specify on behalf of which application(s) the work is being done. |
| 135 | * |
| 136 | * @param filters {@link ScanFilter}s for finding exact BLE devices. |
| 137 | * @param settings Settings for the scan. |
| 138 | * @param workSource {@link WorkSource} identifying the application(s) for which to blame for |
| 139 | * the scan. |
| 140 | * @param callback Callback used to deliver scan results. |
| 141 | * @hide |
| 142 | */ |
| 143 | @SystemApi |
| 144 | @RequiresPermission(allOf = { |
| 145 | Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS }) |
| 146 | public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings, |
| 147 | final WorkSource workSource, final ScanCallback callback) { |
| 148 | startScan(filters, settings, workSource, callback, null); |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 149 | } |
| 150 | |
| 151 | private void startScan(List<ScanFilter> filters, ScanSettings settings, |
Adam Lesinski | 6771d62 | 2016-01-15 18:14:47 -0800 | [diff] [blame] | 152 | final WorkSource workSource, final ScanCallback callback, |
| 153 | List<List<ResultStorageDescriptor>> resultStorages) { |
Wei Wang | 833559d | 2014-08-29 10:26:13 -0700 | [diff] [blame] | 154 | BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); |
Adam Lesinski | 6771d62 | 2016-01-15 18:14:47 -0800 | [diff] [blame] | 155 | if (callback == null) { |
| 156 | throw new IllegalArgumentException("callback is null"); |
| 157 | } |
| 158 | if (settings == null) { |
| 159 | throw new IllegalArgumentException("settings is null"); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 160 | } |
| 161 | synchronized (mLeScanClients) { |
| 162 | if (mLeScanClients.containsKey(callback)) { |
| 163 | postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED); |
| 164 | return; |
| 165 | } |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 166 | IBluetoothGatt gatt; |
| 167 | try { |
| 168 | gatt = mBluetoothManager.getBluetoothGatt(); |
| 169 | } catch (RemoteException e) { |
| 170 | gatt = null; |
| 171 | } |
| 172 | if (gatt == null) { |
Wei Wang | af74e66 | 2014-07-09 14:03:42 -0700 | [diff] [blame] | 173 | postCallbackError(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 174 | return; |
| 175 | } |
Prerepa Viswanadham | 8e5270f | 2014-07-08 16:33:34 -0700 | [diff] [blame] | 176 | if (!isSettingsConfigAllowedForScan(settings)) { |
| 177 | postCallbackError(callback, |
| 178 | ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); |
| 179 | return; |
| 180 | } |
Prerepa Viswanadham | e593d0a | 2015-04-07 14:36:53 -0700 | [diff] [blame] | 181 | if (!isHardwareResourcesAvailableForScan(settings)) { |
| 182 | postCallbackError(callback, |
| 183 | ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES); |
| 184 | return; |
| 185 | } |
| 186 | if (!isSettingsAndFilterComboAllowed(settings, filters)) { |
| 187 | postCallbackError(callback, |
| 188 | ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); |
| 189 | return; |
| 190 | } |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 191 | BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, |
Adam Lesinski | 6771d62 | 2016-01-15 18:14:47 -0800 | [diff] [blame] | 192 | settings, workSource, callback, resultStorages); |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 193 | wrapper.startRegisteration(); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 194 | } |
| 195 | } |
| 196 | |
| 197 | /** |
| 198 | * Stops an ongoing Bluetooth LE scan. |
| 199 | * <p> |
| 200 | * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. |
| 201 | * |
| 202 | * @param callback |
| 203 | */ |
Tor Norbye | 2d49752 | 2015-04-23 17:10:21 -0700 | [diff] [blame] | 204 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 205 | public void stopScan(ScanCallback callback) { |
Wei Wang | 833559d | 2014-08-29 10:26:13 -0700 | [diff] [blame] | 206 | BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 207 | synchronized (mLeScanClients) { |
| 208 | BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback); |
| 209 | if (wrapper == null) { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 210 | if (DBG) Log.d(TAG, "could not find callback wrapper"); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 211 | return; |
| 212 | } |
| 213 | wrapper.stopLeScan(); |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | /** |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 218 | * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth |
| 219 | * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data |
| 220 | * will be delivered through the {@code callback}. |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 221 | * |
| 222 | * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one |
| 223 | * used to start scan. |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 224 | */ |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 225 | public void flushPendingScanResults(ScanCallback callback) { |
Wei Wang | 833559d | 2014-08-29 10:26:13 -0700 | [diff] [blame] | 226 | BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 227 | if (callback == null) { |
| 228 | throw new IllegalArgumentException("callback cannot be null!"); |
| 229 | } |
| 230 | synchronized (mLeScanClients) { |
| 231 | BleScanCallbackWrapper wrapper = mLeScanClients.get(callback); |
| 232 | if (wrapper == null) { |
| 233 | return; |
| 234 | } |
| 235 | wrapper.flushPendingBatchResults(); |
| 236 | } |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 237 | } |
| 238 | |
| 239 | /** |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 240 | * Start truncated scan. |
| 241 | * |
| 242 | * @hide |
| 243 | */ |
| 244 | @SystemApi |
| 245 | public void startTruncatedScan(List<TruncatedFilter> truncatedFilters, ScanSettings settings, |
| 246 | final ScanCallback callback) { |
| 247 | int filterSize = truncatedFilters.size(); |
| 248 | List<ScanFilter> scanFilters = new ArrayList<ScanFilter>(filterSize); |
| 249 | List<List<ResultStorageDescriptor>> scanStorages = |
| 250 | new ArrayList<List<ResultStorageDescriptor>>(filterSize); |
| 251 | for (TruncatedFilter filter : truncatedFilters) { |
| 252 | scanFilters.add(filter.getFilter()); |
| 253 | scanStorages.add(filter.getStorageDescriptors()); |
| 254 | } |
Adam Lesinski | 6771d62 | 2016-01-15 18:14:47 -0800 | [diff] [blame] | 255 | startScan(scanFilters, settings, null, callback, scanStorages); |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 256 | } |
| 257 | |
| 258 | /** |
Wei Wang | ee80922 | 2014-08-12 22:16:32 -0700 | [diff] [blame] | 259 | * Cleans up scan clients. Should be called when bluetooth is down. |
| 260 | * |
| 261 | * @hide |
| 262 | */ |
| 263 | public void cleanup() { |
| 264 | mLeScanClients.clear(); |
| 265 | } |
| 266 | |
| 267 | /** |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 268 | * Bluetooth GATT interface callbacks |
| 269 | */ |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 270 | private class BleScanCallbackWrapper extends IScannerCallback.Stub { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 271 | private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 272 | |
| 273 | private final ScanCallback mScanCallback; |
| 274 | private final List<ScanFilter> mFilters; |
Adam Lesinski | 6771d62 | 2016-01-15 18:14:47 -0800 | [diff] [blame] | 275 | private final WorkSource mWorkSource; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 276 | private ScanSettings mSettings; |
| 277 | private IBluetoothGatt mBluetoothGatt; |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 278 | private List<List<ResultStorageDescriptor>> mResultStorages; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 279 | |
| 280 | // mLeHandle 0: not registered |
Wei Wang | 02bc008 | 2015-11-09 19:45:53 -0800 | [diff] [blame] | 281 | // -1: scan stopped or registration failed |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 282 | // > 0: registered and scan started |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 283 | private int mScannerId; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 284 | |
| 285 | public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, |
| 286 | List<ScanFilter> filters, ScanSettings settings, |
Adam Lesinski | 6771d62 | 2016-01-15 18:14:47 -0800 | [diff] [blame] | 287 | WorkSource workSource, ScanCallback scanCallback, |
| 288 | List<List<ResultStorageDescriptor>> resultStorages) { |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 289 | mBluetoothGatt = bluetoothGatt; |
| 290 | mFilters = filters; |
| 291 | mSettings = settings; |
Adam Lesinski | 6771d62 | 2016-01-15 18:14:47 -0800 | [diff] [blame] | 292 | mWorkSource = workSource; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 293 | mScanCallback = scanCallback; |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 294 | mScannerId = 0; |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 295 | mResultStorages = resultStorages; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 296 | } |
| 297 | |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 298 | public void startRegisteration() { |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 299 | synchronized (this) { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 300 | // Scan stopped. |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 301 | if (mScannerId == -1) return; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 302 | try { |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 303 | mBluetoothGatt.registerScanner(this); |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 304 | wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); |
| 305 | } catch (InterruptedException | RemoteException e) { |
| 306 | Log.e(TAG, "application registeration exception", e); |
| 307 | postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); |
| 308 | } |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 309 | if (mScannerId > 0) { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 310 | mLeScanClients.put(mScanCallback, this); |
| 311 | } else { |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 312 | // Registration timed out or got exception, reset scannerId to -1 so no |
Wei Wang | 02bc008 | 2015-11-09 19:45:53 -0800 | [diff] [blame] | 313 | // subsequent operations can proceed. |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 314 | if (mScannerId == 0) mScannerId = -1; |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 315 | postCallbackError(mScanCallback, |
| 316 | ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 317 | } |
| 318 | } |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 319 | } |
| 320 | |
| 321 | public void stopLeScan() { |
| 322 | synchronized (this) { |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 323 | if (mScannerId <= 0) { |
| 324 | Log.e(TAG, "Error state, mLeHandle: " + mScannerId); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 325 | return; |
| 326 | } |
| 327 | try { |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 328 | mBluetoothGatt.stopScan(mScannerId); |
| 329 | mBluetoothGatt.unregisterScanner(mScannerId); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 330 | } catch (RemoteException e) { |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 331 | Log.e(TAG, "Failed to stop scan and unregister", e); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 332 | } |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 333 | mScannerId = -1; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 334 | } |
| 335 | } |
| 336 | |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 337 | void flushPendingBatchResults() { |
| 338 | synchronized (this) { |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 339 | if (mScannerId <= 0) { |
| 340 | Log.e(TAG, "Error state, mLeHandle: " + mScannerId); |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 341 | return; |
| 342 | } |
| 343 | try { |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 344 | mBluetoothGatt.flushPendingBatchResults(mScannerId); |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 345 | } catch (RemoteException e) { |
| 346 | Log.e(TAG, "Failed to get pending scan results", e); |
| 347 | } |
| 348 | } |
| 349 | } |
| 350 | |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 351 | /** |
| 352 | * Application interface registered - app is ready to go |
| 353 | */ |
| 354 | @Override |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 355 | public void onScannerRegistered(int status, int scannerId) { |
| 356 | Log.d(TAG, "onScannerRegistered() - status=" + status + |
| 357 | " scannerId=" + scannerId + " mScannerId=" + mScannerId); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 358 | synchronized (this) { |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 359 | if (status == BluetoothGatt.GATT_SUCCESS) { |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 360 | try { |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 361 | if (mScannerId == -1) { |
Wei Wang | 02bc008 | 2015-11-09 19:45:53 -0800 | [diff] [blame] | 362 | // Registration succeeds after timeout, unregister client. |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 363 | mBluetoothGatt.unregisterClient(scannerId); |
Wei Wang | 02bc008 | 2015-11-09 19:45:53 -0800 | [diff] [blame] | 364 | } else { |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 365 | mScannerId = scannerId; |
| 366 | mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, |
Wei Wang | 02bc008 | 2015-11-09 19:45:53 -0800 | [diff] [blame] | 367 | mWorkSource, mResultStorages, |
| 368 | ActivityThread.currentOpPackageName()); |
| 369 | } |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 370 | } catch (RemoteException e) { |
| 371 | Log.e(TAG, "fail to start le scan: " + e); |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 372 | mScannerId = -1; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 373 | } |
| 374 | } else { |
| 375 | // registration failed |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 376 | mScannerId = -1; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 377 | } |
| 378 | notifyAll(); |
| 379 | } |
| 380 | } |
| 381 | |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 382 | /** |
| 383 | * Callback reporting an LE scan result. |
| 384 | * |
| 385 | * @hide |
| 386 | */ |
| 387 | @Override |
Wei Wang | e0d4afb | 2014-07-29 21:34:25 -0700 | [diff] [blame] | 388 | public void onScanResult(final ScanResult scanResult) { |
Wei Wang | 020bd7b | 2014-10-16 19:39:52 -0700 | [diff] [blame] | 389 | if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 390 | |
| 391 | // Check null in case the scan has been stopped |
| 392 | synchronized (this) { |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 393 | if (mScannerId <= 0) return; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 394 | } |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 395 | Handler handler = new Handler(Looper.getMainLooper()); |
| 396 | handler.post(new Runnable() { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 397 | @Override |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 398 | public void run() { |
Wei Wang | e0d4afb | 2014-07-29 21:34:25 -0700 | [diff] [blame] | 399 | mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 400 | } |
| 401 | }); |
| 402 | |
| 403 | } |
| 404 | |
| 405 | @Override |
| 406 | public void onBatchScanResults(final List<ScanResult> results) { |
| 407 | Handler handler = new Handler(Looper.getMainLooper()); |
| 408 | handler.post(new Runnable() { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 409 | @Override |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 410 | public void run() { |
| 411 | mScanCallback.onBatchScanResults(results); |
| 412 | } |
| 413 | }); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 414 | } |
| 415 | |
| 416 | @Override |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 417 | public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) { |
Wei Wang | 020bd7b | 2014-10-16 19:39:52 -0700 | [diff] [blame] | 418 | if (VDBG) { |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 419 | Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + |
| 420 | " " + scanResult.toString()); |
Prerepa Viswanadham | 8f2e74c | 2014-07-09 12:51:59 -0700 | [diff] [blame] | 421 | } |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 422 | |
| 423 | // Check null in case the scan has been stopped |
| 424 | synchronized (this) { |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 425 | if (mScannerId <= 0) |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 426 | return; |
Prerepa Viswanadham | 8f2e74c | 2014-07-09 12:51:59 -0700 | [diff] [blame] | 427 | } |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 428 | Handler handler = new Handler(Looper.getMainLooper()); |
| 429 | handler.post(new Runnable() { |
| 430 | @Override |
| 431 | public void run() { |
| 432 | if (onFound) { |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 433 | mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, |
| 434 | scanResult); |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 435 | } else { |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 436 | mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, |
| 437 | scanResult); |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 438 | } |
| 439 | } |
| 440 | }); |
Prerepa Viswanadham | 8f2e74c | 2014-07-09 12:51:59 -0700 | [diff] [blame] | 441 | } |
Prerepa Viswanadham | db1dbb8 | 2015-04-09 17:14:50 -0700 | [diff] [blame] | 442 | |
| 443 | @Override |
| 444 | public void onScanManagerErrorCallback(final int errorCode) { |
| 445 | if (VDBG) { |
| 446 | Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode); |
| 447 | } |
| 448 | synchronized (this) { |
Jakub Pawlowski | 1b49e6e | 2016-10-26 13:05:30 -0700 | [diff] [blame] | 449 | if (mScannerId <= 0) |
Prerepa Viswanadham | db1dbb8 | 2015-04-09 17:14:50 -0700 | [diff] [blame] | 450 | return; |
| 451 | } |
| 452 | postCallbackError(mScanCallback, errorCode); |
| 453 | } |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 454 | } |
| 455 | |
| 456 | private void postCallbackError(final ScanCallback callback, final int errorCode) { |
| 457 | mHandler.post(new Runnable() { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 458 | @Override |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 459 | public void run() { |
| 460 | callback.onScanFailed(errorCode); |
| 461 | } |
| 462 | }); |
| 463 | } |
Prerepa Viswanadham | 8e5270f | 2014-07-08 16:33:34 -0700 | [diff] [blame] | 464 | |
| 465 | private boolean isSettingsConfigAllowedForScan(ScanSettings settings) { |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 466 | if (mBluetoothAdapter.isOffloadedFilteringSupported()) { |
| 467 | return true; |
Prerepa Viswanadham | 8e5270f | 2014-07-08 16:33:34 -0700 | [diff] [blame] | 468 | } |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 469 | final int callbackType = settings.getCallbackType(); |
| 470 | // Only support regular scan if no offloaded filter support. |
| 471 | if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES |
| 472 | && settings.getReportDelayMillis() == 0) { |
| 473 | return true; |
| 474 | } |
| 475 | return false; |
Prerepa Viswanadham | 8e5270f | 2014-07-08 16:33:34 -0700 | [diff] [blame] | 476 | } |
Prerepa Viswanadham | e593d0a | 2015-04-07 14:36:53 -0700 | [diff] [blame] | 477 | |
| 478 | private boolean isSettingsAndFilterComboAllowed(ScanSettings settings, |
| 479 | List <ScanFilter> filterList) { |
| 480 | final int callbackType = settings.getCallbackType(); |
| 481 | // If onlost/onfound is requested, a non-empty filter is expected |
tturney | ab5267a | 2015-04-13 14:55:07 -0700 | [diff] [blame] | 482 | if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH |
| 483 | | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) { |
Prerepa Viswanadham | e593d0a | 2015-04-07 14:36:53 -0700 | [diff] [blame] | 484 | if (filterList == null) { |
| 485 | return false; |
| 486 | } |
| 487 | for (ScanFilter filter : filterList) { |
| 488 | if (filter.isAllFieldsEmpty()) { |
| 489 | return false; |
| 490 | } |
| 491 | } |
| 492 | } |
| 493 | return true; |
| 494 | } |
| 495 | |
| 496 | private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) { |
| 497 | final int callbackType = settings.getCallbackType(); |
| 498 | if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0 |
| 499 | || (callbackType & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) { |
| 500 | // For onlost/onfound, we required hw support be available |
| 501 | return (mBluetoothAdapter.isOffloadedFilteringSupported() && |
| 502 | mBluetoothAdapter.isHardwareTrackingFiltersAvailable()); |
| 503 | } |
| 504 | return true; |
| 505 | } |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 506 | } |