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
This commit is contained in:
cpfeiffer 2016-11-27 02:41:52 +01:00
parent b9ff2cd468
commit 2f7eb9ef23
6 changed files with 101 additions and 12 deletions

View File

@ -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<ParcelUuid> 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);

View File

@ -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();

View File

@ -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();

View File

@ -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<GBDeviceCandidate> CREATOR = new Creator<GBDeviceCandidate>() {
@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<ParcelUuid> 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;
}

View File

@ -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;
}
}

View File

@ -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)) {