1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-21 20:40:27 +02:00

Huami/Zepp OS: Improve reconnection

- Remove notification on unneeded characteristics for Zepp OS devices
- Reset MTU before initializing device, since the support class is
  reused when reconnecting, and keeping the previous high MTU before
  renegotiating again can make the initialization fail sometimes
  (band will never reply)
- If any of the chunked characteristics is null during initialization,
  mark the device as waiting for reconnect, which will make it retry the
  connection later with a backoff delay.
This commit is contained in:
José Rebelo 2023-07-22 20:31:26 +01:00
parent aa87c5abeb
commit 247a954920
3 changed files with 42 additions and 12 deletions

View File

@ -23,9 +23,9 @@ import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothGattService;
import android.location.Location;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -38,8 +38,6 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.Logging; import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Reminder;
import nodomain.freeyourgadget.gadgetbridge.model.WorldClock;
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.CheckInitializedAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.CheckInitializedAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile;
@ -56,6 +54,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBlePro
* @see BtLEQueue * @see BtLEQueue
*/ */
public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport implements GattCallback, GattServerCallback { public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport implements GattCallback, GattServerCallback {
private static final Logger LOG = LoggerFactory.getLogger(AbstractBTLEDeviceSupport.class);
private BtLEQueue mQueue; private BtLEQueue mQueue;
private Map<UUID, BluetoothGattCharacteristic> mAvailableCharacteristics; private Map<UUID, BluetoothGattCharacteristic> mAvailableCharacteristics;
private final Set<UUID> mSupportedServices = new HashSet<>(4); private final Set<UUID> mSupportedServices = new HashSet<>(4);
@ -136,11 +136,13 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im
*/ */
public TransactionBuilder performInitialized(String taskName) throws IOException { public TransactionBuilder performInitialized(String taskName) throws IOException {
if (!isConnected()) { if (!isConnected()) {
LOG.debug("Connecting to device for {}", taskName);
if (!connect()) { if (!connect()) {
throw new IOException("1: Unable to connect to device: " + getDevice()); throw new IOException("1: Unable to connect to device: " + getDevice());
} }
} }
if (!isInitialized()) { if (!isInitialized()) {
LOG.debug("Initializing device for {}", taskName);
// first, add a transaction that performs device initialization // first, add a transaction that performs device initialization
TransactionBuilder builder = createTransactionBuilder("Initialize device"); TransactionBuilder builder = createTransactionBuilder("Initialize device");
builder.add(new CheckInitializedAction(gbDevice)); builder.add(new CheckInitializedAction(gbDevice));

View File

@ -720,11 +720,16 @@ public abstract class Huami2021Support extends HuamiSupport implements ZeppOsFil
return this; return this;
} }
@Override
public HuamiSupport enableNotifications(final TransactionBuilder builder, final boolean enable) {
builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER_2021_READ), enable);
return this;
}
@Override @Override
public Huami2021Support enableFurtherNotifications(final TransactionBuilder builder, public Huami2021Support enableFurtherNotifications(final TransactionBuilder builder,
final boolean enable) { final boolean enable) {
builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER_2021_READ), enable); // Nothing to do here, they are already enabled from enableNotifications
return this; return this;
} }

View File

@ -333,7 +333,10 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
protected boolean isMusicAppStarted = false; protected boolean isMusicAppStarted = false;
protected MediaManager mediaManager; protected MediaManager mediaManager;
private boolean heartRateNotifyEnabled; private boolean heartRateNotifyEnabled;
private int mMTU = 23; private static final int MIN_MTU = 23;
private int mMTU = MIN_MTU;
// Keep track of the previous MTU before reconnection, so that we can request it after reconnection
private int previousMtu = -1;
protected int mActivitySampleSize = 4; protected int mActivitySampleSize = 4;
protected Huami2021ChunkedEncoder huami2021ChunkedEncoder; protected Huami2021ChunkedEncoder huami2021ChunkedEncoder;
@ -371,6 +374,12 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
@Override @Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) { protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
if (getMTU() != MIN_MTU) {
// Reset the MTU before re-initializing the device, otherwise initialization will sometimes fail
previousMtu = getMTU();
setMtu(MIN_MTU);
}
try { try {
byte authFlags = getAuthFlags(); byte authFlags = getAuthFlags();
byte cryptFlags = getCryptFlags(); byte cryptFlags = getCryptFlags();
@ -385,8 +394,13 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
if (characteristicChunked2021Write != null && huami2021ChunkedEncoder == null) { if (characteristicChunked2021Write != null && huami2021ChunkedEncoder == null) {
huami2021ChunkedEncoder = new Huami2021ChunkedEncoder(characteristicChunked2021Write, force2021Protocol(), mMTU); huami2021ChunkedEncoder = new Huami2021ChunkedEncoder(characteristicChunked2021Write, force2021Protocol(), mMTU);
} }
if (characteristicChunked2021Write != null && force2021Protocol()) { if (force2021Protocol()) {
new InitOperation2021(authenticate, authFlags, cryptFlags, this, builder, huami2021ChunkedEncoder, huami2021ChunkedDecoder).perform(); if (characteristicChunked2021Write != null && characteristicChunked2021Read != null) {
new InitOperation2021(authenticate, authFlags, cryptFlags, this, builder, huami2021ChunkedEncoder, huami2021ChunkedDecoder).perform();
} else {
LOG.warn("Chunked 2021 characteristics are null, will attempt to reconnect");
builder.add(new SetDeviceStateAction(getDevice(), State.WAITING_FOR_RECONNECT, getContext()));
}
} else { } else {
new InitOperation(authenticate, authFlags, cryptFlags, this, builder).perform(); new InitOperation(authenticate, authFlags, cryptFlags, this, builder).perform();
} }
@ -510,7 +524,6 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
// TODO: tear down the notifications on quit // TODO: tear down the notifications on quit
public HuamiSupport enableNotifications(TransactionBuilder builder, boolean enable) { public HuamiSupport enableNotifications(TransactionBuilder builder, boolean enable) {
builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_NOTIFICATION), enable); builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_NOTIFICATION), enable);
builder.notify(getCharacteristic(GattService.UUID_SERVICE_CURRENT_TIME), enable);
// Notify CHARACTERISTIC9 to receive random auth code // Notify CHARACTERISTIC9 to receive random auth code
builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_AUTH), enable); builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_AUTH), enable);
if (characteristicChunked2021Read != null) { if (characteristicChunked2021Read != null) {
@ -4116,6 +4129,13 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
public void phase2Initialize(TransactionBuilder builder) { public void phase2Initialize(TransactionBuilder builder) {
LOG.info("phase2Initialize..."); LOG.info("phase2Initialize...");
if (previousMtu > MIN_MTU) {
// We're reconnecting - request the previously set MTU
builder.requestMtu(previousMtu);
previousMtu = -1;
}
requestBatteryInfo(builder); requestBatteryInfo(builder);
} }
@ -4171,13 +4191,12 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
} }
protected void setMtu(final int mtu) { protected void setMtu(final int mtu) {
final Prefs prefs = getDevicePrefs(); if (mtu > MIN_MTU && !allowHighMtu()) {
if (!prefs.getBoolean(PREF_ALLOW_HIGH_MTU, true)) {
LOG.warn("High MTU is not allowed, ignoring"); LOG.warn("High MTU is not allowed, ignoring");
return; return;
} }
if (mtu < 23) { if (mtu < MIN_MTU) {
LOG.error("Device announced unreasonable low MTU of {}, ignoring", mtu); LOG.error("Device announced unreasonable low MTU of {}, ignoring", mtu);
return; return;
} }
@ -4188,6 +4207,10 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
} }
} }
protected boolean allowHighMtu() {
return getDevicePrefs().getBoolean(PREF_ALLOW_HIGH_MTU, true);
}
public int getActivitySampleSize() { public int getActivitySampleSize() {
return mActivitySampleSize; return mActivitySampleSize;
} }