blob: 347fc4df4ebbe8e27ceac70d01b539c3b97ded99 [file] [log] [blame]
Wei Wang6d811182014-05-22 12:10:25 -07001/*
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
17package android.bluetooth.le;
18
Tor Norbye2d497522015-04-23 17:10:21 -070019import android.Manifest;
Amith Yamasani461111b2017-04-13 17:46:53 -070020import android.annotation.NonNull;
21import android.annotation.Nullable;
Tor Norbye2d497522015-04-23 17:10:21 -070022import android.annotation.RequiresPermission;
Wei Wang0d0df3c2014-07-30 15:19:08 -070023import android.annotation.SystemApi;
Fyodor Kupolova1790302015-06-19 15:35:11 -070024import android.app.ActivityThread;
Amith Yamasani461111b2017-04-13 17:46:53 -070025import android.app.PendingIntent;
Wei Wang6d811182014-05-22 12:10:25 -070026import android.bluetooth.BluetoothAdapter;
Wei Wang6d811182014-05-22 12:10:25 -070027import android.bluetooth.BluetoothGatt;
28import android.bluetooth.IBluetoothGatt;
Wei Wang9fb17912014-07-01 15:10:06 -070029import android.bluetooth.IBluetoothManager;
Wei Wang6d811182014-05-22 12:10:25 -070030import android.os.Handler;
31import android.os.Looper;
Wei Wang6d811182014-05-22 12:10:25 -070032import android.os.RemoteException;
Adam Lesinski6771d622016-01-15 18:14:47 -080033import android.os.WorkSource;
Wei Wang6d811182014-05-22 12:10:25 -070034import android.util.Log;
35
Wei Wang0d0df3c2014-07-30 15:19:08 -070036import java.util.ArrayList;
Wei Wang6d811182014-05-22 12:10:25 -070037import java.util.HashMap;
38import java.util.List;
39import java.util.Map;
Wei Wang6d811182014-05-22 12:10:25 -070040
41/**
42 * This class provides methods to perform scan related operations for Bluetooth LE devices. An
Scott Kennedye7b03632015-04-10 10:25:34 -070043 * application can scan for a particular type of Bluetooth LE devices using {@link ScanFilter}. It
Wei Wang685c17582014-07-16 22:02:03 -070044 * can also request different types of callbacks for delivering the result.
Wei Wang6d811182014-05-22 12:10:25 -070045 * <p>
46 * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
47 * {@link BluetoothLeScanner}.
48 * <p>
Wei Wangaf74e662014-07-09 14:03:42 -070049 * <b>Note:</b> Most of the scan methods here require
50 * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
Wei Wang6d811182014-05-22 12:10:25 -070051 *
52 * @see ScanFilter
53 */
54public final class BluetoothLeScanner {
55
56 private static final String TAG = "BluetoothLeScanner";
57 private static final boolean DBG = true;
Wei Wang020bd7b2014-10-16 19:39:52 -070058 private static final boolean VDBG = false;
Wei Wang6d811182014-05-22 12:10:25 -070059
Amith Yamasani461111b2017-04-13 17:46:53 -070060 /**
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 */
Jack He2992cd02017-08-22 21:21:23 -070065 public static final String EXTRA_LIST_SCAN_RESULT =
66 "android.bluetooth.le.extra.LIST_SCAN_RESULT";
Amith Yamasani461111b2017-04-13 17:46:53 -070067
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
Amith Yamasaniad8f0862017-04-24 11:00:05 -070076 * CALLBACK_TYPE_* constants in {@link ScanSettings}.
Jack Hea355e5e2017-08-22 16:06:54 -070077 *
Amith Yamasani461111b2017-04-13 17:46:53 -070078 * @see ScanCallback#onScanResult(int, ScanResult)
79 */
80 public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
81
Wei Wang9fb17912014-07-01 15:10:06 -070082 private final IBluetoothManager mBluetoothManager;
Wei Wang6d811182014-05-22 12:10:25 -070083 private final Handler mHandler;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -070084 private BluetoothAdapter mBluetoothAdapter;
Wei Wang6d811182014-05-22 12:10:25 -070085 private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
86
87 /**
Wei Wangaf74e662014-07-09 14:03:42 -070088 * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
Wei Wang685c17582014-07-16 22:02:03 -070089 *
90 * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management.
Wei Wang6d811182014-05-22 12:10:25 -070091 * @hide
92 */
Wei Wang9fb17912014-07-01 15:10:06 -070093 public BluetoothLeScanner(IBluetoothManager bluetoothManager) {
94 mBluetoothManager = bluetoothManager;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -070095 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Wei Wang6d811182014-05-22 12:10:25 -070096 mHandler = new Handler(Looper.getMainLooper());
97 mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>();
98 }
99
100 /**
Wei Wang685c17582014-07-16 22:02:03 -0700101 * Start Bluetooth LE scan with default parameters and no filters. The scan results will be
Vinay Kalia875d27e2017-09-01 12:18:10 -0700102 * 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}.
Wei Wangaf74e662014-07-09 14:03:42 -0700105 * <p>
Fyodor Kupolov7bd8be02015-07-16 19:40:13 -0700106 * 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.
Wei Wangaf74e662014-07-09 14:03:42 -0700110 *
111 * @param callback Callback used to deliver scan results.
112 * @throws IllegalArgumentException If {@code callback} is null.
113 */
Tor Norbye2d497522015-04-23 17:10:21 -0700114 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Wei Wangaf74e662014-07-09 14:03:42 -0700115 public void startScan(final ScanCallback callback) {
Wei Wang833559d2014-08-29 10:26:13 -0700116 startScan(null, new ScanSettings.Builder().build(), callback);
Wei Wangaf74e662014-07-09 14:03:42 -0700117 }
118
119 /**
Wei Wang6d811182014-05-22 12:10:25 -0700120 * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
Vinay Kalia875d27e2017-09-01 12:18:10 -0700121 * 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}.
Wei Wang6d811182014-05-22 12:10:25 -0700124 * <p>
Fyodor Kupolov7bd8be02015-07-16 19:40:13 -0700125 * 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.
Wei Wang6d811182014-05-22 12:10:25 -0700129 *
130 * @param filters {@link ScanFilter}s for finding exact BLE devices.
Wei Wangaf74e662014-07-09 14:03:42 -0700131 * @param settings Settings for the scan.
132 * @param callback Callback used to deliver scan results.
Wei Wang6d811182014-05-22 12:10:25 -0700133 * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
134 */
Tor Norbye2d497522015-04-23 17:10:21 -0700135 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Wei Wang6d811182014-05-22 12:10:25 -0700136 public void startScan(List<ScanFilter> filters, ScanSettings settings,
137 final ScanCallback callback) {
Amith Yamasani461111b2017-04-13 17:46:53 -0700138 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.
Amith Yamasani96221372017-06-21 14:06:44 -0700145 * <p>
Amith Yamasani172dd5c2017-09-05 13:41:19 -0700146 * 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>
Amith Yamasani96221372017-06-21 14:06:44 -0700151 * 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.
Amith Yamasani461111b2017-04-13 17:46:53 -0700155 *
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);
Adam Lesinski6771d622016-01-15 18:14:47 -0800169 }
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
Jack Hea355e5e2017-08-22 16:06:54 -0700176 * the scan.
Adam Lesinski6771d622016-01-15 18:14:47 -0800177 * @param callback Callback used to deliver scan results.
178 * @hide
179 */
180 @SystemApi
181 @RequiresPermission(allOf = {
Jack Hea355e5e2017-08-22 16:06:54 -0700182 Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS})
Adam Lesinski6771d622016-01-15 18:14:47 -0800183 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
Jack Hea355e5e2017-08-22 16:06:54 -0700194 * the scan.
Adam Lesinski6771d622016-01-15 18:14:47 -0800195 * @param callback Callback used to deliver scan results.
196 * @hide
197 */
198 @SystemApi
199 @RequiresPermission(allOf = {
Jack Hea355e5e2017-08-22 16:06:54 -0700200 Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS})
Adam Lesinski6771d622016-01-15 18:14:47 -0800201 public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings,
Jack Hea355e5e2017-08-22 16:06:54 -0700202 final WorkSource workSource, final ScanCallback callback) {
Amith Yamasani461111b2017-04-13 17:46:53 -0700203 startScan(filters, settings, workSource, callback, null, null);
Wei Wang0d0df3c2014-07-30 15:19:08 -0700204 }
205
Amith Yamasani461111b2017-04-13 17:46:53 -0700206 private int startScan(List<ScanFilter> filters, ScanSettings settings,
Jack Hea355e5e2017-08-22 16:06:54 -0700207 final WorkSource workSource, final ScanCallback callback,
208 final PendingIntent callbackIntent,
209 List<List<ResultStorageDescriptor>> resultStorages) {
Wei Wang833559d2014-08-29 10:26:13 -0700210 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
Amith Yamasani461111b2017-04-13 17:46:53 -0700211 if (callback == null && callbackIntent == null) {
Adam Lesinski6771d622016-01-15 18:14:47 -0800212 throw new IllegalArgumentException("callback is null");
213 }
214 if (settings == null) {
215 throw new IllegalArgumentException("settings is null");
Wei Wang6d811182014-05-22 12:10:25 -0700216 }
217 synchronized (mLeScanClients) {
Amith Yamasani461111b2017-04-13 17:46:53 -0700218 if (callback != null && mLeScanClients.containsKey(callback)) {
Vinay Kalia97229712017-07-28 15:09:57 -0700219 return postCallbackErrorOrReturn(callback,
220 ScanCallback.SCAN_FAILED_ALREADY_STARTED);
Wei Wang6d811182014-05-22 12:10:25 -0700221 }
Wei Wang9fb17912014-07-01 15:10:06 -0700222 IBluetoothGatt gatt;
223 try {
224 gatt = mBluetoothManager.getBluetoothGatt();
225 } catch (RemoteException e) {
226 gatt = null;
227 }
228 if (gatt == null) {
Amith Yamasani461111b2017-04-13 17:46:53 -0700229 return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
Wei Wang9fb17912014-07-01 15:10:06 -0700230 }
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700231 if (!isSettingsConfigAllowedForScan(settings)) {
Amith Yamasani461111b2017-04-13 17:46:53 -0700232 return postCallbackErrorOrReturn(callback,
Jack Hea355e5e2017-08-22 16:06:54 -0700233 ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700234 }
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700235 if (!isHardwareResourcesAvailableForScan(settings)) {
Amith Yamasani461111b2017-04-13 17:46:53 -0700236 return postCallbackErrorOrReturn(callback,
Jack Hea355e5e2017-08-22 16:06:54 -0700237 ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES);
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700238 }
239 if (!isSettingsAndFilterComboAllowed(settings, filters)) {
Amith Yamasani461111b2017-04-13 17:46:53 -0700240 return postCallbackErrorOrReturn(callback,
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700241 ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700242 }
Amith Yamasani461111b2017-04-13 17:46:53 -0700243 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 }
Wei Wang6d811182014-05-22 12:10:25 -0700255 }
Amith Yamasani461111b2017-04-13 17:46:53 -0700256 return ScanCallback.NO_ERROR;
Wei Wang6d811182014-05-22 12:10:25 -0700257 }
258
259 /**
260 * Stops an ongoing Bluetooth LE scan.
Wei Wang6d811182014-05-22 12:10:25 -0700261 *
262 * @param callback
263 */
Tor Norbye2d497522015-04-23 17:10:21 -0700264 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Wei Wang6d811182014-05-22 12:10:25 -0700265 public void stopScan(ScanCallback callback) {
Wei Wang833559d2014-08-29 10:26:13 -0700266 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
Wei Wang6d811182014-05-22 12:10:25 -0700267 synchronized (mLeScanClients) {
268 BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback);
269 if (wrapper == null) {
Wei Wangb661bb72014-08-18 16:08:00 -0700270 if (DBG) Log.d(TAG, "could not find callback wrapper");
Wei Wang6d811182014-05-22 12:10:25 -0700271 return;
272 }
273 wrapper.stopLeScan();
274 }
275 }
276
277 /**
Amith Yamasani461111b2017-04-13 17:46:53 -0700278 * Stops an ongoing Bluetooth LE scan started using a PendingIntent.
Amith Yamasani461111b2017-04-13 17:46:53 -0700279 *
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 /**
Wei Wang9fb17912014-07-01 15:10:06 -0700295 * 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}.
Wei Wang6d811182014-05-22 12:10:25 -0700298 *
299 * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
Jack Hea355e5e2017-08-22 16:06:54 -0700300 * used to start scan.
Wei Wang6d811182014-05-22 12:10:25 -0700301 */
Wei Wang9fb17912014-07-01 15:10:06 -0700302 public void flushPendingScanResults(ScanCallback callback) {
Wei Wang833559d2014-08-29 10:26:13 -0700303 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
Wei Wang9fb17912014-07-01 15:10:06 -0700304 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 }
Wei Wang6d811182014-05-22 12:10:25 -0700314 }
315
316 /**
Wei Wang0d0df3c2014-07-30 15:19:08 -0700317 * 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 }
Amith Yamasani461111b2017-04-13 17:46:53 -0700332 startScan(scanFilters, settings, null, callback, null, scanStorages);
Wei Wang0d0df3c2014-07-30 15:19:08 -0700333 }
334
335 /**
Wei Wangee809222014-08-12 22:16:32 -0700336 * 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 /**
Wei Wang6d811182014-05-22 12:10:25 -0700345 * Bluetooth GATT interface callbacks
346 */
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700347 private class BleScanCallbackWrapper extends IScannerCallback.Stub {
Wei Wangb661bb72014-08-18 16:08:00 -0700348 private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000;
Wei Wang6d811182014-05-22 12:10:25 -0700349
350 private final ScanCallback mScanCallback;
351 private final List<ScanFilter> mFilters;
Adam Lesinski6771d622016-01-15 18:14:47 -0800352 private final WorkSource mWorkSource;
Wei Wang6d811182014-05-22 12:10:25 -0700353 private ScanSettings mSettings;
354 private IBluetoothGatt mBluetoothGatt;
Wei Wang0d0df3c2014-07-30 15:19:08 -0700355 private List<List<ResultStorageDescriptor>> mResultStorages;
Wei Wang6d811182014-05-22 12:10:25 -0700356
357 // mLeHandle 0: not registered
Jakub Pawlowskiee02e1c2017-08-28 04:12:49 -0700358 // -2: registration failed because app is scanning to frequently
Wei Wang02bc0082015-11-09 19:45:53 -0800359 // -1: scan stopped or registration failed
Wei Wang6d811182014-05-22 12:10:25 -0700360 // > 0: registered and scan started
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700361 private int mScannerId;
Wei Wang6d811182014-05-22 12:10:25 -0700362
363 public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
364 List<ScanFilter> filters, ScanSettings settings,
Adam Lesinski6771d622016-01-15 18:14:47 -0800365 WorkSource workSource, ScanCallback scanCallback,
366 List<List<ResultStorageDescriptor>> resultStorages) {
Wei Wang6d811182014-05-22 12:10:25 -0700367 mBluetoothGatt = bluetoothGatt;
368 mFilters = filters;
369 mSettings = settings;
Adam Lesinski6771d622016-01-15 18:14:47 -0800370 mWorkSource = workSource;
Wei Wang6d811182014-05-22 12:10:25 -0700371 mScanCallback = scanCallback;
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700372 mScannerId = 0;
Wei Wang0d0df3c2014-07-30 15:19:08 -0700373 mResultStorages = resultStorages;
Wei Wang6d811182014-05-22 12:10:25 -0700374 }
375
Amith Yamasani461111b2017-04-13 17:46:53 -0700376 public void startRegistration() {
Wei Wang6d811182014-05-22 12:10:25 -0700377 synchronized (this) {
Wei Wangb661bb72014-08-18 16:08:00 -0700378 // Scan stopped.
Jakub Pawlowskiee02e1c2017-08-28 04:12:49 -0700379 if (mScannerId == -1 || mScannerId == -2) return;
Wei Wang6d811182014-05-22 12:10:25 -0700380 try {
Ajay Panickera71643e2017-05-02 16:28:03 -0700381 mBluetoothGatt.registerScanner(this, mWorkSource);
Wei Wangb661bb72014-08-18 16:08:00 -0700382 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 }
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700387 if (mScannerId > 0) {
Wei Wangb661bb72014-08-18 16:08:00 -0700388 mLeScanClients.put(mScanCallback, this);
389 } else {
Narayan Kamath728c8a02017-12-28 15:48:07 +0000390 // Registration timed out or got exception, reset RscannerId to -1 so no
Wei Wang02bc0082015-11-09 19:45:53 -0800391 // subsequent operations can proceed.
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700392 if (mScannerId == 0) mScannerId = -1;
Jakub Pawlowskiee02e1c2017-08-28 04:12:49 -0700393
394 // If scanning too frequently, don't report anything to the app.
395 if (mScannerId == -2) return;
396
Wei Wangb661bb72014-08-18 16:08:00 -0700397 postCallbackError(mScanCallback,
398 ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
Wei Wang6d811182014-05-22 12:10:25 -0700399 }
400 }
Wei Wang6d811182014-05-22 12:10:25 -0700401 }
402
403 public void stopLeScan() {
404 synchronized (this) {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700405 if (mScannerId <= 0) {
406 Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
Wei Wang6d811182014-05-22 12:10:25 -0700407 return;
408 }
409 try {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700410 mBluetoothGatt.stopScan(mScannerId);
411 mBluetoothGatt.unregisterScanner(mScannerId);
Wei Wang6d811182014-05-22 12:10:25 -0700412 } catch (RemoteException e) {
Wei Wang9fb17912014-07-01 15:10:06 -0700413 Log.e(TAG, "Failed to stop scan and unregister", e);
Wei Wang6d811182014-05-22 12:10:25 -0700414 }
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700415 mScannerId = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700416 }
417 }
418
Wei Wang9fb17912014-07-01 15:10:06 -0700419 void flushPendingBatchResults() {
420 synchronized (this) {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700421 if (mScannerId <= 0) {
422 Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
Wei Wang9fb17912014-07-01 15:10:06 -0700423 return;
424 }
425 try {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700426 mBluetoothGatt.flushPendingBatchResults(mScannerId);
Wei Wang9fb17912014-07-01 15:10:06 -0700427 } catch (RemoteException e) {
428 Log.e(TAG, "Failed to get pending scan results", e);
429 }
430 }
431 }
432
Wei Wang6d811182014-05-22 12:10:25 -0700433 /**
434 * Application interface registered - app is ready to go
435 */
436 @Override
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700437 public void onScannerRegistered(int status, int scannerId) {
Jack He2992cd02017-08-22 21:21:23 -0700438 Log.d(TAG, "onScannerRegistered() - status=" + status
439 + " scannerId=" + scannerId + " mScannerId=" + mScannerId);
Wei Wang6d811182014-05-22 12:10:25 -0700440 synchronized (this) {
Wei Wang6d811182014-05-22 12:10:25 -0700441 if (status == BluetoothGatt.GATT_SUCCESS) {
Wei Wang6d811182014-05-22 12:10:25 -0700442 try {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700443 if (mScannerId == -1) {
Wei Wang02bc0082015-11-09 19:45:53 -0800444 // Registration succeeds after timeout, unregister client.
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700445 mBluetoothGatt.unregisterClient(scannerId);
Wei Wang02bc0082015-11-09 19:45:53 -0800446 } else {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700447 mScannerId = scannerId;
448 mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
Ajay Panickera71643e2017-05-02 16:28:03 -0700449 mResultStorages,
Wei Wang02bc0082015-11-09 19:45:53 -0800450 ActivityThread.currentOpPackageName());
451 }
Wei Wang6d811182014-05-22 12:10:25 -0700452 } catch (RemoteException e) {
453 Log.e(TAG, "fail to start le scan: " + e);
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700454 mScannerId = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700455 }
Jakub Pawlowskiee02e1c2017-08-28 04:12:49 -0700456 } else if (status == ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY) {
457 // applicaiton was scanning too frequently
458 mScannerId = -2;
Wei Wang6d811182014-05-22 12:10:25 -0700459 } else {
460 // registration failed
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700461 mScannerId = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700462 }
463 notifyAll();
464 }
465 }
466
Wei Wang6d811182014-05-22 12:10:25 -0700467 /**
468 * Callback reporting an LE scan result.
469 *
470 * @hide
471 */
472 @Override
Wei Wange0d4afb2014-07-29 21:34:25 -0700473 public void onScanResult(final ScanResult scanResult) {
Wei Wang020bd7b2014-10-16 19:39:52 -0700474 if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
Wei Wang6d811182014-05-22 12:10:25 -0700475
476 // Check null in case the scan has been stopped
477 synchronized (this) {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700478 if (mScannerId <= 0) return;
Wei Wang6d811182014-05-22 12:10:25 -0700479 }
Wei Wang9fb17912014-07-01 15:10:06 -0700480 Handler handler = new Handler(Looper.getMainLooper());
481 handler.post(new Runnable() {
Wei Wangb661bb72014-08-18 16:08:00 -0700482 @Override
Wei Wang9fb17912014-07-01 15:10:06 -0700483 public void run() {
Wei Wange0d4afb2014-07-29 21:34:25 -0700484 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
Wei Wang9fb17912014-07-01 15:10:06 -0700485 }
486 });
Wei Wang9fb17912014-07-01 15:10:06 -0700487 }
488
489 @Override
490 public void onBatchScanResults(final List<ScanResult> results) {
491 Handler handler = new Handler(Looper.getMainLooper());
492 handler.post(new Runnable() {
Wei Wangb661bb72014-08-18 16:08:00 -0700493 @Override
Wei Wang9fb17912014-07-01 15:10:06 -0700494 public void run() {
495 mScanCallback.onBatchScanResults(results);
496 }
497 });
Wei Wang6d811182014-05-22 12:10:25 -0700498 }
499
500 @Override
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700501 public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) {
Wei Wang020bd7b2014-10-16 19:39:52 -0700502 if (VDBG) {
Jack He2992cd02017-08-22 21:21:23 -0700503 Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + " " + scanResult.toString());
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700504 }
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700505
506 // Check null in case the scan has been stopped
507 synchronized (this) {
Jack Hea355e5e2017-08-22 16:06:54 -0700508 if (mScannerId <= 0) {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700509 return;
Jack Hea355e5e2017-08-22 16:06:54 -0700510 }
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700511 }
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700512 Handler handler = new Handler(Looper.getMainLooper());
513 handler.post(new Runnable() {
Jack Hea355e5e2017-08-22 16:06:54 -0700514 @Override
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700515 public void run() {
516 if (onFound) {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700517 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH,
518 scanResult);
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700519 } else {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700520 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST,
521 scanResult);
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700522 }
523 }
524 });
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700525 }
Prerepa Viswanadhamdb1dbb82015-04-09 17:14:50 -0700526
527 @Override
528 public void onScanManagerErrorCallback(final int errorCode) {
529 if (VDBG) {
530 Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode);
531 }
532 synchronized (this) {
Jack Hea355e5e2017-08-22 16:06:54 -0700533 if (mScannerId <= 0) {
Prerepa Viswanadhamdb1dbb82015-04-09 17:14:50 -0700534 return;
Jack Hea355e5e2017-08-22 16:06:54 -0700535 }
Prerepa Viswanadhamdb1dbb82015-04-09 17:14:50 -0700536 }
537 postCallbackError(mScanCallback, errorCode);
538 }
Wei Wang6d811182014-05-22 12:10:25 -0700539 }
540
Amith Yamasani461111b2017-04-13 17:46:53 -0700541 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
Wei Wang6d811182014-05-22 12:10:25 -0700550 private void postCallbackError(final ScanCallback callback, final int errorCode) {
551 mHandler.post(new Runnable() {
Wei Wangb661bb72014-08-18 16:08:00 -0700552 @Override
Wei Wang6d811182014-05-22 12:10:25 -0700553 public void run() {
554 callback.onScanFailed(errorCode);
555 }
556 });
557 }
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700558
559 private boolean isSettingsConfigAllowedForScan(ScanSettings settings) {
Wei Wang685c17582014-07-16 22:02:03 -0700560 if (mBluetoothAdapter.isOffloadedFilteringSupported()) {
561 return true;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700562 }
Wei Wang685c17582014-07-16 22:02:03 -0700563 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;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700570 }
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700571
572 private boolean isSettingsAndFilterComboAllowed(ScanSettings settings,
Jack Hea355e5e2017-08-22 16:06:54 -0700573 List<ScanFilter> filterList) {
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700574 final int callbackType = settings.getCallbackType();
575 // If onlost/onfound is requested, a non-empty filter is expected
tturneyab5267a2015-04-13 14:55:07 -0700576 if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH
Jack Hea355e5e2017-08-22 16:06:54 -0700577 | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) {
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700578 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
Jack He2992cd02017-08-22 21:21:23 -0700595 return (mBluetoothAdapter.isOffloadedFilteringSupported()
596 && mBluetoothAdapter.isHardwareTrackingFiltersAvailable());
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700597 }
598 return true;
599 }
Wei Wang6d811182014-05-22 12:10:25 -0700600}