From c7053747cd6d157a620c6980282e4429d7f1bb72 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 19 Nov 2019 14:40:20 +0100 Subject: [PATCH] Add active reconnection Closes #1724 Closes #1632 Closes #1452 Closes #1271 Closes #564 (Probably more) --- .../service/DeviceCommunicationService.java | 13 ++- .../gadgetbridge/service/btle/BtLEQueue.java | 6 +- .../btle/actions/SetDeviceStateAction.java | 3 + .../AutoConnectIntervalReceiver.java | 101 ++++++++++++++++++ 4 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/AutoConnectIntervalReceiver.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 34dd7f66b..1fd696893 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -44,14 +44,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Random; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; -import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmClockReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver; @@ -75,13 +73,13 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.service.receivers.AutoConnectIntervalReceiver; import nodomain.freeyourgadget.gadgetbridge.service.receivers.GBAutoFetchReceiver; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.EmojiConverter; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ADD_CALENDAREVENT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_CONFIGURE; @@ -193,6 +191,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere private BluetoothPairingRequestReceiver mBlueToothPairingRequestReceiver = null; private AlarmClockReceiver mAlarmClockReceiver = null; private GBAutoFetchReceiver mGBAutoFetchReceiver = null; + private AutoConnectIntervalReceiver mAutoConnectInvervalReceiver= null; private AlarmReceiver mAlarmReceiver = null; private CalendarReceiver mCalendarReceiver = null; @@ -760,6 +759,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mGBAutoFetchReceiver = new GBAutoFetchReceiver(); registerReceiver(mGBAutoFetchReceiver, new IntentFilter("android.intent.action.USER_PRESENT")); } + if (mAutoConnectInvervalReceiver == null) { + mAutoConnectInvervalReceiver= new AutoConnectIntervalReceiver(this); + registerReceiver(mAutoConnectInvervalReceiver, new IntentFilter("GB_RECONNECT")); + } } else { if (mPhoneCallReceiver != null) { unregisterReceiver(mPhoneCallReceiver); @@ -809,6 +812,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere unregisterReceiver(mGBAutoFetchReceiver); mGBAutoFetchReceiver = null; } + if (mAutoConnectInvervalReceiver != null) { + unregisterReceiver(mAutoConnectInvervalReceiver); + mAutoConnectInvervalReceiver = null; + } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java index 92b56bb33..a79d8a907 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java @@ -49,6 +49,7 @@ import nodomain.freeyourgadget.gadgetbridge.Logging; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State; import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.receivers.AutoConnectIntervalReceiver; /** * One queue/thread per connectable device. @@ -301,8 +302,6 @@ public final class BtLEQueue { mWaitForServerActionResultLatch.countDown(); } - boolean wasInitialized = mGbDevice.isInitialized(); - setDeviceConnectionState(State.NOT_CONNECTED); // either we've been disconnected because the device is out of range @@ -312,7 +311,7 @@ public final class BtLEQueue { // reconnecting automatically, so we try to fix this by re-creating mBluetoothGatt. // Not sure if this actually works without re-initializing the device... if (mBluetoothGatt != null) { - if (!wasInitialized || !maybeReconnect()) { + if (!maybeReconnect()) { disconnect(); // ensure that we start over cleanly next time } } @@ -329,6 +328,7 @@ public final class BtLEQueue { boolean result = mBluetoothGatt.connect(); if (result) { setDeviceConnectionState(State.WAITING_FOR_RECONNECT); + AutoConnectIntervalReceiver.scheduleReconnect(); } return result; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java index bac898926..334e7432f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java @@ -19,6 +19,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle.actions; import android.bluetooth.BluetoothGatt; import android.content.Context; +import androidx.annotation.NonNull; + import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; public class SetDeviceStateAction extends PlainAction { @@ -43,6 +45,7 @@ public class SetDeviceStateAction extends PlainAction { return context; } + @NonNull @Override public String toString() { return super.toString() + " to " + deviceState; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/AutoConnectIntervalReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/AutoConnectIntervalReceiver.java new file mode 100644 index 000000000..9de1e78e2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/AutoConnectIntervalReceiver.java @@ -0,0 +1,101 @@ +/* Copyright (C) 2019 Andreas Shimokawa + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.receivers; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Build; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Calendar; + +import nodomain.freeyourgadget.gadgetbridge.BuildConfig; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; + +public class AutoConnectIntervalReceiver extends BroadcastReceiver { + + final DeviceCommunicationService service; + static int mDelay = 4; + private static final Logger LOG = LoggerFactory.getLogger(AutoConnectIntervalReceiver.class); + + public AutoConnectIntervalReceiver(DeviceCommunicationService service) { + this.service = service; + IntentFilter filterLocal = new IntentFilter(); + filterLocal.addAction(DeviceManager.ACTION_DEVICES_CHANGED); + LocalBroadcastManager.getInstance(service).registerReceiver(this, filterLocal); + } + + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action == null) { + return; + } + GBDevice gbDevice = service.getGBDevice(); + + if (action.equals(DeviceManager.ACTION_DEVICES_CHANGED)) { + if (gbDevice.isInitialized()) { + LOG.info("will reset connection delay, device is initialized!"); + mDelay = 4; + } + } + else if (action.equals("GB_RECONNECT")) { + if (gbDevice != null) { + if (gbDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT) { + LOG.info("Will re-connect to " + gbDevice.getAddress() + "(" + gbDevice.getName() + ")"); + GBApplication.deviceService().connect(); + } + } + } + } + + public static void scheduleReconnect() { + mDelay*=2; + if (mDelay > 128) { + mDelay = 128; + } + scheduleReconnect(mDelay); + } + + public static void scheduleReconnect(int delay) { + LOG.info("schduling reconnect in " + delay + " seconds"); + AlarmManager am = (AlarmManager) (GBApplication.getContext().getSystemService(Context.ALARM_SERVICE)); + Intent intent = new Intent("GB_RECONNECT"); + intent.setPackage(BuildConfig.APPLICATION_ID); + PendingIntent pendingIntent = PendingIntent.getBroadcast(GBApplication.getContext(), 0, intent, 0); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + am.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, Calendar.getInstance(). + getTimeInMillis() + delay * 1000, pendingIntent); + } else { + am.set(AlarmManager.RTC_WAKEUP, Calendar.getInstance(). + getTimeInMillis() + delay * 1000, pendingIntent); + } + } + +}