discovery: android ble driver
* Updated Reader to handle base/mask UUID.
* Changed to use a timer only for connection
* Changed to use close instead of disconnect to reduce race
between cancel and late callback calls.
Change-Id: Ie455aea7544b478f6f5574c795fc787534350241
diff --git a/android-lib/src/main/java/io/v/android/impl/google/discovery/plugins/ble/Driver.java b/android-lib/src/main/java/io/v/android/impl/google/discovery/plugins/ble/Driver.java
index 1a8a741..c867b42 100644
--- a/android-lib/src/main/java/io/v/android/impl/google/discovery/plugins/ble/Driver.java
+++ b/android-lib/src/main/java/io/v/android/impl/google/discovery/plugins/ble/Driver.java
@@ -323,7 +323,13 @@
private synchronized void startScanning() {
mScanSeens = new HashMap<>();
- mGattReader = new GattReader(mContext, mScanUuids, this);
+ mGattReader =
+ new GattReader(
+ mContext,
+ mScanUuids,
+ mScanBaseUuid.getUuid(),
+ mScanMaskUuid.getUuid(),
+ this);
synchronized (Driver.class) {
if (sClassicScanEnabled) {
// Note that BluetoothLeScan will be started when BluetoothScan finishes.
diff --git a/android-lib/src/main/java/io/v/android/impl/google/discovery/plugins/ble/GattReader.java b/android-lib/src/main/java/io/v/android/impl/google/discovery/plugins/ble/GattReader.java
index 0099475..886f705 100644
--- a/android-lib/src/main/java/io/v/android/impl/google/discovery/plugins/ble/GattReader.java
+++ b/android-lib/src/main/java/io/v/android/impl/google/discovery/plugins/ble/GattReader.java
@@ -10,6 +10,7 @@
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
+import android.os.Build;
import android.util.Log;
import com.google.common.collect.Queues;
@@ -50,33 +51,33 @@
private final ScheduledThreadPoolExecutor mExecutor;
private final Set<UUID> mScanUuids;
+ private final UUID mScanBaseUuid, mScanMaskUuid;
private final Handler mHandler;
private final ArrayDeque<BluetoothDevice> mPendingReads;
- private final Runnable mCancelTask;
-
private BluetoothDevice mCurrentDevice;
private BluetoothGatt mCurrentGatt;
- private ScheduledFuture mCurrentGattTimeout;
+ private ScheduledFuture mCurrentGattConnectionTimeout;
private BluetoothGattService mCurrentService;
private Iterator<BluetoothGattService> mCurrentServiceIterator;
private Iterator<BluetoothGattCharacteristic> mCurrentCharacteristicIterator;
- GattReader(Context context, Set<UUID> scanUuids, Handler handler) {
+ /**
+ * Creates a new Gatt reader.
+ * <p/>
+ *
+ * An empty uuids means all Vanadium services and baseUuid and maskUuid will be used to
+ * filter Vanadium services.
+ */
+ GattReader(Context context, Set<UUID> uuids, UUID baseUuid, UUID maskUuid, Handler handler) {
mContext = context;
mExecutor = new ScheduledThreadPoolExecutor(1);
- mScanUuids = scanUuids;
+ mScanUuids = uuids;
+ mScanBaseUuid = baseUuid;
+ mScanMaskUuid = maskUuid;
mHandler = handler;
mPendingReads = Queues.newArrayDeque();
- mCancelTask =
- new Runnable() {
- @Override
- public void run() {
- Log.e(TAG, "gatt operation timed out: " + mCurrentDevice);
- cancelAndMaybeReadNextDevice();
- }
- };
}
/**
@@ -105,7 +106,7 @@
private synchronized void maybeReadNextDevice() {
mCurrentGatt = null;
- mCurrentGattTimeout = null;
+ mCurrentGattConnectionTimeout = null;
mCurrentService = null;
mCurrentServiceIterator = null;
mCurrentCharacteristicIterator = null;
@@ -116,19 +117,28 @@
}
mCurrentGatt = mCurrentDevice.connectGatt(mContext, false, this);
- mCurrentGattTimeout =
- mExecutor.schedule(mCancelTask, GATT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ mCurrentGattConnectionTimeout =
+ mExecutor.schedule(
+ new Runnable() {
+ @Override
+ public void run() {
+ Log.e(TAG, "gatt connection timed out: " + mCurrentDevice);
+ cancelAndMaybeReadNextDevice();
+ }
+ },
+ GATT_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS);
}
private synchronized void finishAndMaybeReadNextDevice() {
- mCurrentGattTimeout.cancel(false);
- mCurrentGatt.disconnect();
+ mCurrentGattConnectionTimeout.cancel(false);
+ mCurrentGatt.close();
maybeReadNextDevice();
}
private synchronized void cancelAndMaybeReadNextDevice() {
- mCurrentGattTimeout.cancel(false);
+ mCurrentGattConnectionTimeout.cancel(false);
mCurrentGatt.close();
final BluetoothDevice device = mCurrentDevice;
@@ -143,16 +153,22 @@
}
@Override
- public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+ public synchronized void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
- if (newState != BluetoothGatt.STATE_CONNECTED) {
- // Connection is disconnected. Release it.
+ if (gatt != mCurrentGatt) {
+ // This must be for an old Gatt connection which has been already cancelled. Ignore it.
gatt.close();
return;
}
- if (status != BluetoothGatt.GATT_SUCCESS) {
- Log.e(TAG, "connectGatt failed: " + mCurrentDevice + " , status: " + status);
+ // Reset the connection timer.
+ if (!mCurrentGattConnectionTimeout.cancel(false)) {
+ // Already cancelled.
+ return;
+ }
+
+ if (status != BluetoothGatt.GATT_SUCCESS || newState != BluetoothGatt.STATE_CONNECTED) {
+ Log.e(TAG, "connection failed: " + mCurrentDevice + " , status: " + status);
cancelAndMaybeReadNextDevice();
return;
}
@@ -164,7 +180,8 @@
// It is not clear whether DEVICE_TYPE_DUAL is on a BR/EDR physical link, but
// it is safe to not exchange MTU for that type too.
int deviceType = mCurrentDevice.getType();
- if (deviceType != BluetoothDevice.DEVICE_TYPE_CLASSIC
+ if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP
+ && deviceType != BluetoothDevice.DEVICE_TYPE_CLASSIC
&& deviceType != BluetoothDevice.DEVICE_TYPE_DUAL) {
if (!gatt.requestMtu(MTU)) {
Log.e(TAG, "requestMtu failed: " + mCurrentDevice);
@@ -179,11 +196,11 @@
}
@Override
- public synchronized void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
+ public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
if (status != BluetoothGatt.GATT_SUCCESS) {
- Log.w(TAG, "requestMtu failed: " + mCurrentDevice + ", status: " + status);
+ Log.w(TAG, "onMtuChanged failed: " + mCurrentDevice + ", status: " + status);
cancelAndMaybeReadNextDevice();
return;
}
@@ -195,11 +212,11 @@
}
@Override
- public synchronized void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status != BluetoothGatt.GATT_SUCCESS) {
- Log.e(TAG, "discoverServices failed: " + mCurrentDevice + ", status: " + status);
+ Log.e(TAG, "onServicesDiscovered failed: " + mCurrentDevice + ", status: " + status);
cancelAndMaybeReadNextDevice();
return;
}
@@ -208,22 +225,27 @@
maybeReadNextService();
}
+ private boolean isTargetService(UUID uuid) {
+ if (mScanUuids.contains(uuid)) {
+ return true;
+ }
+ return mScanUuids.isEmpty()
+ && (uuid.getMostSignificantBits() & mScanMaskUuid.getMostSignificantBits())
+ == mScanBaseUuid.getMostSignificantBits()
+ && (uuid.getLeastSignificantBits() & mScanMaskUuid.getLeastSignificantBits())
+ == mScanBaseUuid.getLeastSignificantBits();
+ }
+
private void maybeReadNextService() {
while (mCurrentServiceIterator.hasNext()) {
mCurrentService = mCurrentServiceIterator.next();
- if (mScanUuids.isEmpty() || mScanUuids.contains(mCurrentService.getUuid())) {
- // Reset the timer.
- if (!mCurrentGattTimeout.cancel(false)) {
- // Already cancelled.
- return;
- }
- mCurrentGattTimeout =
- mExecutor.schedule(mCancelTask, GATT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-
- mCurrentCharacteristicIterator = mCurrentService.getCharacteristics().iterator();
- maybeReadNextCharacteristic();
- return;
+ if (!isTargetService(mCurrentService.getUuid())) {
+ continue;
}
+
+ mCurrentCharacteristicIterator = mCurrentService.getCharacteristics().iterator();
+ maybeReadNextCharacteristic();
+ return;
}
// All services have been read. Finish the current device read.
@@ -231,12 +253,12 @@
}
@Override
- public synchronized void onCharacteristicRead(
+ public void onCharacteristicRead(
BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status != BluetoothGatt.GATT_SUCCESS) {
- Log.e(TAG, "readCharacteristic failed: " + mCurrentDevice + ", status: " + status);
+ Log.e(TAG, "onCharacteristicRead failed: " + mCurrentDevice + ", status: " + status);
cancelAndMaybeReadNextDevice();
return;
}