From 2f7eb9ef23565c91c617b80541bfac4a18ff62b6 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 27 Nov 2016 02:41:52 +0100 Subject: [PATCH] Some more improvements to discovery - pass service uuids to GBDeviceCandaidate so that DeviceCoordinators can detect devices by their services. Note: they should not rely on service uuids being available --- .../activities/DiscoveryActivity.java | 40 +++++++++++++-- .../devices/miband/MiBand2Coordinator.java | 5 ++ .../devices/miband/MiBandCoordinator.java | 1 + .../gadgetbridge/impl/GBDeviceCandidate.java | 50 ++++++++++++++++--- .../gadgetbridge/util/AndroidUtils.java | 15 ++++++ .../gadgetbridge/util/DeviceHelper.java | 2 +- 6 files changed, 101 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java index bb07ad63..579f8234 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -8,6 +8,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.BroadcastReceiver; @@ -42,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -95,6 +97,14 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC handleDeviceFound(device, rssi); break; } + case BluetoothDevice.ACTION_UUID: { + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, GBDevice.RSSI_UNKNOWN); + Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID); + ParcelUuid[] uuids2 = AndroidUtils.toParcelUUids(uuids); + handleDeviceFound(device, rssi, uuids2); + break; + } case BluetoothDevice.ACTION_BOND_STATE_CHANGED: { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device != null && device.getAddress().equals(bondingAddress)) { @@ -131,10 +141,18 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); try { + ScanRecord scanRecord = result.getScanRecord(); + ParcelUuid[] uuids = null; + if (scanRecord != null) { + //logMessageContent(scanRecord.getBytes()); + List serviceUuids = scanRecord.getServiceUuids(); + if (serviceUuids != null) { + uuids = serviceUuids.toArray(new ParcelUuid[0]); + } + } LOG.warn(result.getDevice().getName() + ": " + - ((result.getScanRecord() != null) ? result.getScanRecord().getBytes().length : -1)); - //logMessageContent(result.getScanRecord().getBytes()); - handleDeviceFound(result.getDevice(), (short) result.getRssi()); + ((scanRecord != null) ? scanRecord.getBytes().length : -1)); + handleDeviceFound(result.getDevice(), (short) result.getRssi(), uuids); } catch (NullPointerException e) { LOG.warn("Error handling scan result", e); } @@ -199,6 +217,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC IntentFilter bluetoothIntents = new IntentFilter(); bluetoothIntents.addAction(BluetoothDevice.ACTION_FOUND); + bluetoothIntents.addAction(BluetoothDevice.ACTION_UUID); bluetoothIntents.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); bluetoothIntents.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); bluetoothIntents.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); @@ -247,9 +266,20 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC } private void handleDeviceFound(BluetoothDevice device, short rssi) { + ParcelUuid[] uuids = device.getUuids(); + if (uuids == null) { + if (device.fetchUuidsWithSdp()) { + return; + } + } + + handleDeviceFound(device, rssi, uuids); + } + + + private void handleDeviceFound(BluetoothDevice device, short rssi, ParcelUuid[] uuids) { LOG.debug("found device: " + device.getName() + ", " + device.getAddress()); if (LOG.isDebugEnabled()) { - ParcelUuid[] uuids = device.getUuids(); if (uuids != null && uuids.length > 0) { for (ParcelUuid uuid : uuids) { LOG.debug(" supports uuid: " + uuid.toString()); @@ -260,7 +290,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC return; // ignore already bonded devices } - GBDeviceCandidate candidate = new GBDeviceCandidate(device, rssi); + GBDeviceCandidate candidate = new GBDeviceCandidate(device, rssi, uuids); DeviceType deviceType = DeviceHelper.getInstance().getSupportedType(candidate); if (deviceType.isSupported()) { candidate.setDeviceType(deviceType); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java index 5ce4eddf..12c945e0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java @@ -40,8 +40,13 @@ public class MiBand2Coordinator extends MiBandCoordinator { return Collections.singletonList(filter); } + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { + if (candidate.supportsService(MiBand2Service.UUID_SERVICE_MIBAND2_SERVICE)) { + return DeviceType.MIBAND2; + } + // and a heuristic for now try { BluetoothDevice device = candidate.getDevice(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java index c44a3321..3597706c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java @@ -49,6 +49,7 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { return Collections.singletonList(filter); } + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { String macAddress = candidate.getMacAddress().toUpperCase(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java index bb9d7fae..4fc25f62 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java @@ -5,17 +5,22 @@ import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; /** * A device candidate is a Bluetooth device that is not yet managed by @@ -27,21 +32,25 @@ public class GBDeviceCandidate implements Parcelable { private final BluetoothDevice device; private final short rssi; + private final ParcelUuid[] serviceUuds; private DeviceType deviceType = DeviceType.UNKNOWN; - public GBDeviceCandidate(BluetoothDevice device, short rssi) { + public GBDeviceCandidate(BluetoothDevice device, short rssi, ParcelUuid[] serviceUuds) { this.device = device; this.rssi = rssi; + this.serviceUuds = mergeServiceUuids(serviceUuds, device.getUuids()); } private GBDeviceCandidate(Parcel in) { device = in.readParcelable(getClass().getClassLoader()); - rssi = (short) in.readInt(); - deviceType = DeviceType.valueOf(in.readString()); - if (device == null) { throw new IllegalStateException("Unable to read state from Parcel"); } + rssi = (short) in.readInt(); + deviceType = DeviceType.valueOf(in.readString()); + + ParcelUuid[] uuids = AndroidUtils.toParcelUUids(in.readParcelableArray(getClass().getClassLoader())); + serviceUuds = mergeServiceUuids(uuids, device.getUuids()); } @Override @@ -49,8 +58,21 @@ public class GBDeviceCandidate implements Parcelable { dest.writeParcelable(device, 0); dest.writeInt(rssi); dest.writeString(deviceType.name()); + dest.writeArray(serviceUuds); } + public static final Creator CREATOR = new Creator() { + @Override + public GBDeviceCandidate createFromParcel(Parcel in) { + return new GBDeviceCandidate(in); + } + + @Override + public GBDeviceCandidate[] newArray(int size) { + return new GBDeviceCandidate[size]; + } + }; + public BluetoothDevice getDevice() { return device; } @@ -67,9 +89,25 @@ public class GBDeviceCandidate implements Parcelable { return device != null ? device.getAddress() : GBApplication.getContext().getString(R.string._unknown_); } + private ParcelUuid[] mergeServiceUuids(ParcelUuid[] serviceUuds, ParcelUuid[] deviceUuids) { + Set uuids = new HashSet<>(); + if (serviceUuds != null) { + uuids.addAll(Arrays.asList(serviceUuds)); + } + if (deviceUuids != null) { + uuids.addAll(Arrays.asList(deviceUuids)); + } + return uuids.toArray(new ParcelUuid[0]); + } + + @NonNull + public ParcelUuid[] getServiceUuids() { + return serviceUuds; + } + public boolean supportsService(UUID aService) { - ParcelUuid[] uuids = device.getUuids(); - if (uuids == null) { + ParcelUuid[] uuids = getServiceUuids(); + if (uuids.length == 0) { LOG.warn("no cached services available for " + this); return false; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java new file mode 100644 index 00000000..f08691e2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java @@ -0,0 +1,15 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +import android.os.ParcelUuid; +import android.os.Parcelable; + +public class AndroidUtils { + public static ParcelUuid[] toParcelUUids(Parcelable[] uuids) { + if (uuids == null) { + return null; + } + ParcelUuid[] uuids2 = new ParcelUuid[uuids.length]; + System.arraycopy(uuids, 0, uuids2, 0, uuids.length); + return uuids2; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index 19078571..df4a76f8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -120,7 +120,7 @@ public class DeviceHelper { } public GBDevice toSupportedDevice(BluetoothDevice device) { - GBDeviceCandidate candidate = new GBDeviceCandidate(device, GBDevice.RSSI_UNKNOWN); + GBDeviceCandidate candidate = new GBDeviceCandidate(device, GBDevice.RSSI_UNKNOWN, device.getUuids()); for (DeviceCoordinator coordinator : getAllCoordinators()) { if (coordinator.supports(candidate)) {