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; |
Wei Wang | e0d4afb | 2014-07-29 21:34:25 -0700 | [diff] [blame] | 25 | import android.bluetooth.BluetoothGattCallbackWrapper; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 26 | import android.bluetooth.IBluetoothGatt; |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 27 | import android.bluetooth.IBluetoothManager; |
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 | */ |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 270 | private class BleScanCallbackWrapper extends BluetoothGattCallbackWrapper { |
| 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 |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 283 | private int mClientIf; |
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; |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 294 | mClientIf = 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. |
| 301 | if (mClientIf == -1) return; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 302 | try { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 303 | UUID uuid = UUID.randomUUID(); |
| 304 | mBluetoothGatt.registerClient(new ParcelUuid(uuid), this); |
| 305 | wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); |
| 306 | } catch (InterruptedException | RemoteException e) { |
| 307 | Log.e(TAG, "application registeration exception", e); |
| 308 | postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); |
| 309 | } |
| 310 | if (mClientIf > 0) { |
| 311 | mLeScanClients.put(mScanCallback, this); |
| 312 | } else { |
Wei Wang | 02bc008 | 2015-11-09 19:45:53 -0800 | [diff] [blame] | 313 | // Registration timed out or got exception, reset clientIf to -1 so no |
| 314 | // subsequent operations can proceed. |
| 315 | if (mClientIf == 0) mClientIf = -1; |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 316 | postCallbackError(mScanCallback, |
| 317 | ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 318 | } |
| 319 | } |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 320 | } |
| 321 | |
| 322 | public void stopLeScan() { |
| 323 | synchronized (this) { |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 324 | if (mClientIf <= 0) { |
| 325 | Log.e(TAG, "Error state, mLeHandle: " + mClientIf); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 326 | return; |
| 327 | } |
| 328 | try { |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 329 | mBluetoothGatt.stopScan(mClientIf, false); |
| 330 | mBluetoothGatt.unregisterClient(mClientIf); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 331 | } catch (RemoteException e) { |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 332 | Log.e(TAG, "Failed to stop scan and unregister", e); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 333 | } |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 334 | mClientIf = -1; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 335 | } |
| 336 | } |
| 337 | |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 338 | void flushPendingBatchResults() { |
| 339 | synchronized (this) { |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 340 | if (mClientIf <= 0) { |
| 341 | Log.e(TAG, "Error state, mLeHandle: " + mClientIf); |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 342 | return; |
| 343 | } |
| 344 | try { |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 345 | mBluetoothGatt.flushPendingBatchResults(mClientIf, false); |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 346 | } catch (RemoteException e) { |
| 347 | Log.e(TAG, "Failed to get pending scan results", e); |
| 348 | } |
| 349 | } |
| 350 | } |
| 351 | |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 352 | /** |
| 353 | * Application interface registered - app is ready to go |
| 354 | */ |
| 355 | @Override |
| 356 | public void onClientRegistered(int status, int clientIf) { |
| 357 | Log.d(TAG, "onClientRegistered() - status=" + status + |
Wei Wang | 02bc008 | 2015-11-09 19:45:53 -0800 | [diff] [blame] | 358 | " clientIf=" + clientIf + " mClientIf=" + mClientIf); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 359 | synchronized (this) { |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 360 | if (status == BluetoothGatt.GATT_SUCCESS) { |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 361 | try { |
Wei Wang | 02bc008 | 2015-11-09 19:45:53 -0800 | [diff] [blame] | 362 | if (mClientIf == -1) { |
| 363 | // Registration succeeds after timeout, unregister client. |
| 364 | mBluetoothGatt.unregisterClient(clientIf); |
| 365 | } else { |
| 366 | mClientIf = clientIf; |
| 367 | mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters, |
| 368 | mWorkSource, mResultStorages, |
| 369 | ActivityThread.currentOpPackageName()); |
| 370 | } |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 371 | } catch (RemoteException e) { |
| 372 | Log.e(TAG, "fail to start le scan: " + e); |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 373 | mClientIf = -1; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 374 | } |
| 375 | } else { |
| 376 | // registration failed |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 377 | mClientIf = -1; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 378 | } |
| 379 | notifyAll(); |
| 380 | } |
| 381 | } |
| 382 | |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 383 | /** |
| 384 | * Callback reporting an LE scan result. |
| 385 | * |
| 386 | * @hide |
| 387 | */ |
| 388 | @Override |
Wei Wang | e0d4afb | 2014-07-29 21:34:25 -0700 | [diff] [blame] | 389 | public void onScanResult(final ScanResult scanResult) { |
Wei Wang | 020bd7b | 2014-10-16 19:39:52 -0700 | [diff] [blame] | 390 | if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 391 | |
| 392 | // Check null in case the scan has been stopped |
| 393 | synchronized (this) { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 394 | if (mClientIf <= 0) return; |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 395 | } |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 396 | Handler handler = new Handler(Looper.getMainLooper()); |
| 397 | handler.post(new Runnable() { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 398 | @Override |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 399 | public void run() { |
Wei Wang | e0d4afb | 2014-07-29 21:34:25 -0700 | [diff] [blame] | 400 | mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 401 | } |
| 402 | }); |
| 403 | |
| 404 | } |
| 405 | |
| 406 | @Override |
| 407 | public void onBatchScanResults(final List<ScanResult> results) { |
| 408 | Handler handler = new Handler(Looper.getMainLooper()); |
| 409 | handler.post(new Runnable() { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 410 | @Override |
Wei Wang | 9fb1791 | 2014-07-01 15:10:06 -0700 | [diff] [blame] | 411 | public void run() { |
| 412 | mScanCallback.onBatchScanResults(results); |
| 413 | } |
| 414 | }); |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 415 | } |
| 416 | |
| 417 | @Override |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 418 | public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) { |
Wei Wang | 020bd7b | 2014-10-16 19:39:52 -0700 | [diff] [blame] | 419 | if (VDBG) { |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 420 | Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + |
| 421 | " " + scanResult.toString()); |
Prerepa Viswanadham | 8f2e74c | 2014-07-09 12:51:59 -0700 | [diff] [blame] | 422 | } |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 423 | |
| 424 | // Check null in case the scan has been stopped |
| 425 | synchronized (this) { |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 426 | if (mClientIf <= 0) |
| 427 | return; |
Prerepa Viswanadham | 8f2e74c | 2014-07-09 12:51:59 -0700 | [diff] [blame] | 428 | } |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 429 | Handler handler = new Handler(Looper.getMainLooper()); |
| 430 | handler.post(new Runnable() { |
| 431 | @Override |
| 432 | public void run() { |
| 433 | if (onFound) { |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 434 | mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, |
| 435 | scanResult); |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 436 | } else { |
Wei Wang | 0d0df3c | 2014-07-30 15:19:08 -0700 | [diff] [blame] | 437 | mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, |
| 438 | scanResult); |
Prerepa Viswanadham | d5324e4 | 2014-08-07 09:44:20 -0700 | [diff] [blame] | 439 | } |
| 440 | } |
| 441 | }); |
Prerepa Viswanadham | 8f2e74c | 2014-07-09 12:51:59 -0700 | [diff] [blame] | 442 | } |
Prerepa Viswanadham | db1dbb8 | 2015-04-09 17:14:50 -0700 | [diff] [blame] | 443 | |
| 444 | @Override |
| 445 | public void onScanManagerErrorCallback(final int errorCode) { |
| 446 | if (VDBG) { |
| 447 | Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode); |
| 448 | } |
| 449 | synchronized (this) { |
| 450 | if (mClientIf <= 0) |
| 451 | return; |
| 452 | } |
| 453 | postCallbackError(mScanCallback, errorCode); |
| 454 | } |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 455 | } |
| 456 | |
| 457 | private void postCallbackError(final ScanCallback callback, final int errorCode) { |
| 458 | mHandler.post(new Runnable() { |
Wei Wang | b661bb7 | 2014-08-18 16:08:00 -0700 | [diff] [blame] | 459 | @Override |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 460 | public void run() { |
| 461 | callback.onScanFailed(errorCode); |
| 462 | } |
| 463 | }); |
| 464 | } |
Prerepa Viswanadham | 8e5270f | 2014-07-08 16:33:34 -0700 | [diff] [blame] | 465 | |
| 466 | private boolean isSettingsConfigAllowedForScan(ScanSettings settings) { |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 467 | if (mBluetoothAdapter.isOffloadedFilteringSupported()) { |
| 468 | return true; |
Prerepa Viswanadham | 8e5270f | 2014-07-08 16:33:34 -0700 | [diff] [blame] | 469 | } |
Wei Wang | 685c1758 | 2014-07-16 22:02:03 -0700 | [diff] [blame] | 470 | final int callbackType = settings.getCallbackType(); |
| 471 | // Only support regular scan if no offloaded filter support. |
| 472 | if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES |
| 473 | && settings.getReportDelayMillis() == 0) { |
| 474 | return true; |
| 475 | } |
| 476 | return false; |
Prerepa Viswanadham | 8e5270f | 2014-07-08 16:33:34 -0700 | [diff] [blame] | 477 | } |
Prerepa Viswanadham | e593d0a | 2015-04-07 14:36:53 -0700 | [diff] [blame] | 478 | |
| 479 | private boolean isSettingsAndFilterComboAllowed(ScanSettings settings, |
| 480 | List <ScanFilter> filterList) { |
| 481 | final int callbackType = settings.getCallbackType(); |
| 482 | // If onlost/onfound is requested, a non-empty filter is expected |
tturney | ab5267a | 2015-04-13 14:55:07 -0700 | [diff] [blame] | 483 | if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH |
| 484 | | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) { |
Prerepa Viswanadham | e593d0a | 2015-04-07 14:36:53 -0700 | [diff] [blame] | 485 | if (filterList == null) { |
| 486 | return false; |
| 487 | } |
| 488 | for (ScanFilter filter : filterList) { |
| 489 | if (filter.isAllFieldsEmpty()) { |
| 490 | return false; |
| 491 | } |
| 492 | } |
| 493 | } |
| 494 | return true; |
| 495 | } |
| 496 | |
| 497 | private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) { |
| 498 | final int callbackType = settings.getCallbackType(); |
| 499 | if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0 |
| 500 | || (callbackType & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) { |
| 501 | // For onlost/onfound, we required hw support be available |
| 502 | return (mBluetoothAdapter.isOffloadedFilteringSupported() && |
| 503 | mBluetoothAdapter.isHardwareTrackingFiltersAvailable()); |
| 504 | } |
| 505 | return true; |
| 506 | } |
Wei Wang | 6d81118 | 2014-05-22 12:10:25 -0700 | [diff] [blame] | 507 | } |