Some more Mi Band pairing improvements #180

- listen to notifications early -- the band then actually tells us that
  authentication is required
- check for this after sending user info
- add authentication states to GBDevice
- workaround for event problems in pairing activity (delivered although
  already unregistered)
- BtLEQueue now deals with gatt events coming *before* connectGatt()
  actually returned (namely the connection event)
here
cpfeiffer 2016-02-13 00:09:35 +01:00
parent 8294921de7
commit c86365ee2e
7 changed files with 99 additions and 14 deletions

View File

@ -1,7 +1,7 @@
###Changelog
####Version (next)
* Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it deems appropriate, there is no action to perform on the watch for this to happen.
* Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it deems appropriate, there is no action to perform on the watch for this to happen.* Mi Band: improvements to pairing
####Version 0.7.4
* Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved.

View File

@ -159,6 +159,12 @@ public class MiBandPairingActivity extends Activity {
}
private void pairingFinished(boolean pairedSuccessfully) {
LOG.debug("pairingFinished: " + pairedSuccessfully);
if (!isPairing) {
// already gone?
return;
}
isPairing = false;
LocalBroadcastManager.getInstance(this).unregisterReceiver(mPairingReceiver);
unregisterReceiver(mBondingReceiver);
@ -188,12 +194,13 @@ public class MiBandPairingActivity extends Activity {
bondingMacAddress = device.getAddress();
if (bondState == BluetoothDevice.BOND_BONDING) {
LOG.info("Bonding in progress: " + device.getAddress());
GB.toast(this, "Bonding in progress: " + bondingMacAddress, Toast.LENGTH_LONG, GB.INFO);
return;
}
GB.toast(this, "Creating bond with" + bondingMacAddress, Toast.LENGTH_LONG, GB.INFO);
if (!device.createBond()) {
GB.toast(this, "Unable to pair with " + device.getAddress(), Toast.LENGTH_LONG, GB.ERROR);
GB.toast(this, "Unable to pair with " + bondingMacAddress, Toast.LENGTH_LONG, GB.ERROR);
}
}

View File

@ -203,6 +203,10 @@ public class GBDevice implements Parcelable {
return GBApplication.getContext().getString(R.string.connected);
case INITIALIZING:
return GBApplication.getContext().getString(R.string.initializing);
case AUTHENTICATION_REQUIRED:
return GBApplication.getContext().getString(R.string.authentication_required);
case AUTHENTICATING:
return GBApplication.getContext().getString(R.string.authenticating);
case INITIALIZED:
return GBApplication.getContext().getString(R.string.initialized);
}
@ -333,6 +337,8 @@ public class GBDevice implements Parcelable {
CONNECTING,
CONNECTED,
INITIALIZING,
AUTHENTICATION_REQUIRED, // some kind of pairing is required by the device
AUTHENTICATING, // some kind of pairing is requested by the device
/**
* Means that the device is connected AND all the necessary initialization steps
* have been performed. At the very least, this means that basic information like

View File

@ -271,8 +271,8 @@ public final class BtLEQueue {
}
private boolean checkCorrectGattInstance(BluetoothGatt gatt, String where) {
if (gatt != mBluetoothGatt) {
LOG.info("Ignoring event from wrong BluetoothGatt instance: " + where);
if (gatt != mBluetoothGatt && mBluetoothGatt != null) {
LOG.info("Ignoring event from wrong BluetoothGatt instance: " + where + "; " + gatt);
return false;
}
return true;
@ -319,7 +319,7 @@ public final class BtLEQueue {
setDeviceConnectionState(State.CONNECTED);
// Attempts to discover services after successful connection.
LOG.info("Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
gatt.discoverServices());
break;
case BluetoothProfile.STATE_DISCONNECTED:
LOG.info("Disconnected from GATT server.");

View File

@ -0,0 +1,27 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
import android.bluetooth.BluetoothGatt;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.PlainAction;
public class CheckAuthenticationNeededAction extends PlainAction {
private final GBDevice mDevice;
public CheckAuthenticationNeededAction(GBDevice device) {
super();
mDevice = device;
}
@Override
public boolean run(BluetoothGatt gatt) {
// the state is set in MiBandSupport.handleNotificationNotif()
switch (mDevice.getState()) {
case AUTHENTICATION_REQUIRED: // fall through
case AUTHENTICATING:
return false; // abort the whole thing
default:
return true;
}
}
}

View File

@ -31,6 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile;
import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
@ -94,12 +95,14 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext()));
pair(builder)
enableNotifications(builder, true)
.pair(builder)
.requestDeviceInfo(builder)
.sendUserInfo(builder)
.checkAuthenticationNeeded(builder, getDevice())
.setWearLocation(builder)
.setFitnessGoal(builder)
.enableNotifications(builder, true)
.enableFurtherNotifications(builder, true)
.setCurrentTime(builder)
.requestBatteryInfo(builder)
.setInitialized(builder);
@ -107,6 +110,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
return builder;
}
private MiBandSupport checkAuthenticationNeeded(TransactionBuilder builder, GBDevice device) {
builder.add(new CheckAuthenticationNeededAction(device));
return this;
}
/**
* Last action of initialization sequence. Sets the device to initialized.
* It is only invoked if all other actions were successfully run, so the device
@ -120,8 +128,12 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
// TODO: tear down the notifications on quit
private MiBandSupport enableNotifications(TransactionBuilder builder, boolean enable) {
builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_NOTIFICATION), enable)
.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS), enable)
builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_NOTIFICATION), enable);
return this;
}
private MiBandSupport enableFurtherNotifications(TransactionBuilder builder, boolean enable) {
builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS), enable)
.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_ACTIVITY_DATA), enable)
.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_BATTERY), enable)
.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable);
@ -685,6 +697,26 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
return;
}
switch (value[0]) {
case MiBandService.NOTIFY_AUTHENTICATION_FAILED:
// we get first FAILED, then NOTIFY_STATUS_MOTOR_AUTH (0x13)
// which means, we need to authenticate by tapping
getDevice().setState(State.AUTHENTICATION_REQUIRED);
getDevice().sendDeviceUpdateIntent(getContext());
GB.toast(getContext(), "Band needs pairing", Toast.LENGTH_LONG, GB.ERROR);
break;
case MiBandService.NOTIFY_AUTHENTICATION_SUCCESS: // fall through -- not sure which one we get
case MiBandService.NOTIFY_STATUS_MOTOR_AUTH_SUCCESS:
LOG.info("Band successfully authenticated");
// maybe we can perform the rest of the initialization from here
doInitialize();
break;
case MiBandService.NOTIFY_STATUS_MOTOR_AUTH:
LOG.info("Band needs authentication (MOTOR_AUTH)");
getDevice().setState(State.AUTHENTICATING);
getDevice().sendDeviceUpdateIntent(getContext());
break;
default:
for (byte b : value) {
LOG.warn("DATA: " + String.format("0x%2x", b));
@ -692,6 +724,15 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
}
}
private void doInitialize() {
try {
TransactionBuilder builder = performInitialized("just initializing after authentication");
builder.queue(getQueue());
} catch (IOException ex) {
LOG.error("Unable to initialize device after authentication", ex);
}
}
private void handleDeviceInfo(byte[] value, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
mDeviceInfo = new DeviceInfo(value);
@ -763,10 +804,12 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
}
private void handleUserInfoResult(byte[] value, int status) {
// successfully transfered user info means we're initialized
if (status == BluetoothGatt.GATT_SUCCESS) {
setConnectionState(State.INITIALIZED);
}
// successfully transferred user info means we're initialized
// commented out, because we have SetDeviceStateAction which sets initialized
// state on every successful initialization.
// if (status == BluetoothGatt.GATT_SUCCESS) {
// setConnectionState(State.INITIALIZED);
// }
}
private void setConnectionState(State newState) {

View File

@ -221,5 +221,7 @@
<string name="activity_prefs_weight_kg">Weight in kg</string>
<string name="appmanager_health_activate">Activate</string>
<string name="appmanager_health_deactivate">Deactivate</string>
<string name="authenticating">authenticating</string>
<string name="authentication_required">authentication required</string>
</resources>