mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
ble: BtLEQueue optimizations for CompanionDevice auto connect (#5019)
This commit is contained in:
committed by
José Rebelo
parent
d98e51190d
commit
9976d396ec
+39
-18
@@ -28,6 +28,7 @@ import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -74,6 +75,9 @@ public abstract class AbstractBTLEMultiDeviceSupport extends AbstractBTLEDeviceS
|
||||
private final GBDevice[] devices;
|
||||
private final Map<UUID, BluetoothGattCharacteristic>[] mAvailableCharacteristics;
|
||||
|
||||
/// used to guard {@link #connect()}, {@link #disconnect()} and {@link #dispose()}
|
||||
protected final Object ConnectionMonitor = new Object();
|
||||
|
||||
public AbstractBTLEMultiDeviceSupport(Logger logger, int deviceCount) {
|
||||
this.logger = logger;
|
||||
this.deviceCount = deviceCount;
|
||||
@@ -133,28 +137,40 @@ public abstract class AbstractBTLEMultiDeviceSupport extends AbstractBTLEDeviceS
|
||||
throw new IllegalArgumentException("No sub device with address: " + address);
|
||||
}
|
||||
|
||||
/// Device specific code usually should {@code synchronize()} on {@link #ConnectionMonitor}.
|
||||
/// @see AbstractBTLEDeviceSupport#connect()
|
||||
@CallSuper
|
||||
@Override
|
||||
public boolean connect() {
|
||||
// Connect to the queue for each device.
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
if (mQueues[i] == null && devices[i] != null) {
|
||||
mQueues[i] = new BtLEQueue(devices[i], mSupportedServerServices[i], this);
|
||||
if (bleApis[i] != null) {
|
||||
bleApis[i].setQueue(mQueues[i]);
|
||||
synchronized (ConnectionMonitor) {
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
if (mQueues[i] == null && devices[i] != null) {
|
||||
mQueues[i] = new BtLEQueue(devices[i], mSupportedServerServices[i], this);
|
||||
if (bleApis[i] != null) {
|
||||
bleApis[i].setQueue(mQueues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mQueues[i] != null && !mQueues[i].connect()) {
|
||||
return false;
|
||||
if (mQueues[i] != null && !mQueues[i].connect()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Disconnects, but doesn't dispose.
|
||||
/// <p>
|
||||
/// Device specific code usually should {@code synchronize()} on {@link #ConnectionMonitor}.
|
||||
/// </p>
|
||||
@CallSuper
|
||||
public void disconnect() {
|
||||
for (BtLEQueue queue : mQueues) {
|
||||
if (queue != null) {
|
||||
queue.disconnect();
|
||||
synchronized (ConnectionMonitor) {
|
||||
for (BtLEQueue queue : mQueues) {
|
||||
if (queue != null) {
|
||||
queue.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,15 +212,20 @@ public abstract class AbstractBTLEMultiDeviceSupport extends AbstractBTLEDeviceS
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// Device specific code usually should {@code synchronize()} on {@link #ConnectionMonitor}.
|
||||
/// @see AbstractBTLEDeviceSupport#dispose()
|
||||
@CallSuper
|
||||
@Override
|
||||
public void dispose() {
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
if (mQueues[i] != null) {
|
||||
mQueues[i].dispose();
|
||||
mQueues[i] = null;
|
||||
}
|
||||
if (bleApis[i] != null) {
|
||||
bleApis[i].dispose();
|
||||
synchronized (ConnectionMonitor) {
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
if (mQueues[i] != null) {
|
||||
mQueues[i].dispose();
|
||||
mQueues[i] = null;
|
||||
}
|
||||
if (bleApis[i] != null) {
|
||||
bleApis[i].dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
-14
@@ -72,6 +72,9 @@ public abstract class AbstractBTLESingleDeviceSupport extends AbstractBTLEDevice
|
||||
private final List<AbstractBleProfile<?>> mSupportedProfiles = new ArrayList<>();
|
||||
private final Object characteristicsMonitor = new Object();
|
||||
|
||||
/// used to guard {@link #connect()}, {@link #disconnect()} and {@link #dispose()}
|
||||
protected final Object ConnectionMonitor = new Object();
|
||||
|
||||
private BleIntentApi bleApi = null;
|
||||
|
||||
public AbstractBTLESingleDeviceSupport(Logger logger) {
|
||||
@@ -81,21 +84,33 @@ public abstract class AbstractBTLESingleDeviceSupport extends AbstractBTLEDevice
|
||||
}
|
||||
}
|
||||
|
||||
/// Device specific code usually should {@code synchronize()} on {@link #ConnectionMonitor}.
|
||||
/// @see AbstractBTLEDeviceSupport#connect()
|
||||
@CallSuper
|
||||
@Override
|
||||
public boolean connect() {
|
||||
if (mQueue == null) {
|
||||
mQueue = new BtLEQueue(getDevice(), mSupportedServerServices, this);
|
||||
if(bleApi != null) {
|
||||
bleApi.setQueue(mQueue);
|
||||
synchronized (ConnectionMonitor) {
|
||||
if (mQueue == null) {
|
||||
mQueue = new BtLEQueue(getDevice(), mSupportedServerServices, this);
|
||||
if (bleApi != null) {
|
||||
bleApi.setQueue(mQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mQueue.connect();
|
||||
return mQueue.connect();
|
||||
}
|
||||
}
|
||||
|
||||
/// Disconnects, but doesn't dispose.
|
||||
/// <p>
|
||||
/// Device specific code usually should {@code synchronize()} on {@link #ConnectionMonitor}.
|
||||
/// </p>
|
||||
@CallSuper
|
||||
public void disconnect() {
|
||||
if (mQueue != null) {
|
||||
mQueue.disconnect();
|
||||
synchronized (ConnectionMonitor) {
|
||||
if (mQueue != null) {
|
||||
mQueue.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,15 +149,20 @@ public abstract class AbstractBTLESingleDeviceSupport extends AbstractBTLEDevice
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// Device specific code usually should {@code synchronize()} on {@link #ConnectionMonitor}.
|
||||
/// @see AbstractBTLEDeviceSupport#dispose()
|
||||
@CallSuper
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (mQueue != null) {
|
||||
mQueue.dispose();
|
||||
mQueue = null;
|
||||
}
|
||||
synchronized (ConnectionMonitor) {
|
||||
if (mQueue != null) {
|
||||
mQueue.dispose();
|
||||
mQueue = null;
|
||||
}
|
||||
|
||||
if(bleApi != null) {
|
||||
bleApi.dispose();
|
||||
if (bleApi != null) {
|
||||
bleApi.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+113
-90
@@ -33,6 +33,7 @@ import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -43,12 +44,12 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
@@ -75,8 +76,7 @@ public final class BtLEQueue {
|
||||
private final Set<? extends BluetoothGattService> mSupportedServerServices;
|
||||
|
||||
private final BlockingQueue<AbstractTransaction> mTransactions;
|
||||
private volatile boolean mDisposed;
|
||||
private volatile boolean mCrashed;
|
||||
private final AtomicBoolean mDisposed;
|
||||
private volatile boolean mAbortTransaction;
|
||||
private volatile boolean mAbortServerTransaction;
|
||||
private volatile boolean mPauseTransaction;
|
||||
@@ -92,13 +92,17 @@ public final class BtLEQueue {
|
||||
private final boolean mImplicitGattCallbackModify;
|
||||
private final boolean mSendWriteRequestResponse;
|
||||
|
||||
private Thread dispatchThread = new Thread("BtLEQueue_" + THREAD_COUNTER.getAndIncrement()) {
|
||||
private final Thread mDispatchThread;
|
||||
private final HandlerThread mReceiverThread;
|
||||
private final Handler mReceiverHandler;
|
||||
|
||||
private class DispatchRunnable implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
LOG.debug("Queue Dispatch Thread started.");
|
||||
boolean crashed = false;
|
||||
|
||||
while (!mDisposed && !mCrashed) {
|
||||
while (!mDisposed.get() && !crashed) {
|
||||
try {
|
||||
AbstractTransaction qTransaction = mTransactions.take();
|
||||
|
||||
@@ -117,12 +121,11 @@ public final class BtLEQueue {
|
||||
mConnectionLatch = null;
|
||||
}
|
||||
|
||||
if(qTransaction instanceof ServerTransaction) {
|
||||
ServerTransaction serverTransaction = (ServerTransaction)qTransaction;
|
||||
if (qTransaction instanceof final ServerTransaction serverTransaction) {
|
||||
internalGattServerCallback.setTransactionGattCallback(serverTransaction.getGattCallback());
|
||||
mAbortServerTransaction = false;
|
||||
|
||||
for (BtLEServerAction action : serverTransaction.getActions()) {
|
||||
for (final BtLEServerAction action : serverTransaction.getActions()) {
|
||||
if (mAbortServerTransaction) { // got disconnected
|
||||
LOG.info("Aborting running server transaction");
|
||||
break;
|
||||
@@ -147,15 +150,14 @@ public final class BtLEQueue {
|
||||
}
|
||||
}
|
||||
|
||||
if(qTransaction instanceof Transaction) {
|
||||
Transaction transaction = (Transaction)qTransaction;
|
||||
if (qTransaction instanceof final Transaction transaction) {
|
||||
LOG.trace("Changing gatt callback for {}? {}", transaction.getTaskName(), transaction.isModifyGattCallback());
|
||||
if (mImplicitGattCallbackModify || transaction.isModifyGattCallback()) {
|
||||
internalGattCallback.setTransactionGattCallback(transaction.getGattCallback());
|
||||
}
|
||||
mAbortTransaction = false;
|
||||
// Run all actions of the transaction until one doesn't succeed
|
||||
for (BtLEAction action : transaction.getActions()) {
|
||||
for (final BtLEAction action : transaction.getActions()) {
|
||||
if (mAbortTransaction) { // got disconnected
|
||||
LOG.info("Aborting running transaction");
|
||||
break;
|
||||
@@ -163,7 +165,7 @@ public final class BtLEQueue {
|
||||
while ((action instanceof WriteAction) && mPauseTransaction && !mAbortTransaction) {
|
||||
LOG.info("Pausing WriteAction");
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(100L);
|
||||
} catch (Exception e) {
|
||||
LOG.info("Exception during pause", e);
|
||||
break;
|
||||
@@ -174,10 +176,10 @@ public final class BtLEQueue {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("About to run action: {}", action);
|
||||
}
|
||||
if (action instanceof GattListenerAction) {
|
||||
if (action instanceof final GattListenerAction listenerAction) {
|
||||
// this special action overwrites the transaction gatt listener (if any), it must
|
||||
// always be the last action in the transaction
|
||||
internalGattCallback.setTransactionGattCallback(((GattListenerAction) action).getGattCallback());
|
||||
internalGattCallback.setTransactionGattCallback(listenerAction.getGattCallback());
|
||||
}
|
||||
if (action.run(mBluetoothGatt)) {
|
||||
// check again, maybe due to some condition, action did not need to write, so we can't wait
|
||||
@@ -197,10 +199,10 @@ public final class BtLEQueue {
|
||||
}
|
||||
} catch (InterruptedException ignored) {
|
||||
mConnectionLatch = null;
|
||||
LOG.debug("Thread interrupted");
|
||||
LOG.debug("Queue Dispatch Thread interrupted");
|
||||
} catch (Throwable ex) {
|
||||
LOG.error("Queue Dispatch Thread died", ex);
|
||||
mCrashed = true;
|
||||
crashed = true;
|
||||
mConnectionLatch = null;
|
||||
} finally {
|
||||
mWaitForActionResultLatch = null;
|
||||
@@ -211,7 +213,7 @@ public final class BtLEQueue {
|
||||
}
|
||||
};
|
||||
|
||||
public BtLEQueue(GBDevice gbDevice, Set<? extends BluetoothGattService> supportedServerServices, AbstractBTLEDeviceSupport deviceSupport) {
|
||||
BtLEQueue(GBDevice gbDevice, Set<? extends BluetoothGattService> supportedServerServices, AbstractBTLEDeviceSupport deviceSupport) {
|
||||
// 1) apply all settings
|
||||
mBluetoothAdapter = deviceSupport.getBluetoothAdapter();
|
||||
mContext = deviceSupport.getContext();
|
||||
@@ -222,22 +224,39 @@ public final class BtLEQueue {
|
||||
mSendWriteRequestResponse = deviceSupport.getSendWriteRequestResponse();
|
||||
mSupportedServerServices = supportedServerServices;
|
||||
|
||||
long threadIdx = THREAD_COUNTER.getAndIncrement();
|
||||
|
||||
// 2) create new objects
|
||||
mDisposed = new AtomicBoolean(false);
|
||||
mGattMonitor = new Object();
|
||||
mTransactions = new LinkedBlockingQueue<>();
|
||||
internalGattCallback = new InternalGattCallback(deviceSupport);
|
||||
internalGattServerCallback = new InternalGattServerCallback(deviceSupport);
|
||||
mDispatchThread = new Thread(new DispatchRunnable(), "BtLEQueue_" + threadIdx + "_out");
|
||||
|
||||
// 3) finally start the dispatch thread
|
||||
dispatchThread.start();
|
||||
// 3) start the thread
|
||||
mDispatchThread.start();
|
||||
|
||||
// 4) handler thread ensure serial processing and informative thread name in the log
|
||||
if(GBApplication.isRunningOreoOrLater()){
|
||||
mReceiverThread = new HandlerThread("BtLEQueue_" + threadIdx + "_in");
|
||||
mReceiverThread.start();
|
||||
mReceiverHandler = new Handler(mReceiverThread.getLooper());
|
||||
} else {
|
||||
mReceiverThread = null;
|
||||
mReceiverHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isConnected() {
|
||||
if (mGbDevice.isConnected()) {
|
||||
State state = mGbDevice.getState();
|
||||
boolean gatt = (mBluetoothGatt != null);
|
||||
boolean dispatch = mDispatchThread.isAlive();
|
||||
boolean receiver = (mReceiverThread == null || mReceiverThread.isAlive());
|
||||
if (state.equalsOrHigherThan(State.CONNECTED) && gatt && dispatch && receiver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG.debug("isConnected(): current state = {}", mGbDevice.getState());
|
||||
LOG.debug("not connected: state={} gatt={} dispatch={} receiver={}", state, gatt, dispatch, receiver);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -248,20 +267,32 @@ public final class BtLEQueue {
|
||||
*
|
||||
* @return <code>true</code> whether the connection attempt was successfully triggered and <code>false</code> if that failed or if there is already a connection
|
||||
*/
|
||||
public boolean connect() {
|
||||
mPauseTransaction = false;
|
||||
if (isConnected()) {
|
||||
LOG.warn("Ignoring connect() because already connected.");
|
||||
return false;
|
||||
}
|
||||
boolean connect() {
|
||||
synchronized (mGattMonitor) {
|
||||
if (mBluetoothGatt != null) {
|
||||
// Tribal knowledge says you're better off not reusing existing BluetoothGatt connections,
|
||||
// so create a new one.
|
||||
LOG.info("connect() requested -- disconnecting previous connection: {}", mGbDevice.getName());
|
||||
disconnect();
|
||||
State state = mGbDevice.getState();
|
||||
if (state.equalsOrHigherThan(State.CONNECTING)) {
|
||||
LOG.warn("connect - ignored, state is {}", state);
|
||||
return false;
|
||||
} else if (mBluetoothGatt != null) {
|
||||
LOG.warn("connect - ignored, mBluetoothGatt isn't null");
|
||||
return false;
|
||||
} else if (mDisposed.get()) {
|
||||
LOG.error("connect - ignored, this BtLEQueue has already been disposed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connectImp()) {
|
||||
setDeviceConnectionState(State.CONNECTING);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean connectImp() {
|
||||
mPauseTransaction = false;
|
||||
|
||||
LOG.info("Attempting to connect to {}", mGbDevice.getName());
|
||||
mBluetoothAdapter.cancelDiscovery();
|
||||
BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(mGbDevice.getAddress());
|
||||
@@ -281,47 +312,50 @@ public final class BtLEQueue {
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (mGattMonitor) {
|
||||
// connectGatt with true doesn't really work ;( too often connection problems
|
||||
if (GBApplication.isRunningMarshmallowOrLater()) {
|
||||
mBluetoothGatt = remoteDevice.connectGatt(mContext, false, internalGattCallback, BluetoothDevice.TRANSPORT_LE);
|
||||
} else {
|
||||
mBluetoothGatt = remoteDevice.connectGatt(mContext, false, internalGattCallback);
|
||||
}
|
||||
|
||||
// connectGatt with true doesn't really work ;( too often connection problems
|
||||
if (GBApplication.isRunningOreoOrLater()){
|
||||
mBluetoothGatt = remoteDevice.connectGatt(mContext, false,
|
||||
internalGattCallback, BluetoothDevice.TRANSPORT_LE,
|
||||
BluetoothDevice.PHY_LE_CODED_MASK, mReceiverHandler);
|
||||
}else if (GBApplication.isRunningMarshmallowOrLater()) {
|
||||
mBluetoothGatt = remoteDevice.connectGatt(mContext, false,
|
||||
internalGattCallback, BluetoothDevice.TRANSPORT_LE);
|
||||
} else {
|
||||
mBluetoothGatt = remoteDevice.connectGatt(mContext, false,
|
||||
internalGattCallback);
|
||||
}
|
||||
boolean result = mBluetoothGatt != null;
|
||||
if (result) {
|
||||
setDeviceConnectionState(State.CONNECTING);
|
||||
}
|
||||
return result;
|
||||
|
||||
return mBluetoothGatt != null;
|
||||
}
|
||||
|
||||
private void setDeviceConnectionState(final State newState) {
|
||||
new Handler(Looper.getMainLooper()).post(() -> {
|
||||
LOG.debug("new device connection state: {}", newState);
|
||||
mGbDevice.setState(newState);
|
||||
mGbDevice.sendDeviceUpdateIntent(mContext, GBDevice.DeviceUpdateSubject.CONNECTION_STATE);
|
||||
});
|
||||
LOG.debug("new device connection state: {}", newState);
|
||||
mGbDevice.setState(newState);
|
||||
mGbDevice.sendDeviceUpdateIntent(mContext, GBDevice.DeviceUpdateSubject.CONNECTION_STATE);
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
void disconnect() {
|
||||
synchronized (mGattMonitor) {
|
||||
LOG.debug("disconnect()");
|
||||
BluetoothGatt gatt = mBluetoothGatt;
|
||||
if (gatt != null) {
|
||||
mBluetoothGatt = null;
|
||||
LOG.info("Disconnecting BtLEQueue from GATT device");
|
||||
LOG.info("disconnecting BluetoothGatt");
|
||||
gatt.disconnect();
|
||||
gatt.close();
|
||||
setDeviceConnectionState(State.NOT_CONNECTED);
|
||||
}
|
||||
mPauseTransaction = false;
|
||||
BluetoothGattServer gattServer = mBluetoothGattServer;
|
||||
if (gattServer != null) {
|
||||
mBluetoothGattServer = null;
|
||||
LOG.info("disconnecting BluetoothGattServer");
|
||||
gattServer.clearServices();
|
||||
gattServer.close();
|
||||
}
|
||||
|
||||
if (mGbDevice.getState() != State.NOT_CONNECTED) {
|
||||
setDeviceConnectionState(State.NOT_CONNECTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +405,7 @@ public final class BtLEQueue {
|
||||
LOG.info("enabling automatic immediate BLE reconnection");
|
||||
mPauseTransaction = false;
|
||||
if (mBluetoothGatt.connect()) {
|
||||
setDeviceConnectionState(State.WAITING_FOR_RECONNECT);
|
||||
setDeviceConnectionState(State.CONNECTING);
|
||||
} else {
|
||||
forceDisconnect = true;
|
||||
}
|
||||
@@ -406,19 +440,22 @@ public final class BtLEQueue {
|
||||
mPauseTransaction = paused;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (mDisposed) {
|
||||
void dispose() {
|
||||
if (mDisposed.getAndSet(true)) {
|
||||
LOG.warn("dispose() was called repeatedly");
|
||||
return;
|
||||
}
|
||||
mDisposed = true;
|
||||
// try {
|
||||
|
||||
disconnect();
|
||||
dispatchThread.interrupt();
|
||||
dispatchThread = null;
|
||||
// dispatchThread.join();
|
||||
// } catch (InterruptedException ex) {
|
||||
// LOG.error("Exception while disposing BtLEQueue", ex);
|
||||
// }
|
||||
|
||||
if (mReceiverThread != null) {
|
||||
mReceiverHandler.post(() -> {
|
||||
mDispatchThread.interrupt();
|
||||
mReceiverThread.quitSafely();
|
||||
});
|
||||
} else {
|
||||
mDispatchThread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,7 +463,7 @@ public final class BtLEQueue {
|
||||
*
|
||||
* @param transaction
|
||||
*/
|
||||
public void add(Transaction transaction) {
|
||||
void add(Transaction transaction) {
|
||||
LOG.debug("about to add: {}", transaction);
|
||||
if (!transaction.isEmpty()) {
|
||||
mTransactions.add(transaction);
|
||||
@@ -446,11 +483,9 @@ public final class BtLEQueue {
|
||||
|
||||
/**
|
||||
* Adds a serverTransaction to the end of the queue
|
||||
*
|
||||
* @param transaction
|
||||
*/
|
||||
public void add(ServerTransaction transaction) {
|
||||
LOG.debug("about to add: {}", transaction);
|
||||
void add(ServerTransaction transaction) {
|
||||
LOG.debug("about to add server: {}", transaction);
|
||||
if(!transaction.isEmpty()) {
|
||||
mTransactions.add(transaction);
|
||||
}
|
||||
@@ -461,7 +496,7 @@ public final class BtLEQueue {
|
||||
* Note that actions of the *currently executing* transaction
|
||||
* will still be executed before the given transaction.
|
||||
*/
|
||||
public void insert(Transaction transaction) {
|
||||
void insert(Transaction transaction) {
|
||||
LOG.debug("about to insert: {}", transaction);
|
||||
if (!transaction.isEmpty()) {
|
||||
List<AbstractTransaction> tail = new ArrayList<>(mTransactions.size() + 2);
|
||||
@@ -477,20 +512,6 @@ public final class BtLEQueue {
|
||||
mTransactions.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of supported GATT services on the connected device. This should be
|
||||
* invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
|
||||
*
|
||||
* @return A {@code List} of supported services.
|
||||
*/
|
||||
public List<BluetoothGattService> getSupportedGattServices() {
|
||||
if (mBluetoothGatt == null) {
|
||||
LOG.warn("BluetoothGatt is null => no services available.");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mBluetoothGatt.getServices();
|
||||
}
|
||||
|
||||
/** @noinspection BooleanMethodIsAlwaysInverted*/
|
||||
private boolean checkCorrectGattInstance(BluetoothGatt gatt, String where) {
|
||||
if (gatt != mBluetoothGatt && mBluetoothGatt != null) {
|
||||
@@ -592,7 +613,9 @@ public final class BtLEQueue {
|
||||
break;
|
||||
case BluetoothProfile.STATE_DISCONNECTED:
|
||||
LOG.info("Disconnected from GATT server.");
|
||||
handleDisconnected(status);
|
||||
synchronized (mGattMonitor) {
|
||||
handleDisconnected(status);
|
||||
}
|
||||
break;
|
||||
case BluetoothProfile.STATE_CONNECTING:
|
||||
LOG.info("Connecting to GATT server...");
|
||||
@@ -940,11 +963,11 @@ public final class BtLEQueue {
|
||||
GattServerCallback mTransactionGattCallback;
|
||||
private final GattServerCallback mExternalGattServerCallback;
|
||||
|
||||
public InternalGattServerCallback(GattServerCallback externalGattServerCallback) {
|
||||
InternalGattServerCallback(GattServerCallback externalGattServerCallback) {
|
||||
mExternalGattServerCallback = externalGattServerCallback;
|
||||
}
|
||||
|
||||
public void setTransactionGattCallback(@Nullable GattServerCallback callback) {
|
||||
void setTransactionGattCallback(@Nullable GattServerCallback callback) {
|
||||
mTransactionGattCallback = callback;
|
||||
}
|
||||
|
||||
@@ -993,7 +1016,7 @@ public final class BtLEQueue {
|
||||
success = callback.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
|
||||
}
|
||||
if (responseNeeded && mSendWriteRequestResponse) {
|
||||
mBluetoothGattServer.sendResponse(device, requestId, success ? BluetoothGatt.GATT_SUCCESS : BluetoothGatt.GATT_FAILURE, 0, new byte[0]);
|
||||
mBluetoothGattServer.sendResponse(device, requestId, success ? BluetoothGatt.GATT_SUCCESS : BluetoothGatt.GATT_FAILURE, 0, EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1021,7 +1044,7 @@ public final class BtLEQueue {
|
||||
success = callback.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
|
||||
}
|
||||
if (responseNeeded && mSendWriteRequestResponse) {
|
||||
mBluetoothGattServer.sendResponse(device, requestId, success ? BluetoothGatt.GATT_SUCCESS : BluetoothGatt.GATT_FAILURE, 0, new byte[0]);
|
||||
mBluetoothGattServer.sendResponse(device, requestId, success ? BluetoothGatt.GATT_SUCCESS : BluetoothGatt.GATT_FAILURE, 0, EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -147,7 +147,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLESingleDevic
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.EmojiConverter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
@@ -212,11 +211,13 @@ public class BangleJSDeviceSupport extends AbstractBTLESingleDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
stopGlobalUartReceiver();
|
||||
stopLocationUpdate();
|
||||
stopRequestQueue();
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
synchronized (ConnectionMonitor) {
|
||||
super.dispose();
|
||||
stopGlobalUartReceiver();
|
||||
stopLocationUpdate();
|
||||
stopRequestQueue();
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void stopGlobalUartReceiver(){
|
||||
|
||||
+5
-3
@@ -72,10 +72,12 @@ public class BinarySensorSupport extends BinarySensorBaseSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
synchronized (ConnectionMonitor) {
|
||||
super.dispose();
|
||||
|
||||
LocalBroadcastManager.getInstance(getContext())
|
||||
.unregisterReceiver(stateRequestReceiver);
|
||||
LocalBroadcastManager.getInstance(getContext())
|
||||
.unregisterReceiver(stateRequestReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
BroadcastReceiver stateRequestReceiver = new BroadcastReceiver() {
|
||||
|
||||
+4
-7
@@ -42,17 +42,13 @@ import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.Logging;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.Logging;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.preferences.DevicePrefs;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.BcdUtil;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
@@ -60,7 +56,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.Reminder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.casio.CasioConstants;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LANGUAGE;
|
||||
@@ -120,8 +115,10 @@ public abstract class Casio2C2DSupport extends CasioSupport {
|
||||
|
||||
@Override
|
||||
public boolean connect() {
|
||||
requests.clear();
|
||||
return super.connect();
|
||||
synchronized (ConnectionMonitor) {
|
||||
requests.clear();
|
||||
return super.connect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-3
@@ -114,10 +114,12 @@ public class CasioGB6900DeviceSupport extends CasioSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
LOG.info("Dispose");
|
||||
close();
|
||||
synchronized (ConnectionMonitor) {
|
||||
LOG.info("Dispose");
|
||||
close();
|
||||
|
||||
super.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void close() {
|
||||
|
||||
+12
-10
@@ -195,19 +195,21 @@ public class G1DeviceSupport extends AbstractBTLEMultiDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// Remove all background tasks.
|
||||
backgroundTasksHandler.removeCallbacksAndMessages(null);
|
||||
synchronized (ConnectionMonitor) {
|
||||
// Remove all background tasks.
|
||||
backgroundTasksHandler.removeCallbacksAndMessages(null);
|
||||
|
||||
// Kill both sides.
|
||||
leftSide = null;
|
||||
rightSide = null;
|
||||
// Kill both sides.
|
||||
leftSide = null;
|
||||
rightSide = null;
|
||||
|
||||
// Stop listening for intent actions
|
||||
if (intentReceiver != null) {
|
||||
getContext().unregisterReceiver(intentReceiver);
|
||||
// Stop listening for intent actions
|
||||
if (intentReceiver != null) {
|
||||
getContext().unregisterReceiver(intentReceiver);
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-4
@@ -157,11 +157,13 @@ public class FlipperZeroSupport extends FlipperZeroBaseSupport{
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
synchronized (ConnectionMonitor) {
|
||||
super.dispose();
|
||||
|
||||
if(recevierRegistered) {
|
||||
getContext().unregisterReceiver(receiver);
|
||||
recevierRegistered = false;
|
||||
if (recevierRegistered) {
|
||||
getContext().unregisterReceiver(receiver);
|
||||
recevierRegistered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -134,9 +134,11 @@ public class GarminSupport extends AbstractBTLESingleDeviceSupport implements IC
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
LOG.info("Garmin dispose()");
|
||||
GBLocationService.stop(getContext(), getDevice());
|
||||
super.dispose();
|
||||
synchronized (ConnectionMonitor) {
|
||||
LOG.info("Garmin dispose()");
|
||||
GBLocationService.stop(getContext(), getDevice());
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void addFileToDownloadList(FileTransferHandler.DirectoryEntry directoryEntry) {
|
||||
|
||||
+5
-3
@@ -82,10 +82,12 @@ public class HPlusSupport extends AbstractBTLESingleDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
LOG.info("Dispose");
|
||||
close();
|
||||
synchronized (ConnectionMonitor) {
|
||||
LOG.info("Dispose");
|
||||
close();
|
||||
|
||||
super.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-2
@@ -107,8 +107,10 @@ public class ZeppOsBtleSupport extends AbstractBTLESingleDeviceSupport implement
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
zeppOsSupport.dispose();
|
||||
super.dispose();
|
||||
synchronized (ConnectionMonitor) {
|
||||
zeppOsSupport.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-2
@@ -203,8 +203,10 @@ public class HuaweiLESupport extends AbstractBTLESingleDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
supportProvider.dispose();
|
||||
super.dispose();
|
||||
synchronized (ConnectionMonitor) {
|
||||
supportProvider.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-2
@@ -123,8 +123,10 @@ public class IdasenDeviceSupport extends AbstractBTLESingleDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(commandReceiver);
|
||||
synchronized (ConnectionMonitor) {
|
||||
super.dispose();
|
||||
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(commandReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-3
@@ -2066,9 +2066,11 @@ public class WatchXPlusDeviceSupport extends AbstractBTLESingleDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||
broadcastManager.unregisterReceiver(broadcastReceiver);
|
||||
super.dispose();
|
||||
synchronized (ConnectionMonitor) {
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||
broadcastManager.unregisterReceiver(broadcastReceiver);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static double onSamplingInterval(int i, int i2) {
|
||||
|
||||
+4
-2
@@ -210,8 +210,10 @@ public class MoyoungDeviceSupport extends AbstractBTLESingleDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
idleUpdateHandler.removeCallbacks(updateIdleStepsRunnable);
|
||||
synchronized (ConnectionMonitor) {
|
||||
super.dispose();
|
||||
idleUpdateHandler.removeCallbacks(updateIdleStepsRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
private BluetoothGattCharacteristic getTargetCharacteristicForPacketType(byte packetType) {
|
||||
|
||||
+7
-5
@@ -463,12 +463,14 @@ public class QHybridSupport extends QHybridBaseSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(commandReceiver);
|
||||
GBApplication.getContext().unregisterReceiver(globalCommandReceiver);
|
||||
if (watchAdapter != null) {
|
||||
watchAdapter.dispose();
|
||||
synchronized (ConnectionMonitor) {
|
||||
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(commandReceiver);
|
||||
GBApplication.getContext().unregisterReceiver(globalCommandReceiver);
|
||||
if (watchAdapter != null) {
|
||||
watchAdapter.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-2
@@ -206,8 +206,10 @@ public class SuperCarsSupport extends AbstractBTLESingleDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(commandReceiver);
|
||||
synchronized (ConnectionMonitor) {
|
||||
super.dispose();
|
||||
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(commandReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-5
@@ -119,12 +119,14 @@ public class UltrahumanDeviceSupport extends AbstractBTLESingleDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (CommandReceiver.Registered) {
|
||||
CommandReceiver.Registered = false;
|
||||
getContext().unregisterReceiver(CommandReceiver);
|
||||
}
|
||||
synchronized (ConnectionMonitor) {
|
||||
if (CommandReceiver.Registered) {
|
||||
CommandReceiver.Registered = false;
|
||||
getContext().unregisterReceiver(CommandReceiver);
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-4
@@ -141,10 +141,12 @@ public class UM25Support extends UM25BaseSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
LocalBroadcastManager.getInstance(getContext())
|
||||
.unregisterReceiver(resetReceiver);
|
||||
executor.shutdown();
|
||||
synchronized (ConnectionMonitor) {
|
||||
super.dispose();
|
||||
LocalBroadcastManager.getInstance(getContext())
|
||||
.unregisterReceiver(resetReceiver);
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void startLoop(){
|
||||
|
||||
+4
-2
@@ -235,8 +235,10 @@ public class VescDeviceSupport extends VescBaseDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(commandReceiver);
|
||||
synchronized (ConnectionMonitor) {
|
||||
super.dispose();
|
||||
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(commandReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
BroadcastReceiver commandReceiver = new BroadcastReceiver() {
|
||||
|
||||
+5
-3
@@ -466,9 +466,11 @@ public class Watch9DeviceSupport extends AbstractBTLESingleDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||
broadcastManager.unregisterReceiver(broadcastReceiver);
|
||||
super.dispose();
|
||||
synchronized (ConnectionMonitor) {
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||
broadcastManager.unregisterReceiver(broadcastReceiver);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-2
@@ -226,8 +226,10 @@ public class XiaomiBleSupport extends XiaomiConnectionSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
mXiaomiSupport.onDisconnect();
|
||||
super.dispose();
|
||||
synchronized (ConnectionMonitor) {
|
||||
mXiaomiSupport.onDisconnect();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+9
-7
@@ -105,15 +105,17 @@ public class YawellRingDeviceSupport extends AbstractBTLESingleDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
backgroundTasksHandler.removeCallbacksAndMessages(null);
|
||||
synchronized (ConnectionMonitor) {
|
||||
backgroundTasksHandler.removeCallbacksAndMessages(null);
|
||||
|
||||
LOG.info("Stopping live activity timeout scheduler");
|
||||
if(liveActivityContext.getRealtimeStepsScheduler() != null) {
|
||||
liveActivityContext.getRealtimeStepsScheduler().shutdown();
|
||||
liveActivityContext.setRealtimeStepsScheduler(null);
|
||||
LOG.info("Stopping live activity timeout scheduler");
|
||||
if (liveActivityContext.getRealtimeStepsScheduler() != null) {
|
||||
liveActivityContext.getRealtimeStepsScheduler().shutdown();
|
||||
liveActivityContext.setRealtimeStepsScheduler(null);
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user