From 443594db8c7231e57a5d4e6df08cd8cc24d5cb59 Mon Sep 17 00:00:00 2001 From: Thomas Kuehne Date: Sun, 29 Jun 2025 00:00:00 +0200 Subject: [PATCH] add CompanionDeviceService initiated re-connection (#5023) --- .../BluetoothConnectReceiver.java | 53 ++++++++++------- .../service/GBCompanionDeviceService.java | 58 ++++++++++--------- 2 files changed, 65 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java index d65a167516..0c4d9b374e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2024 Andreas Shimokawa, Daniel Dakhno, José Rebelo +/* Copyright (C) 2016-2025 Andreas Shimokawa, Daniel Dakhno, José Rebelo, Thomas Kuehne This file is part of Gadgetbridge. @@ -16,6 +16,9 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.externalevents; +import static nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State.WAITING_FOR_RECONNECT; +import static nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State.WAITING_FOR_SCAN; + import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; @@ -26,6 +29,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; @@ -33,10 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; public class BluetoothConnectReceiver extends BroadcastReceiver { private static final Logger LOG = LoggerFactory.getLogger(BluetoothConnectReceiver.class); - final DeviceCommunicationService service; - - public BluetoothConnectReceiver(final DeviceCommunicationService service) { - this.service = service; + public BluetoothConnectReceiver(final DeviceCommunicationService ignored) { } @Override @@ -56,26 +57,38 @@ public class BluetoothConnectReceiver extends BroadcastReceiver { return; } - LOG.info("connection attempt detected from {}", device.getAddress()); + final String address = device.getAddress(); + LOG.debug("observed device {} via ACL_CONNECTED", address); - final GBDevice gbDevice = GBApplication.app().getDeviceManager().getDeviceByAddress(device.getAddress()); + observedDevice(address); + } + + public static void observedDevice(String address) { + final DeviceManager manager = GBApplication.app().getDeviceManager(); + final GBDevice gbDevice = manager.getDeviceByAddress(address); if (gbDevice == null) { - LOG.info("Connected device {} unknown", device.getAddress()); + LOG.debug("observed non-GB device {}", address); return; } - final SharedPreferences deviceSpecificPreferences = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()); - if (deviceSpecificPreferences == null) { - LOG.warn("no preferences found for connecting device {}", device.getAddress()); - return; + + final GBDevice.State state = gbDevice.getState(); + if (state == WAITING_FOR_RECONNECT || state == WAITING_FOR_SCAN) { + LOG.debug("re-connecting to observed device due to state {}", state); + } else { + final SharedPreferences pref = GBApplication.getDeviceSpecificSharedPrefs(address); + if (pref == null) { + LOG.warn("no preferences found for connecting device {}", address); + return; + } + + if (pref.getBoolean(GBPrefs.DEVICE_CONNECT_BACK, false)) { + LOG.debug("re-connecting to observed device due DEVICE_CONNECT_BACK preference"); + } else { + LOG.info("ignoring observed device {} {}", address, state); + return; + } } - boolean reactToConnection = deviceSpecificPreferences.getBoolean(GBPrefs.DEVICE_CONNECT_BACK, false); - reactToConnection |= gbDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT; - reactToConnection |= gbDevice.getState() == GBDevice.State.WAITING_FOR_SCAN; - if (!reactToConnection) { - LOG.info("Ignoring connection attempt from {}", device.getAddress()); - return; - } - LOG.info("Will re-connect to {} ({})", gbDevice.getAddress(), gbDevice.getName()); + GBApplication.deviceService(gbDevice).connect(); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/GBCompanionDeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/GBCompanionDeviceService.java index f13838d999..42482d35d5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/GBCompanionDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/GBCompanionDeviceService.java @@ -18,8 +18,10 @@ package nodomain.freeyourgadget.gadgetbridge.service; import android.companion.AssociationInfo; +import android.companion.CompanionDeviceManager; import android.companion.CompanionDeviceService; import android.companion.DevicePresenceEvent; +import android.net.MacAddress; import android.os.Build; import androidx.annotation.DeprecatedSinceApi; @@ -30,14 +32,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver; -import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue; -import nodomain.freeyourgadget.gadgetbridge.service.receivers.AutoConnectIntervalReceiver; +import nodomain.freeyourgadget.gadgetbridge.util.BondingUtil; /** - * For now this service only ensures that GB is less likely to get killed. - * See {@link #maybeConnect}. Android API documentation: + * Potentially {@link BluetoothConnectReceiver#observedDevice(String) reconnects} observed + * companion devices. Android API documentation: *
* The system binding CompanionDeviceService elevates the priority of the process that the service * is running in, and thus may prevent the Low-memory killer from killing the process at expense of @@ -52,8 +52,8 @@ public class GBCompanionDeviceService extends CompanionDeviceService { @Override public void onDeviceAppeared(@NonNull String address) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { - LOG.debug("onDeviceAppeared address:{}", address); - maybeConnect(); + LOG.debug("observed device {} via onDeviceAppeared(old {})", address, Build.VERSION.SDK_INT); + BluetoothConnectReceiver.observedDevice(address); } } @@ -68,8 +68,12 @@ public class GBCompanionDeviceService extends CompanionDeviceService { @Override public void onDeviceAppeared(@NonNull AssociationInfo associationInfo) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.BAKLAVA) { - LOG.debug("onDeviceAppeared association:{}", associationInfo.getDeviceMacAddress()); - maybeConnect(); + MacAddress mac = associationInfo.getDeviceMacAddress(); + if (mac != null) { + String address = mac.toString(); + LOG.debug("observed device {} via onDeviceAppeared(new {})", address, Build.VERSION.SDK_INT); + BluetoothConnectReceiver.observedDevice(address); + } } } @@ -95,29 +99,31 @@ public class GBCompanionDeviceService extends CompanionDeviceService { "EVENT_SELF_MANAGED_DISAPPEARED"; default -> Integer.toString(code); }; - LOG.debug("onDevicePresenceEvent {} association:{}", type, event.getAssociationId()); switch (code) { case DevicePresenceEvent.EVENT_BLE_APPEARED: case DevicePresenceEvent.EVENT_BT_CONNECTED: case DevicePresenceEvent.EVENT_SELF_MANAGED_APPEARED: - maybeConnect(); + CompanionDeviceManager manager = BondingUtil.getCompanionDeviceManager(getBaseContext()); + if (manager == null) { + LOG.error("CompanionDeviceManager is null"); + return; + } + for (final AssociationInfo info : manager.getMyAssociations()) { + if (info.getId() == event.getAssociationId()) { + MacAddress mac = info.getDeviceMacAddress(); + if (mac != null) { + String address = mac.toString(); + LOG.debug("observed device {} via {}", address, type); + BluetoothConnectReceiver.observedDevice(address); + return; + } + } + } + LOG.warn("no matching AssociationInfo for {}", event); break; + default: + LOG.debug("onDevicePresenceEvent {} association:{}", type, event.getAssociationId()); } } - - /** - * FIXME {@link DeviceCommunicationService#connectToDevice} has to play nicer with - * concurrent reconnect triggers before code can be added here - *
    - *
  1. {@link AutoConnectIntervalReceiver}
  2. - *
  3. {@link BluetoothConnectReceiver}
  4. - *
  5. {@link DeviceService#ACTION_CONNECT}
  6. - *
  7. device specific - e.g. {@link BtLEQueue#maybeReconnect}'s {@code mBluetoothGatt.connect}
  8. - *
- */ - private void maybeConnect() { - // TODO check DEVICE_CONNECT_BACK, WAITING_FOR_RECONNECT and WAITING_FOR_SCAN - // and conditionally try to establish a connection - } }