Remove BluetoothAdapterCallback. Simplify leScan Api
App does not need to explicitly register/unregister callback
bug 8599881
Change-Id: I18cfef14d7ddb344722945e657dcb959823b412b
diff --git a/api/current.txt b/api/current.txt
index ef00be7..4851c14 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4625,13 +4625,11 @@
method public boolean isEnabled();
method public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
method public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
- method public boolean registerCallback(android.bluetooth.BluetoothAdapterCallback);
method public boolean setName(java.lang.String);
method public boolean startDiscovery();
- method public boolean startLeScan();
- method public boolean startLeScan(java.util.UUID[]);
- method public void stopLeScan();
- method public boolean unRegisterCallback(android.bluetooth.BluetoothAdapterCallback);
+ method public boolean startLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
+ method public boolean startLeScan(java.util.UUID[], android.bluetooth.BluetoothAdapter.LeScanCallback);
+ method public void stopLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
field public static final java.lang.String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
field public static final java.lang.String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
@@ -4662,12 +4660,8 @@
field public static final int STATE_TURNING_ON = 11; // 0xb
}
- public abstract class BluetoothAdapterCallback {
- ctor public BluetoothAdapterCallback();
- method public void onCallbackRegistration(int);
- method public void onLeScan(android.bluetooth.BluetoothDevice, int, byte[]);
- field public static final int CALLBACK_REGISTERED = 0; // 0x0
- field public static final int CALLBACK_REGISTRATION_FAILURE = 1; // 0x1
+ public static abstract interface BluetoothAdapter.LeScanCallback {
+ method public abstract void onLeScan(android.bluetooth.BluetoothDevice, int, byte[]);
}
public class BluetoothAssignedNumbers {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2e9c9e3..3498bb8 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -20,7 +20,6 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.ParcelUuid;
@@ -30,11 +29,14 @@
import android.util.Pair;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.HashMap;
import java.util.LinkedList;
+import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
@@ -357,9 +359,7 @@
private final IBluetoothManager mManagerService;
private IBluetooth mService;
- private Handler mServiceRecordHandler;
- private BluetoothAdapterCallback mCallback;
- private int mClientIf;
+ private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients;
/**
* Get a handle to the default local Bluetooth adapter.
@@ -394,7 +394,7 @@
mService = managerService.registerAdapter(mManagerCallback);
} catch (RemoteException e) {Log.e(TAG, "", e);}
mManagerService = managerService;
- mServiceRecordHandler = null;
+ mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>();
}
/**
@@ -1409,72 +1409,38 @@
}
/**
- * Register an callback to receive async results, such as LE scan result.
+ * Callback interface used to deliver LE scan results.
*
- * <p>This is an asynchronous call. The callback
- * {@link BluetoothAdapterCallback#onCallbackRegistration}
- * is used to notify success or failure if the function returns true.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param callback BluetootAdapter callback handler that will receive asynchronous callbacks.
- * @return If true, the callback will be called to notify success or failure,
- * false on immediate error
+ * @see #startLeScan(LeScanCallback)
+ * @see #startLeScan(UUID[], LeScanCallback)
*/
- public boolean registerCallback(BluetoothAdapterCallback callback) {
- try {
- IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
- mCallback = callback;
- UUID uuid = UUID.randomUUID();
- if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
-
- iGatt.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
- return true;
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- return false;
- }
- }
-
- /**
- * Unregister the registered callback.
- */
- public boolean unRegisterCallback(BluetoothAdapterCallback callback) {
- if (callback != mCallback) return false;
- try {
- IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
-
- iGatt.unregisterClient(mClientIf);
- return true;
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- return false;
- }
+ public interface LeScanCallback {
+ /**
+ * Callback reporting an LE device found during a device scan initiated
+ * by the {@link BluetoothAdapter#startLeScan} function.
+ *
+ * @param device Identifies the remote device
+ * @param rssi The RSSI value for the remote device as reported by the
+ * Bluetooth hardware. 0 if no RSSI value is available.
+ * @param scanRecord The content of the advertisement record offered by
+ * the remote device.
+ */
+ public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
}
/**
* Starts a scan for Bluetooth LE devices.
*
* <p>Results of the scan are reported using the
- * {@link BluetoothAdapterCallback#onLeScan} callback.
+ * {@link LeScanCallback#onLeScan} callback.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
+ * @param callback the callback LE scan results are delivered
* @return true, if the scan was started successfully
*/
- public boolean startLeScan() {
- if (DBG) Log.d(TAG, "startLeScan()");
- if (mClientIf == 0) return false;
-
- try {
- IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
- iGatt.startScan(mClientIf, false);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- return false;
- }
-
- return true;
+ public boolean startLeScan(LeScanCallback callback) {
+ return startLeScan(null, callback);
}
/**
@@ -1482,155 +1448,281 @@
* advertise given services.
*
* <p>Devices which advertise all specified services are reported using the
- * {@link BluetoothAdapterCallback#onLeScan} callback.
+ * {@link LeScanCallback#onLeScan} callback.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param serviceUuids Array of services to look for
+ * @param callback the callback LE scan results are delivered
* @return true, if the scan was started successfully
*/
- public boolean startLeScan(UUID[] serviceUuids) {
- if (DBG) Log.d(TAG, "startLeScan() - with UUIDs");
- if (mClientIf == 0) return false;
+ public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) {
+ if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids);
- try {
- IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
- ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
- for(int i = 0; i != uuids.length; ++i) {
- uuids[i] = new ParcelUuid(serviceUuids[i]);
- }
- iGatt.startScanWithUuids(mClientIf, false, uuids);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
+ if (callback == null) {
+ if (DBG) Log.e(TAG, "startLeScan: null callback");
return false;
}
- return true;
+ synchronized(mLeScanClients) {
+ if (mLeScanClients.containsKey(callback)) {
+ if (DBG) Log.e(TAG, "LE Scan has already started");
+ return false;
+ }
+
+ try {
+ IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
+ UUID uuid = UUID.randomUUID();
+ GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids);
+
+ iGatt.registerClient(new ParcelUuid(uuid), wrapper);
+ if (wrapper.scanStarted()) {
+ mLeScanClients.put(callback, wrapper);
+ return true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+ return false;
}
/**
* Stops an ongoing Bluetooth LE device scan.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callback used to identify which scan to stop
+ * must be the same handle used to start the scan
*/
- public void stopLeScan() {
- if (DBG) Log.d(TAG, "stopScan()");
- if (mClientIf == 0) return;
-
- try {
- IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
- iGatt.stopScan(mClientIf, false);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
+ public void stopLeScan(LeScanCallback callback) {
+ if (DBG) Log.d(TAG, "stopLeScan()");
+ GattCallbackWrapper wrapper;
+ synchronized(mLeScanClients) {
+ wrapper = mLeScanClients.remove(callback);
+ if (wrapper == null) return;
}
+ wrapper.stopLeScan();
}
/**
* Bluetooth GATT interface callbacks
*/
- private final IBluetoothGattCallback mBluetoothGattCallback =
- new IBluetoothGattCallback.Stub() {
- /**
- * Application interface registered - app is ready to go
- */
- public void onClientRegistered(int status, int clientIf) {
- if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
- + " clientIf=" + clientIf);
- mClientIf = clientIf;
- mCallback.onCallbackRegistration(status == BluetoothGatt.GATT_SUCCESS ?
- BluetoothAdapterCallback.CALLBACK_REGISTERED :
- BluetoothAdapterCallback.CALLBACK_REGISTRATION_FAILURE);
- }
+ private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub {
+ private static final int LE_CALLBACK_REG_TIMEOUT = 2000;
+ private static final int LE_CALLBACK_REG_WAIT_COUNT = 5;
- public void onClientConnectionState(int status, int clientIf,
- boolean connected, String address) {
- // no op
- }
+ private final LeScanCallback mLeScanCb;
+ // mLeHandle 0: not registered
+ // -1: scan stopped
+ // >0: registered and scan started
+ private int mLeHandle;
+ private final UUID[] mScanFilter;
+ private WeakReference<BluetoothAdapter> mBluetoothAdapter;
- /**
- * Callback reporting an LE scan result.
- * @hide
- */
- public void onScanResult(String address, int rssi, byte[] advData) {
- if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+ public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter,
+ LeScanCallback leScanCb, UUID[] uuid) {
+ mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter);
+ mLeScanCb = leScanCb;
+ mScanFilter = uuid;
+ mLeHandle = 0;
+ }
- try {
- mCallback.onLeScan(getRemoteDevice(address), rssi, advData);
- } catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ public boolean scanStarted() {
+ boolean started = false;
+ synchronized(this) {
+ if (mLeHandle == -1) return false;
+
+ int count = 0;
+ // wait for callback registration and LE scan to start
+ while (mLeHandle == 0 && count < LE_CALLBACK_REG_WAIT_COUNT) {
+ try {
+ wait(LE_CALLBACK_REG_TIMEOUT);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Callback reg wait interrupted: " + e);
+ }
+ count++;
}
+ started = (mLeHandle > 0);
}
+ return started;
+ }
- public void onGetService(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid) {
- // no op
+ public void stopLeScan() {
+ synchronized(this) {
+ if (mLeHandle <= 0) {
+ Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
+ return;
+ }
+ BluetoothAdapter adapter = mBluetoothAdapter.get();
+ if (adapter != null) {
+ try {
+ IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt();
+ iGatt.stopScan(mLeHandle, false);
+ iGatt.unregisterClient(mLeHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to stop scan and unregister" + e);
+ }
+ } else {
+ Log.e(TAG, "stopLeScan, BluetoothAdapter is null");
+ }
+ mLeHandle = -1;
+ notifyAll();
}
+ }
- public void onGetIncludedService(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int inclSrvcType, int inclSrvcInstId,
- ParcelUuid inclSrvcUuid) {
- // no op
+ /**
+ * Application interface registered - app is ready to go
+ */
+ public void onClientRegistered(int status, int clientIf) {
+ if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status +
+ " clientIf=" + clientIf);
+ synchronized(this) {
+ if (mLeHandle == -1) {
+ if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled");
+ }
+
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ mLeHandle = clientIf;
+ IBluetoothGatt iGatt = null;
+ try {
+ BluetoothAdapter adapter = mBluetoothAdapter.get();
+ if (adapter != null) {
+ iGatt = adapter.getBluetoothManager().getBluetoothGatt();
+ if (mScanFilter == null) {
+ iGatt.startScan(mLeHandle, false);
+ } else {
+ ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length];
+ for(int i = 0; i != uuids.length; ++i) {
+ uuids[i] = new ParcelUuid(mScanFilter[i]);
+ }
+ iGatt.startScanWithUuids(mLeHandle, false, uuids);
+ }
+ } else {
+ Log.e(TAG, "onClientRegistered, BluetoothAdapter null");
+ mLeHandle = -1;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "fail to start le scan: " + e);
+ mLeHandle = -1;
+ }
+ if (mLeHandle == -1) {
+ // registration succeeded but start scan failed
+ if (iGatt != null) {
+ try {
+ iGatt.unregisterClient(mLeHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "fail to unregister callback: " + mLeHandle +
+ " error: " + e);
+ }
+ }
+ }
+ } else {
+ // registration failed
+ mLeHandle = -1;
+ }
+ notifyAll();
}
+ }
- public void onGetCharacteristic(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int charProps) {
- // no op
+ public void onClientConnectionState(int status, int clientIf,
+ boolean connected, String address) {
+ // no op
+ }
+
+ /**
+ * Callback reporting an LE scan result.
+ * @hide
+ */
+ public void onScanResult(String address, int rssi, byte[] advData) {
+ if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+
+ // Check null in case the scan has been stopped
+ synchronized(this) {
+ if (mLeHandle <= 0) return;
}
-
- public void onGetDescriptor(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- ParcelUuid descUuid) {
- // no op
+ try {
+ BluetoothAdapter adapter = mBluetoothAdapter.get();
+ if (adapter == null) {
+ Log.d(TAG, "onScanResult, BluetoothAdapter null");
+ return;
+ }
+ mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
}
+ }
- public void onSearchComplete(String address, int status) {
- // no op
- }
+ public void onGetService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid) {
+ // no op
+ }
- public void onCharacteristicRead(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid, byte[] value) {
- // no op
- }
+ public void onGetIncludedService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int inclSrvcType, int inclSrvcInstId,
+ ParcelUuid inclSrvcUuid) {
+ // no op
+ }
- public void onCharacteristicWrite(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid) {
- // no op
- }
+ public void onGetCharacteristic(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int charProps) {
+ // no op
+ }
- public void onNotify(String address, int srvcType,
+ public void onGetDescriptor(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descUuid) {
+ // no op
+ }
+
+ public void onSearchComplete(String address, int status) {
+ // no op
+ }
+
+ public void onCharacteristicRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid, byte[] value) {
+ // no op
+ }
+
+ public void onCharacteristicWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid) {
+ // no op
+ }
+
+ public void onNotify(String address, int srvcType,
int srvcInstId, ParcelUuid srvcUuid,
int charInstId, ParcelUuid charUuid,
byte[] value) {
- // no op
- }
+ // no op
+ }
- public void onDescriptorRead(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- ParcelUuid descrUuid, byte[] value) {
- // no op
- }
+ public void onDescriptorRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descrUuid, byte[] value) {
+ // no op
+ }
- public void onDescriptorWrite(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- ParcelUuid descrUuid) {
- // no op
- }
+ public void onDescriptorWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descrUuid) {
+ // no op
+ }
- public void onExecuteWrite(String address, int status) {
- // no op
- }
+ public void onExecuteWrite(String address, int status) {
+ // no op
+ }
- public void onReadRemoteRssi(String address, int rssi, int status) {
- // no op
- }
- };
+ public void onReadRemoteRssi(String address, int rssi, int status) {
+ // no op
+ }
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothAdapterCallback.java b/core/java/android/bluetooth/BluetoothAdapterCallback.java
deleted file mode 100644
index a726bc9..0000000
--- a/core/java/android/bluetooth/BluetoothAdapterCallback.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.bluetooth.BluetoothDevice;
-
-/**
- * This abstract class is used to implement {@link BluetoothAdapter} callbacks.
- */
-public abstract class BluetoothAdapterCallback {
-
- /**
- * Indicates the callback has been registered successfully
- */
- public static final int CALLBACK_REGISTERED = 0;
-
- /**
- * Indicates the callback registration has failed
- */
- public static final int CALLBACK_REGISTRATION_FAILURE = 1;
-
- /**
- * Callback to inform change in registration state of the application.
- *
- * @param status Returns {@link #CALLBACK_REGISTERED} if the application
- * was successfully registered.
- */
- public void onCallbackRegistration(int status) {
- }
-
- /**
- * Callback reporting an LE device found during a device scan initiated
- * by the {@link BluetoothAdapter#startLeScan} function.
- *
- * @param device Identifies the remote device
- * @param rssi The RSSI value for the remote device as reported by the
- * Bluetooth hardware. 0 if no RSSI value is available.
- * @param scanRecord The content of the advertisement record offered by
- * the remote device.
- */
- public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
- }
-}
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 19083b5..172f3bc 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -127,7 +127,7 @@
try {
IBluetoothManager managerService = mAdapter.getBluetoothManager();
- IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+ IBluetoothGatt iGatt = managerService.getBluetoothGatt();
if (iGatt == null) return connectedDevices;
connectedDevices = iGatt.getDevicesMatchingConnectionStates(
@@ -172,7 +172,7 @@
try {
IBluetoothManager managerService = mAdapter.getBluetoothManager();
- IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+ IBluetoothGatt iGatt = managerService.getBluetoothGatt();
if (iGatt == null) return devices;
devices = iGatt.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
@@ -203,7 +203,7 @@
try {
IBluetoothManager managerService = mAdapter.getBluetoothManager();
- IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+ IBluetoothGatt iGatt = managerService.getBluetoothGatt();
if (iGatt == null) {
Log.e(TAG, "Fail to get GATT Server connection");
return null;