blob: 93ea299bc8dd42889715dea53caf99007be3734a [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
Wei Wang0d0df3c2014-07-30 15:19:08 -070019import android.annotation.SystemApi;
Wei Wang6d811182014-05-22 12:10:25 -070020import android.bluetooth.BluetoothAdapter;
Wei Wang6d811182014-05-22 12:10:25 -070021import android.bluetooth.BluetoothGatt;
Wei Wange0d4afb2014-07-29 21:34:25 -070022import android.bluetooth.BluetoothGattCallbackWrapper;
Wei Wang6d811182014-05-22 12:10:25 -070023import android.bluetooth.IBluetoothGatt;
Wei Wang9fb17912014-07-01 15:10:06 -070024import android.bluetooth.IBluetoothManager;
Wei Wang6d811182014-05-22 12:10:25 -070025import android.os.Handler;
26import android.os.Looper;
27import android.os.ParcelUuid;
28import android.os.RemoteException;
Wei Wang6d811182014-05-22 12:10:25 -070029import android.util.Log;
30
Wei Wang0d0df3c2014-07-30 15:19:08 -070031import java.util.ArrayList;
Wei Wang6d811182014-05-22 12:10:25 -070032import java.util.HashMap;
33import java.util.List;
34import java.util.Map;
35import java.util.UUID;
36
37/**
38 * This class provides methods to perform scan related operations for Bluetooth LE devices. An
Wei Wang685c17582014-07-16 22:02:03 -070039 * application can scan for a particular type of Bluetotoh LE devices using {@link ScanFilter}. It
40 * can also request different types of callbacks for delivering the result.
Wei Wang6d811182014-05-22 12:10:25 -070041 * <p>
42 * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
43 * {@link BluetoothLeScanner}.
44 * <p>
Wei Wangaf74e662014-07-09 14:03:42 -070045 * <b>Note:</b> Most of the scan methods here require
46 * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
Wei Wang6d811182014-05-22 12:10:25 -070047 *
48 * @see ScanFilter
49 */
50public final class BluetoothLeScanner {
51
52 private static final String TAG = "BluetoothLeScanner";
53 private static final boolean DBG = true;
Wei Wang020bd7b2014-10-16 19:39:52 -070054 private static final boolean VDBG = false;
Wei Wang6d811182014-05-22 12:10:25 -070055
Wei Wang9fb17912014-07-01 15:10:06 -070056 private final IBluetoothManager mBluetoothManager;
Wei Wang6d811182014-05-22 12:10:25 -070057 private final Handler mHandler;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -070058 private BluetoothAdapter mBluetoothAdapter;
Wei Wang6d811182014-05-22 12:10:25 -070059 private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
60
61 /**
Wei Wangaf74e662014-07-09 14:03:42 -070062 * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
Wei Wang685c17582014-07-16 22:02:03 -070063 *
64 * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management.
Wei Wang6d811182014-05-22 12:10:25 -070065 * @hide
66 */
Wei Wang9fb17912014-07-01 15:10:06 -070067 public BluetoothLeScanner(IBluetoothManager bluetoothManager) {
68 mBluetoothManager = bluetoothManager;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -070069 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Wei Wang6d811182014-05-22 12:10:25 -070070 mHandler = new Handler(Looper.getMainLooper());
71 mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>();
72 }
73
74 /**
Wei Wang685c17582014-07-16 22:02:03 -070075 * Start Bluetooth LE scan with default parameters and no filters. The scan results will be
76 * delivered through {@code callback}.
Wei Wangaf74e662014-07-09 14:03:42 -070077 * <p>
78 * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
79 *
80 * @param callback Callback used to deliver scan results.
81 * @throws IllegalArgumentException If {@code callback} is null.
82 */
83 public void startScan(final ScanCallback callback) {
84 if (callback == null) {
85 throw new IllegalArgumentException("callback is null");
86 }
Wei Wang833559d2014-08-29 10:26:13 -070087 startScan(null, new ScanSettings.Builder().build(), callback);
Wei Wangaf74e662014-07-09 14:03:42 -070088 }
89
90 /**
Wei Wang6d811182014-05-22 12:10:25 -070091 * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
92 * <p>
93 * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
94 *
95 * @param filters {@link ScanFilter}s for finding exact BLE devices.
Wei Wangaf74e662014-07-09 14:03:42 -070096 * @param settings Settings for the scan.
97 * @param callback Callback used to deliver scan results.
Wei Wang6d811182014-05-22 12:10:25 -070098 * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
99 */
100 public void startScan(List<ScanFilter> filters, ScanSettings settings,
101 final ScanCallback callback) {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700102 startScan(filters, settings, callback, null);
103 }
104
105 private void startScan(List<ScanFilter> filters, ScanSettings settings,
106 final ScanCallback callback, List<List<ResultStorageDescriptor>> resultStorages) {
Wei Wang833559d2014-08-29 10:26:13 -0700107 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
Wei Wang6d811182014-05-22 12:10:25 -0700108 if (settings == null || callback == null) {
109 throw new IllegalArgumentException("settings or callback is null");
110 }
111 synchronized (mLeScanClients) {
112 if (mLeScanClients.containsKey(callback)) {
113 postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED);
114 return;
115 }
Wei Wang9fb17912014-07-01 15:10:06 -0700116 IBluetoothGatt gatt;
117 try {
118 gatt = mBluetoothManager.getBluetoothGatt();
119 } catch (RemoteException e) {
120 gatt = null;
121 }
122 if (gatt == null) {
Wei Wangaf74e662014-07-09 14:03:42 -0700123 postCallbackError(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
Wei Wang9fb17912014-07-01 15:10:06 -0700124 return;
125 }
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700126 if (!isSettingsConfigAllowedForScan(settings)) {
127 postCallbackError(callback,
128 ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
129 return;
130 }
Wei Wang9fb17912014-07-01 15:10:06 -0700131 BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
Wei Wang0d0df3c2014-07-30 15:19:08 -0700132 settings, callback, resultStorages);
Wei Wangb661bb72014-08-18 16:08:00 -0700133 wrapper.startRegisteration();
Wei Wang6d811182014-05-22 12:10:25 -0700134 }
135 }
136
137 /**
138 * Stops an ongoing Bluetooth LE scan.
139 * <p>
140 * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
141 *
142 * @param callback
143 */
144 public void stopScan(ScanCallback callback) {
Wei Wang833559d2014-08-29 10:26:13 -0700145 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
Wei Wang6d811182014-05-22 12:10:25 -0700146 synchronized (mLeScanClients) {
147 BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback);
148 if (wrapper == null) {
Wei Wangb661bb72014-08-18 16:08:00 -0700149 if (DBG) Log.d(TAG, "could not find callback wrapper");
Wei Wang6d811182014-05-22 12:10:25 -0700150 return;
151 }
152 wrapper.stopLeScan();
153 }
154 }
155
156 /**
Wei Wang9fb17912014-07-01 15:10:06 -0700157 * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth
158 * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data
159 * will be delivered through the {@code callback}.
Wei Wang6d811182014-05-22 12:10:25 -0700160 *
161 * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
162 * used to start scan.
Wei Wang6d811182014-05-22 12:10:25 -0700163 */
Wei Wang9fb17912014-07-01 15:10:06 -0700164 public void flushPendingScanResults(ScanCallback callback) {
Wei Wang833559d2014-08-29 10:26:13 -0700165 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
Wei Wang9fb17912014-07-01 15:10:06 -0700166 if (callback == null) {
167 throw new IllegalArgumentException("callback cannot be null!");
168 }
169 synchronized (mLeScanClients) {
170 BleScanCallbackWrapper wrapper = mLeScanClients.get(callback);
171 if (wrapper == null) {
172 return;
173 }
174 wrapper.flushPendingBatchResults();
175 }
Wei Wang6d811182014-05-22 12:10:25 -0700176 }
177
178 /**
Wei Wang0d0df3c2014-07-30 15:19:08 -0700179 * Start truncated scan.
180 *
181 * @hide
182 */
183 @SystemApi
184 public void startTruncatedScan(List<TruncatedFilter> truncatedFilters, ScanSettings settings,
185 final ScanCallback callback) {
186 int filterSize = truncatedFilters.size();
187 List<ScanFilter> scanFilters = new ArrayList<ScanFilter>(filterSize);
188 List<List<ResultStorageDescriptor>> scanStorages =
189 new ArrayList<List<ResultStorageDescriptor>>(filterSize);
190 for (TruncatedFilter filter : truncatedFilters) {
191 scanFilters.add(filter.getFilter());
192 scanStorages.add(filter.getStorageDescriptors());
193 }
194 startScan(scanFilters, settings, callback, scanStorages);
195 }
196
197 /**
Wei Wangee809222014-08-12 22:16:32 -0700198 * Cleans up scan clients. Should be called when bluetooth is down.
199 *
200 * @hide
201 */
202 public void cleanup() {
203 mLeScanClients.clear();
204 }
205
206 /**
Wei Wang6d811182014-05-22 12:10:25 -0700207 * Bluetooth GATT interface callbacks
208 */
Wei Wangb661bb72014-08-18 16:08:00 -0700209 private class BleScanCallbackWrapper extends BluetoothGattCallbackWrapper {
210 private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000;
Wei Wang6d811182014-05-22 12:10:25 -0700211
212 private final ScanCallback mScanCallback;
213 private final List<ScanFilter> mFilters;
214 private ScanSettings mSettings;
215 private IBluetoothGatt mBluetoothGatt;
Wei Wang0d0df3c2014-07-30 15:19:08 -0700216 private List<List<ResultStorageDescriptor>> mResultStorages;
Wei Wang6d811182014-05-22 12:10:25 -0700217
218 // mLeHandle 0: not registered
219 // -1: scan stopped
220 // > 0: registered and scan started
Wei Wang685c17582014-07-16 22:02:03 -0700221 private int mClientIf;
Wei Wang6d811182014-05-22 12:10:25 -0700222
223 public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
224 List<ScanFilter> filters, ScanSettings settings,
Wei Wang0d0df3c2014-07-30 15:19:08 -0700225 ScanCallback scanCallback, List<List<ResultStorageDescriptor>> resultStorages) {
Wei Wang6d811182014-05-22 12:10:25 -0700226 mBluetoothGatt = bluetoothGatt;
227 mFilters = filters;
228 mSettings = settings;
229 mScanCallback = scanCallback;
Wei Wang685c17582014-07-16 22:02:03 -0700230 mClientIf = 0;
Wei Wang0d0df3c2014-07-30 15:19:08 -0700231 mResultStorages = resultStorages;
Wei Wang6d811182014-05-22 12:10:25 -0700232 }
233
Wei Wangb661bb72014-08-18 16:08:00 -0700234 public void startRegisteration() {
Wei Wang6d811182014-05-22 12:10:25 -0700235 synchronized (this) {
Wei Wangb661bb72014-08-18 16:08:00 -0700236 // Scan stopped.
237 if (mClientIf == -1) return;
Wei Wang6d811182014-05-22 12:10:25 -0700238 try {
Wei Wangb661bb72014-08-18 16:08:00 -0700239 UUID uuid = UUID.randomUUID();
240 mBluetoothGatt.registerClient(new ParcelUuid(uuid), this);
241 wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS);
242 } catch (InterruptedException | RemoteException e) {
243 Log.e(TAG, "application registeration exception", e);
244 postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
245 }
246 if (mClientIf > 0) {
247 mLeScanClients.put(mScanCallback, this);
248 } else {
249 postCallbackError(mScanCallback,
250 ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
Wei Wang6d811182014-05-22 12:10:25 -0700251 }
252 }
Wei Wang6d811182014-05-22 12:10:25 -0700253 }
254
255 public void stopLeScan() {
256 synchronized (this) {
Wei Wang685c17582014-07-16 22:02:03 -0700257 if (mClientIf <= 0) {
258 Log.e(TAG, "Error state, mLeHandle: " + mClientIf);
Wei Wang6d811182014-05-22 12:10:25 -0700259 return;
260 }
261 try {
Wei Wang685c17582014-07-16 22:02:03 -0700262 mBluetoothGatt.stopScan(mClientIf, false);
263 mBluetoothGatt.unregisterClient(mClientIf);
Wei Wang6d811182014-05-22 12:10:25 -0700264 } catch (RemoteException e) {
Wei Wang9fb17912014-07-01 15:10:06 -0700265 Log.e(TAG, "Failed to stop scan and unregister", e);
Wei Wang6d811182014-05-22 12:10:25 -0700266 }
Wei Wang685c17582014-07-16 22:02:03 -0700267 mClientIf = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700268 }
269 }
270
Wei Wang9fb17912014-07-01 15:10:06 -0700271 void flushPendingBatchResults() {
272 synchronized (this) {
Wei Wang685c17582014-07-16 22:02:03 -0700273 if (mClientIf <= 0) {
274 Log.e(TAG, "Error state, mLeHandle: " + mClientIf);
Wei Wang9fb17912014-07-01 15:10:06 -0700275 return;
276 }
277 try {
Wei Wang685c17582014-07-16 22:02:03 -0700278 mBluetoothGatt.flushPendingBatchResults(mClientIf, false);
Wei Wang9fb17912014-07-01 15:10:06 -0700279 } catch (RemoteException e) {
280 Log.e(TAG, "Failed to get pending scan results", e);
281 }
282 }
283 }
284
Wei Wang6d811182014-05-22 12:10:25 -0700285 /**
286 * Application interface registered - app is ready to go
287 */
288 @Override
289 public void onClientRegistered(int status, int clientIf) {
290 Log.d(TAG, "onClientRegistered() - status=" + status +
291 " clientIf=" + clientIf);
Wei Wang6d811182014-05-22 12:10:25 -0700292 synchronized (this) {
Wei Wang685c17582014-07-16 22:02:03 -0700293 if (mClientIf == -1) {
Wei Wangb661bb72014-08-18 16:08:00 -0700294 if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled");
Wei Wang6d811182014-05-22 12:10:25 -0700295 }
296
297 if (status == BluetoothGatt.GATT_SUCCESS) {
Wei Wang685c17582014-07-16 22:02:03 -0700298 mClientIf = clientIf;
Wei Wang6d811182014-05-22 12:10:25 -0700299 try {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700300 mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters,
301 mResultStorages);
Wei Wang6d811182014-05-22 12:10:25 -0700302 } catch (RemoteException e) {
303 Log.e(TAG, "fail to start le scan: " + e);
Wei Wang685c17582014-07-16 22:02:03 -0700304 mClientIf = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700305 }
306 } else {
307 // registration failed
Wei Wang685c17582014-07-16 22:02:03 -0700308 mClientIf = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700309 }
310 notifyAll();
311 }
312 }
313
Wei Wang6d811182014-05-22 12:10:25 -0700314 /**
315 * Callback reporting an LE scan result.
316 *
317 * @hide
318 */
319 @Override
Wei Wange0d4afb2014-07-29 21:34:25 -0700320 public void onScanResult(final ScanResult scanResult) {
Wei Wang020bd7b2014-10-16 19:39:52 -0700321 if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
Wei Wang6d811182014-05-22 12:10:25 -0700322
323 // Check null in case the scan has been stopped
324 synchronized (this) {
Wei Wangb661bb72014-08-18 16:08:00 -0700325 if (mClientIf <= 0) return;
Wei Wang6d811182014-05-22 12:10:25 -0700326 }
Wei Wang9fb17912014-07-01 15:10:06 -0700327 Handler handler = new Handler(Looper.getMainLooper());
328 handler.post(new Runnable() {
Wei Wangb661bb72014-08-18 16:08:00 -0700329 @Override
Wei Wang9fb17912014-07-01 15:10:06 -0700330 public void run() {
Wei Wange0d4afb2014-07-29 21:34:25 -0700331 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
Wei Wang9fb17912014-07-01 15:10:06 -0700332 }
333 });
334
335 }
336
337 @Override
338 public void onBatchScanResults(final List<ScanResult> results) {
339 Handler handler = new Handler(Looper.getMainLooper());
340 handler.post(new Runnable() {
Wei Wangb661bb72014-08-18 16:08:00 -0700341 @Override
Wei Wang9fb17912014-07-01 15:10:06 -0700342 public void run() {
343 mScanCallback.onBatchScanResults(results);
344 }
345 });
Wei Wang6d811182014-05-22 12:10:25 -0700346 }
347
348 @Override
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700349 public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) {
Wei Wang020bd7b2014-10-16 19:39:52 -0700350 if (VDBG) {
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700351 Log.d(TAG, "onFoundOrLost() - onFound = " + onFound +
352 " " + scanResult.toString());
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700353 }
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700354
355 // Check null in case the scan has been stopped
356 synchronized (this) {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700357 if (mClientIf <= 0)
358 return;
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700359 }
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700360 Handler handler = new Handler(Looper.getMainLooper());
361 handler.post(new Runnable() {
362 @Override
363 public void run() {
364 if (onFound) {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700365 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH,
366 scanResult);
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700367 } else {
Wei Wang0d0df3c2014-07-30 15:19:08 -0700368 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST,
369 scanResult);
Prerepa Viswanadhamd5324e42014-08-07 09:44:20 -0700370 }
371 }
372 });
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700373 }
Wei Wang6d811182014-05-22 12:10:25 -0700374 }
375
376 private void postCallbackError(final ScanCallback callback, final int errorCode) {
377 mHandler.post(new Runnable() {
Wei Wangb661bb72014-08-18 16:08:00 -0700378 @Override
Wei Wang6d811182014-05-22 12:10:25 -0700379 public void run() {
380 callback.onScanFailed(errorCode);
381 }
382 });
383 }
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700384
385 private boolean isSettingsConfigAllowedForScan(ScanSettings settings) {
Wei Wang685c17582014-07-16 22:02:03 -0700386 if (mBluetoothAdapter.isOffloadedFilteringSupported()) {
387 return true;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700388 }
Wei Wang685c17582014-07-16 22:02:03 -0700389 final int callbackType = settings.getCallbackType();
390 // Only support regular scan if no offloaded filter support.
391 if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
392 && settings.getReportDelayMillis() == 0) {
393 return true;
394 }
395 return false;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -0700396 }
Wei Wang6d811182014-05-22 12:10:25 -0700397}