blob: 5715ff87242d1e6fdc231ed048928b1d17008787 [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;
Wei Wange0d4afb2014-07-29 21:34:25 -070025import android.bluetooth.BluetoothGattCallbackWrapper;
Wei Wang6d811182014-05-22 12:10:25 -070026import android.bluetooth.IBluetoothGatt;
Wei Wang9fb17912014-07-01 15:10:06 -070027import android.bluetooth.IBluetoothManager;
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 */
Wei Wangb661bb72014-08-18 16:08:00 -0700270 private class BleScanCallbackWrapper extends BluetoothGattCallbackWrapper {
271 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
Wei Wang685c17582014-07-16 22:02:03 -0700283 private int mClientIf;
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;
Wei Wang685c17582014-07-16 22:02:03 -0700294 mClientIf = 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.
301 if (mClientIf == -1) return;
Wei Wang6d811182014-05-22 12:10:25 -0700302 try {
Wei Wangb661bb72014-08-18 16:08:00 -0700303 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 Wang02bc0082015-11-09 19:45:53 -0800313 // Registration timed out or got exception, reset clientIf to -1 so no
314 // subsequent operations can proceed.
315 if (mClientIf == 0) mClientIf = -1;
Wei Wangb661bb72014-08-18 16:08:00 -0700316 postCallbackError(mScanCallback,
317 ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
Wei Wang6d811182014-05-22 12:10:25 -0700318 }
319 }
Wei Wang6d811182014-05-22 12:10:25 -0700320 }
321
322 public void stopLeScan() {
323 synchronized (this) {
Wei Wang685c17582014-07-16 22:02:03 -0700324 if (mClientIf <= 0) {
325 Log.e(TAG, "Error state, mLeHandle: " + mClientIf);
Wei Wang6d811182014-05-22 12:10:25 -0700326 return;
327 }
328 try {
Wei Wang685c17582014-07-16 22:02:03 -0700329 mBluetoothGatt.stopScan(mClientIf, false);
330 mBluetoothGatt.unregisterClient(mClientIf);
Wei Wang6d811182014-05-22 12:10:25 -0700331 } catch (RemoteException e) {
Wei Wang9fb17912014-07-01 15:10:06 -0700332 Log.e(TAG, "Failed to stop scan and unregister", e);
Wei Wang6d811182014-05-22 12:10:25 -0700333 }
Wei Wang685c17582014-07-16 22:02:03 -0700334 mClientIf = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700335 }
336 }
337
Wei Wang9fb17912014-07-01 15:10:06 -0700338 void flushPendingBatchResults() {
339 synchronized (this) {
Wei Wang685c17582014-07-16 22:02:03 -0700340 if (mClientIf <= 0) {
341 Log.e(TAG, "Error state, mLeHandle: " + mClientIf);
Wei Wang9fb17912014-07-01 15:10:06 -0700342 return;
343 }
344 try {
Wei Wang685c17582014-07-16 22:02:03 -0700345 mBluetoothGatt.flushPendingBatchResults(mClientIf, false);
Wei Wang9fb17912014-07-01 15:10:06 -0700346 } catch (RemoteException e) {
347 Log.e(TAG, "Failed to get pending scan results", e);
348 }
349 }
350 }
351
Wei Wang6d811182014-05-22 12:10:25 -0700352 /**
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 Wang02bc0082015-11-09 19:45:53 -0800358 " clientIf=" + clientIf + " mClientIf=" + mClientIf);
Wei Wang6d811182014-05-22 12:10:25 -0700359 synchronized (this) {
Wei Wang6d811182014-05-22 12:10:25 -0700360 if (status == BluetoothGatt.GATT_SUCCESS) {
Wei Wang6d811182014-05-22 12:10:25 -0700361 try {
Wei Wang02bc0082015-11-09 19:45:53 -0800362 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 Wang6d811182014-05-22 12:10:25 -0700371 } catch (RemoteException e) {
372 Log.e(TAG, "fail to start le scan: " + e);
Wei Wang685c17582014-07-16 22:02:03 -0700373 mClientIf = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700374 }
375 } else {
376 // registration failed
Wei Wang685c17582014-07-16 22:02:03 -0700377 mClientIf = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700378 }
379 notifyAll();
380 }
381 }
382
Wei Wang6d811182014-05-22 12:10:25 -0700383 /**
384 * Callback reporting an LE scan result.
385 *
386 * @hide
387 */
388 @Override
Wei Wange0d4afb2014-07-29 21:34:25 -0700389 public void onScanResult(final ScanResult scanResult) {
Wei Wang020bd7b2014-10-16 19:39:52 -0700390 if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
Wei Wang6d811182014-05-22 12:10:25 -0700391
392 // Check null in case the scan has been stopped
393 synchronized (this) {
Wei Wangb661bb72014-08-18 16:08:00 -0700394 if (mClientIf <= 0) return;
Wei Wang6d811182014-05-22 12:10:25 -0700395 }
Wei Wang9fb17912014-07-01 15:10:06 -0700396 Handler handler = new Handler(Looper.getMainLooper());
397 handler.post(new Runnable() {
Wei Wangb661bb72014-08-18 16:08:00 -0700398 @Override
Wei Wang9fb17912014-07-01 15:10:06 -0700399 public void run() {
Wei Wange0d4afb2014-07-29 21:34:25 -0700400 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
Wei Wang9fb17912014-07-01 15:10:06 -0700401 }
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 Wangb661bb72014-08-18 16:08:00 -0700410 @Override
Wei Wang9fb17912014-07-01 15:10:06 -0700411 public void run() {
412 mScanCallback.onBatchScanResults(results);
413 }
414 });
Wei Wang6d811182014-05-22 12:10:25 -0700415 }
416
417 @Override
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700418 public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) {
Wei Wang020bd7b2014-10-16 19:39:52 -0700419 if (VDBG) {
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700420 Log.d(TAG, "onFoundOrLost() - onFound = " + onFound +
421 " " + scanResult.toString());
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700422 }
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700423
424 // Check null in case the scan has been stopped
425 synchronized (this) {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700426 if (mClientIf <= 0)
427 return;
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700428 }
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700429 Handler handler = new Handler(Looper.getMainLooper());
430 handler.post(new Runnable() {
431 @Override
432 public void run() {
433 if (onFound) {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700434 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH,
435 scanResult);
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700436 } else {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700437 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST,
438 scanResult);
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700439 }
440 }
441 });
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700442 }
Prerepa Viswanadhamdb1dbb82015-04-09 17:14:50 -0700443
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 Wang6d811182014-05-22 12:10:25 -0700455 }
456
457 private void postCallbackError(final ScanCallback callback, final int errorCode) {
458 mHandler.post(new Runnable() {
Wei Wangb661bb72014-08-18 16:08:00 -0700459 @Override
Wei Wang6d811182014-05-22 12:10:25 -0700460 public void run() {
461 callback.onScanFailed(errorCode);
462 }
463 });
464 }
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700465
466 private boolean isSettingsConfigAllowedForScan(ScanSettings settings) {
Wei Wang685c17582014-07-16 22:02:03 -0700467 if (mBluetoothAdapter.isOffloadedFilteringSupported()) {
468 return true;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700469 }
Wei Wang685c17582014-07-16 22:02:03 -0700470 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 Viswanadham8e5270f2014-07-08 16:33:34 -0700477 }
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700478
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
tturneyab5267a2015-04-13 14:55:07 -0700483 if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH
484 | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) {
Prerepa Viswanadhame593d0a2015-04-07 14:36:53 -0700485 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 Wang6d811182014-05-22 12:10:25 -0700507}