blob: b63c614711eab63321e1b8cf5f7ee7f1fae12ea6 [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;
20import android.annotation.RequiresPermission;
Wei Wang0d0df3c2014-07-30 15:19:08 -070021import android.annotation.SystemApi;
Fyodor Kupolova1790302015-06-19 15:35:11 -070022import android.app.ActivityThread;
Wei Wang6d811182014-05-22 12:10:25 -070023import android.bluetooth.BluetoothAdapter;
Wei Wang6d811182014-05-22 12:10:25 -070024import android.bluetooth.BluetoothGatt;
25import android.bluetooth.IBluetoothGatt;
Wei Wang9fb17912014-07-01 15:10:06 -070026import android.bluetooth.IBluetoothManager;
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -070027import android.bluetooth.le.IScannerCallback;
Wei Wang6d811182014-05-22 12:10:25 -070028import android.os.Handler;
29import android.os.Looper;
30import android.os.ParcelUuid;
31import android.os.RemoteException;
Adam Lesinski6771d622016-01-15 18:14:47 -080032import android.os.WorkSource;
Wei Wang6d811182014-05-22 12:10:25 -070033import android.util.Log;
34
Wei Wang0d0df3c2014-07-30 15:19:08 -070035import java.util.ArrayList;
Wei Wang6d811182014-05-22 12:10:25 -070036import java.util.HashMap;
37import java.util.List;
38import java.util.Map;
39import java.util.UUID;
40
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
Wei Wang9fb17912014-07-01 15:10:06 -070060 private final IBluetoothManager mBluetoothManager;
Wei Wang6d811182014-05-22 12:10:25 -070061 private final Handler mHandler;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -070062 private BluetoothAdapter mBluetoothAdapter;
Wei Wang6d811182014-05-22 12:10:25 -070063 private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
64
65 /**
Wei Wangaf74e662014-07-09 14:03:42 -070066 * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
Wei Wang685c17582014-07-16 22:02:03 -070067 *
68 * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management.
Wei Wang6d811182014-05-22 12:10:25 -070069 * @hide
70 */
Wei Wang9fb17912014-07-01 15:10:06 -070071 public BluetoothLeScanner(IBluetoothManager bluetoothManager) {
72 mBluetoothManager = bluetoothManager;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -070073 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Wei Wang6d811182014-05-22 12:10:25 -070074 mHandler = new Handler(Looper.getMainLooper());
75 mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>();
76 }
77
78 /**
Wei Wang685c17582014-07-16 22:02:03 -070079 * Start Bluetooth LE scan with default parameters and no filters. The scan results will be
80 * delivered through {@code callback}.
Wei Wangaf74e662014-07-09 14:03:42 -070081 * <p>
82 * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
Fyodor Kupolov7bd8be02015-07-16 19:40:13 -070083 * 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 Wangaf74e662014-07-09 14:03:42 -070087 *
88 * @param callback Callback used to deliver scan results.
89 * @throws IllegalArgumentException If {@code callback} is null.
90 */
Tor Norbye2d497522015-04-23 17:10:21 -070091 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Wei Wangaf74e662014-07-09 14:03:42 -070092 public void startScan(final ScanCallback callback) {
Wei Wang833559d2014-08-29 10:26:13 -070093 startScan(null, new ScanSettings.Builder().build(), callback);
Wei Wangaf74e662014-07-09 14:03:42 -070094 }
95
96 /**
Wei Wang6d811182014-05-22 12:10:25 -070097 * 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 Kupolov7bd8be02015-07-16 19:40:13 -0700100 * 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 Wang6d811182014-05-22 12:10:25 -0700104 *
105 * @param filters {@link ScanFilter}s for finding exact BLE devices.
Wei Wangaf74e662014-07-09 14:03:42 -0700106 * @param settings Settings for the scan.
107 * @param callback Callback used to deliver scan results.
Wei Wang6d811182014-05-22 12:10:25 -0700108 * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
109 */
Tor Norbye2d497522015-04-23 17:10:21 -0700110 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Wei Wang6d811182014-05-22 12:10:25 -0700111 public void startScan(List<ScanFilter> filters, ScanSettings settings,
112 final ScanCallback callback) {
Adam Lesinski6771d622016-01-15 18:14:47 -0800113 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 Wang0d0df3c2014-07-30 15:19:08 -0700149 }
150
151 private void startScan(List<ScanFilter> filters, ScanSettings settings,
Adam Lesinski6771d622016-01-15 18:14:47 -0800152 final WorkSource workSource, final ScanCallback callback,
153 List<List<ResultStorageDescriptor>> resultStorages) {
Wei Wang833559d2014-08-29 10:26:13 -0700154 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
Adam Lesinski6771d622016-01-15 18:14:47 -0800155 if (callback == null) {
156 throw new IllegalArgumentException("callback is null");
157 }
158 if (settings == null) {
159 throw new IllegalArgumentException("settings is null");
Wei Wang6d811182014-05-22 12:10:25 -0700160 }
161 synchronized (mLeScanClients) {
162 if (mLeScanClients.containsKey(callback)) {
163 postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED);
164 return;
165 }
Wei Wang9fb17912014-07-01 15:10:06 -0700166 IBluetoothGatt gatt;
167 try {
168 gatt = mBluetoothManager.getBluetoothGatt();
169 } catch (RemoteException e) {
170 gatt = null;
171 }
172 if (gatt == null) {
Wei Wangaf74e662014-07-09 14:03:42 -0700173 postCallbackError(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
Wei Wang9fb17912014-07-01 15:10:06 -0700174 return;
175 }
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700176 if (!isSettingsConfigAllowedForScan(settings)) {
177 postCallbackError(callback,
178 ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
179 return;
180 }
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700181 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 Wang9fb17912014-07-01 15:10:06 -0700191 BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
Adam Lesinski6771d622016-01-15 18:14:47 -0800192 settings, workSource, callback, resultStorages);
Wei Wangb661bb72014-08-18 16:08:00 -0700193 wrapper.startRegisteration();
Wei Wang6d811182014-05-22 12:10:25 -0700194 }
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 Norbye2d497522015-04-23 17:10:21 -0700204 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Wei Wang6d811182014-05-22 12:10:25 -0700205 public void stopScan(ScanCallback callback) {
Wei Wang833559d2014-08-29 10:26:13 -0700206 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
Wei Wang6d811182014-05-22 12:10:25 -0700207 synchronized (mLeScanClients) {
208 BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback);
209 if (wrapper == null) {
Wei Wangb661bb72014-08-18 16:08:00 -0700210 if (DBG) Log.d(TAG, "could not find callback wrapper");
Wei Wang6d811182014-05-22 12:10:25 -0700211 return;
212 }
213 wrapper.stopLeScan();
214 }
215 }
216
217 /**
Wei Wang9fb17912014-07-01 15:10:06 -0700218 * 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 Wang6d811182014-05-22 12:10:25 -0700221 *
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 Wang6d811182014-05-22 12:10:25 -0700224 */
Wei Wang9fb17912014-07-01 15:10:06 -0700225 public void flushPendingScanResults(ScanCallback callback) {
Wei Wang833559d2014-08-29 10:26:13 -0700226 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
Wei Wang9fb17912014-07-01 15:10:06 -0700227 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 Wang6d811182014-05-22 12:10:25 -0700237 }
238
239 /**
Wei Wang0d0df3c2014-07-30 15:19:08 -0700240 * 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 Lesinski6771d622016-01-15 18:14:47 -0800255 startScan(scanFilters, settings, null, callback, scanStorages);
Wei Wang0d0df3c2014-07-30 15:19:08 -0700256 }
257
258 /**
Wei Wangee809222014-08-12 22:16:32 -0700259 * 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 Wang6d811182014-05-22 12:10:25 -0700268 * Bluetooth GATT interface callbacks
269 */
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700270 private class BleScanCallbackWrapper extends IScannerCallback.Stub {
Wei Wangb661bb72014-08-18 16:08:00 -0700271 private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000;
Wei Wang6d811182014-05-22 12:10:25 -0700272
273 private final ScanCallback mScanCallback;
274 private final List<ScanFilter> mFilters;
Adam Lesinski6771d622016-01-15 18:14:47 -0800275 private final WorkSource mWorkSource;
Wei Wang6d811182014-05-22 12:10:25 -0700276 private ScanSettings mSettings;
277 private IBluetoothGatt mBluetoothGatt;
Wei Wang0d0df3c2014-07-30 15:19:08 -0700278 private List<List<ResultStorageDescriptor>> mResultStorages;
Wei Wang6d811182014-05-22 12:10:25 -0700279
280 // mLeHandle 0: not registered
Wei Wang02bc0082015-11-09 19:45:53 -0800281 // -1: scan stopped or registration failed
Wei Wang6d811182014-05-22 12:10:25 -0700282 // > 0: registered and scan started
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700283 private int mScannerId;
Wei Wang6d811182014-05-22 12:10:25 -0700284
285 public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
286 List<ScanFilter> filters, ScanSettings settings,
Adam Lesinski6771d622016-01-15 18:14:47 -0800287 WorkSource workSource, ScanCallback scanCallback,
288 List<List<ResultStorageDescriptor>> resultStorages) {
Wei Wang6d811182014-05-22 12:10:25 -0700289 mBluetoothGatt = bluetoothGatt;
290 mFilters = filters;
291 mSettings = settings;
Adam Lesinski6771d622016-01-15 18:14:47 -0800292 mWorkSource = workSource;
Wei Wang6d811182014-05-22 12:10:25 -0700293 mScanCallback = scanCallback;
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700294 mScannerId = 0;
Wei Wang0d0df3c2014-07-30 15:19:08 -0700295 mResultStorages = resultStorages;
Wei Wang6d811182014-05-22 12:10:25 -0700296 }
297
Wei Wangb661bb72014-08-18 16:08:00 -0700298 public void startRegisteration() {
Wei Wang6d811182014-05-22 12:10:25 -0700299 synchronized (this) {
Wei Wangb661bb72014-08-18 16:08:00 -0700300 // Scan stopped.
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700301 if (mScannerId == -1) return;
Wei Wang6d811182014-05-22 12:10:25 -0700302 try {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700303 mBluetoothGatt.registerScanner(this);
Wei Wangb661bb72014-08-18 16:08:00 -0700304 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 Pawlowski1b49e6e2016-10-26 13:05:30 -0700309 if (mScannerId > 0) {
Wei Wangb661bb72014-08-18 16:08:00 -0700310 mLeScanClients.put(mScanCallback, this);
311 } else {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700312 // Registration timed out or got exception, reset scannerId to -1 so no
Wei Wang02bc0082015-11-09 19:45:53 -0800313 // subsequent operations can proceed.
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700314 if (mScannerId == 0) mScannerId = -1;
Wei Wangb661bb72014-08-18 16:08:00 -0700315 postCallbackError(mScanCallback,
316 ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
Wei Wang6d811182014-05-22 12:10:25 -0700317 }
318 }
Wei Wang6d811182014-05-22 12:10:25 -0700319 }
320
321 public void stopLeScan() {
322 synchronized (this) {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700323 if (mScannerId <= 0) {
324 Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
Wei Wang6d811182014-05-22 12:10:25 -0700325 return;
326 }
327 try {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700328 mBluetoothGatt.stopScan(mScannerId);
329 mBluetoothGatt.unregisterScanner(mScannerId);
Wei Wang6d811182014-05-22 12:10:25 -0700330 } catch (RemoteException e) {
Wei Wang9fb17912014-07-01 15:10:06 -0700331 Log.e(TAG, "Failed to stop scan and unregister", e);
Wei Wang6d811182014-05-22 12:10:25 -0700332 }
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700333 mScannerId = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700334 }
335 }
336
Wei Wang9fb17912014-07-01 15:10:06 -0700337 void flushPendingBatchResults() {
338 synchronized (this) {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700339 if (mScannerId <= 0) {
340 Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
Wei Wang9fb17912014-07-01 15:10:06 -0700341 return;
342 }
343 try {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700344 mBluetoothGatt.flushPendingBatchResults(mScannerId);
Wei Wang9fb17912014-07-01 15:10:06 -0700345 } catch (RemoteException e) {
346 Log.e(TAG, "Failed to get pending scan results", e);
347 }
348 }
349 }
350
Wei Wang6d811182014-05-22 12:10:25 -0700351 /**
352 * Application interface registered - app is ready to go
353 */
354 @Override
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700355 public void onScannerRegistered(int status, int scannerId) {
356 Log.d(TAG, "onScannerRegistered() - status=" + status +
357 " scannerId=" + scannerId + " mScannerId=" + mScannerId);
Wei Wang6d811182014-05-22 12:10:25 -0700358 synchronized (this) {
Wei Wang6d811182014-05-22 12:10:25 -0700359 if (status == BluetoothGatt.GATT_SUCCESS) {
Wei Wang6d811182014-05-22 12:10:25 -0700360 try {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700361 if (mScannerId == -1) {
Wei Wang02bc0082015-11-09 19:45:53 -0800362 // Registration succeeds after timeout, unregister client.
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700363 mBluetoothGatt.unregisterClient(scannerId);
Wei Wang02bc0082015-11-09 19:45:53 -0800364 } else {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700365 mScannerId = scannerId;
366 mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
Wei Wang02bc0082015-11-09 19:45:53 -0800367 mWorkSource, mResultStorages,
368 ActivityThread.currentOpPackageName());
369 }
Wei Wang6d811182014-05-22 12:10:25 -0700370 } catch (RemoteException e) {
371 Log.e(TAG, "fail to start le scan: " + e);
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700372 mScannerId = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700373 }
374 } else {
375 // registration failed
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700376 mScannerId = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700377 }
378 notifyAll();
379 }
380 }
381
Wei Wang6d811182014-05-22 12:10:25 -0700382 /**
383 * Callback reporting an LE scan result.
384 *
385 * @hide
386 */
387 @Override
Wei Wange0d4afb2014-07-29 21:34:25 -0700388 public void onScanResult(final ScanResult scanResult) {
Wei Wang020bd7b2014-10-16 19:39:52 -0700389 if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
Wei Wang6d811182014-05-22 12:10:25 -0700390
391 // Check null in case the scan has been stopped
392 synchronized (this) {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700393 if (mScannerId <= 0) return;
Wei Wang6d811182014-05-22 12:10:25 -0700394 }
Wei Wang9fb17912014-07-01 15:10:06 -0700395 Handler handler = new Handler(Looper.getMainLooper());
396 handler.post(new Runnable() {
Wei Wangb661bb72014-08-18 16:08:00 -0700397 @Override
Wei Wang9fb17912014-07-01 15:10:06 -0700398 public void run() {
Wei Wange0d4afb2014-07-29 21:34:25 -0700399 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
Wei Wang9fb17912014-07-01 15:10:06 -0700400 }
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 Wangb661bb72014-08-18 16:08:00 -0700409 @Override
Wei Wang9fb17912014-07-01 15:10:06 -0700410 public void run() {
411 mScanCallback.onBatchScanResults(results);
412 }
413 });
Wei Wang6d811182014-05-22 12:10:25 -0700414 }
415
416 @Override
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700417 public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) {
Wei Wang020bd7b2014-10-16 19:39:52 -0700418 if (VDBG) {
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700419 Log.d(TAG, "onFoundOrLost() - onFound = " + onFound +
420 " " + scanResult.toString());
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700421 }
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700422
423 // Check null in case the scan has been stopped
424 synchronized (this) {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700425 if (mScannerId <= 0)
Wei Wang0d0df3c2014-07-30 15:19:08 -0700426 return;
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700427 }
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700428 Handler handler = new Handler(Looper.getMainLooper());
429 handler.post(new Runnable() {
430 @Override
431 public void run() {
432 if (onFound) {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700433 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH,
434 scanResult);
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700435 } else {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700436 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST,
437 scanResult);
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700438 }
439 }
440 });
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700441 }
Prerepa Viswanadhamdb1dbb82015-04-09 17:14:50 -0700442
443 @Override
444 public void onScanManagerErrorCallback(final int errorCode) {
445 if (VDBG) {
446 Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode);
447 }
448 synchronized (this) {
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700449 if (mScannerId <= 0)
Prerepa Viswanadhamdb1dbb82015-04-09 17:14:50 -0700450 return;
451 }
452 postCallbackError(mScanCallback, errorCode);
453 }
Wei Wang6d811182014-05-22 12:10:25 -0700454 }
455
456 private void postCallbackError(final ScanCallback callback, final int errorCode) {
457 mHandler.post(new Runnable() {
Wei Wangb661bb72014-08-18 16:08:00 -0700458 @Override
Wei Wang6d811182014-05-22 12:10:25 -0700459 public void run() {
460 callback.onScanFailed(errorCode);
461 }
462 });
463 }
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700464
465 private boolean isSettingsConfigAllowedForScan(ScanSettings settings) {
Wei Wang685c17582014-07-16 22:02:03 -0700466 if (mBluetoothAdapter.isOffloadedFilteringSupported()) {
467 return true;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700468 }
Wei Wang685c17582014-07-16 22:02:03 -0700469 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 Viswanadham8e5270f2014-07-08 16:33:34 -0700476 }
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700477
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
tturneyab5267a2015-04-13 14:55:07 -0700482 if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH
483 | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) {
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700484 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 Wang6d811182014-05-22 12:10:25 -0700506}