mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
ble: add ReadPhyAction, SetPreferredPhyAction, onServiceChanged and onReliableWriteCompleted
This commit is contained in:
committed by
José Rebelo
parent
e40608dcf6
commit
f732ff582a
+19
-2
@@ -28,6 +28,7 @@ import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -44,7 +45,6 @@ import java.util.UUID;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.Logging;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.CheckInitializedAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
@@ -52,7 +52,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
/**
|
||||
* Abstract base class for all devices connected through Bluetooth Low Energy (LE) aka
|
||||
* Bluetooth Smart.
|
||||
* <p/>
|
||||
* <p>
|
||||
* The connection to the device and all communication is made with a generic {@link BtLEQueue}.
|
||||
* Messages to the device are encoded as {@link BtLEAction actions} or {@link BtLEServerAction actions}
|
||||
* that are grouped with a {@link Transaction} or {@link ServerTransaction} and sent via {@link BtLEQueue}.
|
||||
@@ -554,6 +554,23 @@ public abstract class AbstractBTLEMultiDeviceSupport extends AbstractBTLEDeviceS
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceChanged(@NonNull BluetoothGatt gatt){
|
||||
logger.warn("onServiceChanged is NOT supported by AbstractBTLEMultiDeviceSupport");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current MTU, or 0 if unknown
|
||||
*
|
||||
|
||||
+56
-31
@@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2024 Andreas Shimokawa, Carsten Pfeiffer, Damien
|
||||
Gaignon, Daniel Dakhno, Uwe Hermann
|
||||
/* Copyright (C) 2015-2025 Andreas Shimokawa, Carsten Pfeiffer, Damien
|
||||
Gaignon, Daniel Dakhno, Uwe Hermann, Thomas Kuehne
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@@ -21,6 +21,11 @@ import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
@@ -29,17 +34,18 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.OperationStatus;
|
||||
|
||||
/**
|
||||
* Abstract base class for a BTLEOperation, i.e. an operation that does more than
|
||||
* Abstract base class for a {@link BTLEOperation}, i.e. an operation that does more than
|
||||
* just sending a few bytes to the device. It typically involves exchanging many messages
|
||||
* between the mobile and the device.
|
||||
* <p/>
|
||||
* One operation may execute multiple @{link Transaction transactions} with each
|
||||
* multiple @{link BTLEAction actions}.
|
||||
* <p/>
|
||||
* This class implements GattCallback so that subclasses may override those methods
|
||||
* <p>
|
||||
* One operation may execute multiple {@link Transaction transactions} with each
|
||||
* multiple {@link BtLEAction actions}.
|
||||
* <p>
|
||||
* This class implements {@link GattCallback} so that subclasses may override those methods
|
||||
* to handle those events.
|
||||
* Note: by default all Gatt events are forwarded to AbstractBTLEDeviceSupport, subclasses may override
|
||||
* this behavior.
|
||||
* <p>
|
||||
* Note: by default all Gatt events are forwarded to {@link AbstractBTLESingleDeviceSupport},
|
||||
* subclasses may override this behavior.
|
||||
*/
|
||||
public abstract class AbstractBTLEOperation<T extends AbstractBTLESingleDeviceSupport> implements GattCallback, BTLEOperation {
|
||||
private final T mSupport;
|
||||
@@ -53,9 +59,7 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLESingleDeviceSu
|
||||
/**
|
||||
* Performs this operation. The whole operation is asynchronous, i.e.
|
||||
* this method quickly returns before the actual operation is finished.
|
||||
* Calls #prePerform() and, if successful, #doPerform().
|
||||
*
|
||||
* @throws IOException
|
||||
* Calls {@link #prePerform()} and, if successful, {@link #doPerform()}.
|
||||
*/
|
||||
@Override
|
||||
public final void perform() throws IOException {
|
||||
@@ -66,42 +70,32 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLESingleDeviceSu
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for subclasses to perform something before #doPerform() is invoked.
|
||||
*
|
||||
* @throws IOException
|
||||
* Hook for subclasses to perform something before {@link #doPerform()} is invoked.
|
||||
*/
|
||||
protected void prePerform() throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this. When invoked, #prePerform() returned
|
||||
* Subclasses must implement this. When invoked, {@link #prePerform()} returned
|
||||
* successfully.
|
||||
* Note that subclasses HAVE TO call #operationFinished() when the entire
|
||||
* Note that subclasses HAVE TO call {@link #operationFinished()} when the entire
|
||||
* operation is done (successful or not).
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
protected abstract void doPerform() throws IOException;
|
||||
|
||||
/**
|
||||
* You MUST call this method when the operation has finished, either
|
||||
* successfully or unsuccessfully.
|
||||
*
|
||||
* <p>
|
||||
* Subclasses must ensure that the {@link BtLEQueue queue's}'s gatt callback (set on the transaction builder by {@link #performInitialized(String)})
|
||||
* is being unset, otherwise it will continue to receive events until another transaction is being executed by the queue.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void operationFinished() throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to the DeviceSupport instance and additionally sets this instance as the Gatt
|
||||
* callback for the transaction.
|
||||
*
|
||||
* @param taskName
|
||||
* @return
|
||||
* @throws IOException
|
||||
* Delegates to the {@link AbstractBTLESingleDeviceSupport#performInitialized(String)} instance
|
||||
* and additionally sets this instance as the {@link GattCallback} for the transaction.
|
||||
*/
|
||||
public TransactionBuilder performInitialized(String taskName) throws IOException {
|
||||
TransactionBuilder builder = mSupport.performInitialized(taskName);
|
||||
@@ -109,20 +103,27 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLESingleDeviceSu
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to the {@link AbstractBTLESingleDeviceSupport#createTransactionBuilder(String)}
|
||||
* instance and additionally sets this instance as the {@link GattCallback} for the transaction.
|
||||
*/
|
||||
public TransactionBuilder createTransactionBuilder(String taskName) {
|
||||
TransactionBuilder builder = getSupport().createTransactionBuilder(taskName);
|
||||
TransactionBuilder builder = mSupport.createTransactionBuilder(taskName);
|
||||
builder.setCallback(this);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// Delegates to {@link AbstractBTLESingleDeviceSupport#performImmediately(TransactionBuilder)}
|
||||
public void performImmediately(TransactionBuilder builder) throws IOException {
|
||||
mSupport.performImmediately(builder);
|
||||
}
|
||||
|
||||
/// Delegates to {@link AbstractBTLESingleDeviceSupport#getContext()}
|
||||
protected Context getContext() {
|
||||
return mSupport.getContext();
|
||||
}
|
||||
|
||||
/// Delegates to {@link AbstractBTLESingleDeviceSupport#getDevice()}
|
||||
protected GBDevice getDevice() {
|
||||
return mSupport.getDevice();
|
||||
}
|
||||
@@ -143,10 +144,12 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLESingleDeviceSu
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
/// Delegates to {@link AbstractBTLESingleDeviceSupport#getCharacteristic(UUID)}
|
||||
protected BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
|
||||
return mSupport.getCharacteristic(uuid);
|
||||
}
|
||||
|
||||
/// Delegates to {@link AbstractBTLESingleDeviceSupport#getQueue()}
|
||||
protected BtLEQueue getQueue() {
|
||||
return mSupport.getQueue();
|
||||
}
|
||||
@@ -170,7 +173,6 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLESingleDeviceSu
|
||||
return mSupport;
|
||||
}
|
||||
|
||||
// All Gatt callbacks delegated to MiBandSupport
|
||||
@Override
|
||||
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
||||
mSupport.onConnectionStateChange(gatt, status, newState);
|
||||
@@ -215,4 +217,27 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLESingleDeviceSu
|
||||
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||
mSupport.onMtuChanged(gatt, mtu, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||
mSupport.onReliableWriteCompleted(gatt, status);
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@Override
|
||||
public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
|
||||
mSupport.onPhyRead(gatt, txPhy, rxPhy, status);
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@Override
|
||||
public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
|
||||
mSupport.onPhyUpdate(gatt, txPhy, rxPhy, status);
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
@Override
|
||||
public void onServiceChanged(@NonNull BluetoothGatt gatt) {
|
||||
mSupport.onServiceChanged(gatt);
|
||||
}
|
||||
}
|
||||
|
||||
+22
@@ -29,6 +29,7 @@ import android.bluetooth.BluetoothGattService;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -469,6 +470,27 @@ public abstract class AbstractBTLESingleDeviceSupport extends AbstractBTLEDevice
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceChanged(@NonNull BluetoothGatt gatt){
|
||||
logger.warn("onServiceChanged is NOT supported by AbstractBTLESingleDeviceSupport");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current MTU, or 0 if unknown
|
||||
* @return the current MTU, 0 if unknown
|
||||
|
||||
+19
-1
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2024 Carsten Pfeiffer, Daniel Dakhno
|
||||
/* Copyright (C) 2015-2025 Carsten Pfeiffer, Daniel Dakhno, Thomas Kuehne
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Base class for GattCallbacks wishing to just implement a few of the methods.
|
||||
*/
|
||||
@@ -64,4 +66,20 @@ public abstract class AbstractGattCallback implements GattCallback {
|
||||
@Override
|
||||
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceChanged(@NonNull BluetoothGatt gatt){
|
||||
}
|
||||
}
|
||||
|
||||
+127
-22
@@ -31,11 +31,13 @@ import android.bluetooth.BluetoothGattService;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -517,11 +519,11 @@ public final class BtLEQueue {
|
||||
GattCallback mTransactionGattCallback;
|
||||
private final GattCallback mExternalGattCallback;
|
||||
|
||||
public InternalGattCallback(GattCallback externalGattCallback) {
|
||||
InternalGattCallback(GattCallback externalGattCallback) {
|
||||
mExternalGattCallback = externalGattCallback;
|
||||
}
|
||||
|
||||
public void setTransactionGattCallback(@Nullable GattCallback callback) {
|
||||
void setTransactionGattCallback(@Nullable GattCallback callback) {
|
||||
mTransactionGattCallback = callback;
|
||||
}
|
||||
|
||||
@@ -540,6 +542,10 @@ public final class BtLEQueue {
|
||||
BleNamesResolver.getStateString(newState), BleNamesResolver.getStatusString(status),
|
||||
BleNamesResolver.getBondStateString(bondState));
|
||||
|
||||
if (!checkCorrectGattInstance(gatt, "onConnectionStateChange")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mGattMonitor) {
|
||||
if (mBluetoothGatt == null) {
|
||||
mBluetoothGatt = gatt;
|
||||
@@ -597,7 +603,9 @@ public final class BtLEQueue {
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||
if (!checkCorrectGattInstance(gatt, "services discovered: " + BleNamesResolver.getStatusString(status))) {
|
||||
LOG.debug("services discovered: {}", BleNamesResolver.getStatusString(status));
|
||||
|
||||
if (!checkCorrectGattInstance(gatt, "onServicesDiscovered")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -630,14 +638,14 @@ public final class BtLEQueue {
|
||||
checkWaitingCharacteristic(characteristic, status);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||
super.onMtuChanged(gatt, mtu, status);
|
||||
|
||||
LOG.debug("mtu changed to {} {}", mtu, BleNamesResolver.getStatusString(status));
|
||||
|
||||
if (!checkCorrectGattInstance(gatt, "onMtuChanged")) {
|
||||
return;
|
||||
}
|
||||
|
||||
final GattCallback callback = getCallbackToUse();
|
||||
if (callback != null) {
|
||||
callback.onMtuChanged(gatt, mtu, status);
|
||||
@@ -661,13 +669,15 @@ public final class BtLEQueue {
|
||||
public void onCharacteristicRead(@NonNull BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic,
|
||||
@NonNull byte[] value, int status) {
|
||||
LOG.debug(
|
||||
"characteristic read: {} {} {}",
|
||||
characteristic.getUuid(),
|
||||
BleNamesResolver.getStatusString(status),
|
||||
status == BluetoothGatt.GATT_SUCCESS ? ": " + Logging.formatBytes(value) : ""
|
||||
);
|
||||
if (!checkCorrectGattInstance(gatt, "characteristic read")) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
String content = Logging.formatBytes(value);
|
||||
LOG.debug(
|
||||
"characteristic read: {} {} - {}", characteristic.getUuid(),
|
||||
BleNamesResolver.getStatusString(status), content
|
||||
);
|
||||
}
|
||||
|
||||
if (!checkCorrectGattInstance(gatt, "onCharacteristicRead")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -690,8 +700,13 @@ public final class BtLEQueue {
|
||||
|
||||
@Override
|
||||
public void onDescriptorRead(@NonNull BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) {
|
||||
LOG.debug("descriptor read: {} {}", descriptor.getUuid(), BleNamesResolver.getStatusString(status));
|
||||
if (!checkCorrectGattInstance(gatt, "descriptor read")) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
String content = Logging.formatBytes(value);
|
||||
LOG.debug("descriptor read: {} {} - {}", descriptor.getUuid(),
|
||||
BleNamesResolver.getStatusString(status), content);
|
||||
}
|
||||
|
||||
if (!checkCorrectGattInstance(gatt, "onDescriptorRead")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -737,7 +752,7 @@ public final class BtLEQueue {
|
||||
@NonNull byte[] value) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
String content = Logging.formatBytes(value);
|
||||
LOG.debug("characteristic changed: {} value: {}", characteristic.getUuid(), content);
|
||||
LOG.debug("characteristic changed: {} - {}", characteristic.getUuid(), content);
|
||||
}
|
||||
if (!checkCorrectGattInstance(gatt, "characteristic changed")) {
|
||||
return;
|
||||
@@ -757,7 +772,7 @@ public final class BtLEQueue {
|
||||
|
||||
@Override
|
||||
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
|
||||
LOG.debug("remote rssi: {} {}", rssi, BleNamesResolver.getStatusString(status));
|
||||
LOG.debug("read remote rssi: {} {}", rssi, BleNamesResolver.getStatusString(status));
|
||||
if (!checkCorrectGattInstance(gatt, "remote rssi")) {
|
||||
return;
|
||||
}
|
||||
@@ -772,6 +787,96 @@ public final class BtLEQueue {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||
LOG.debug("reliable write completed: {}", BleNamesResolver.getStatusString(status));
|
||||
|
||||
if (!checkCorrectGattInstance(gatt, "onReliableWriteCompleted")) {
|
||||
return;
|
||||
}
|
||||
|
||||
final GattCallback callback = getCallbackToUse();
|
||||
if (callback != null) {
|
||||
try {
|
||||
callback.onReliableWriteCompleted(gatt, status);
|
||||
} catch (Throwable ex) {
|
||||
LOG.error("onReliableWriteCompleted failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
final CountDownLatch latch = mWaitForActionResultLatch;
|
||||
if (latch != null) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
|
||||
LOG.debug("phy read: tx={} rx={} {}", txPhy, rxPhy,
|
||||
BleNamesResolver.getStatusString(status));
|
||||
|
||||
if (!checkCorrectGattInstance(gatt, "onPhyRead")) {
|
||||
return;
|
||||
}
|
||||
|
||||
final GattCallback callback = getCallbackToUse();
|
||||
if (callback != null) {
|
||||
try {
|
||||
callback.onPhyRead(gatt, txPhy, rxPhy, status);
|
||||
} catch (Throwable ex) {
|
||||
LOG.error("onPhyRead failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
final CountDownLatch latch = mWaitForActionResultLatch;
|
||||
if (latch != null) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
|
||||
LOG.debug("phy updated: tx={} rx={} {}", txPhy, rxPhy,
|
||||
BleNamesResolver.getStatusString(status));
|
||||
|
||||
if (!checkCorrectGattInstance(gatt, "onPhyUpdate")) {
|
||||
return;
|
||||
}
|
||||
|
||||
final GattCallback callback = getCallbackToUse();
|
||||
if (callback != null) {
|
||||
try {
|
||||
callback.onPhyUpdate(gatt, txPhy, rxPhy, status);
|
||||
} catch (Throwable ex) {
|
||||
LOG.error("onPhyUpdate failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// not all updates are triggered by GB:
|
||||
// can't use mWaitForActionResultLatch here
|
||||
}
|
||||
|
||||
@Override
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
public void onServiceChanged(@NonNull BluetoothGatt gatt) {
|
||||
LOG.debug("service changed");
|
||||
|
||||
if (!checkCorrectGattInstance(gatt, "onServiceChanged")) {
|
||||
return;
|
||||
}
|
||||
|
||||
final GattCallback callback = getCallbackToUse();
|
||||
if (callback != null) {
|
||||
try {
|
||||
callback.onServiceChanged(gatt);
|
||||
} catch (Throwable ex) {
|
||||
LOG.error("onServiceChanged failed", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkWaitingCharacteristic(BluetoothGattCharacteristic characteristic, int status) {
|
||||
if (status != BluetoothGatt.GATT_SUCCESS) {
|
||||
if (characteristic != null) {
|
||||
@@ -779,7 +884,7 @@ public final class BtLEQueue {
|
||||
}
|
||||
mAbortTransaction = true;
|
||||
}
|
||||
final BluetoothGattCharacteristic waitCharacteristic = BtLEQueue.this.mWaitCharacteristic;
|
||||
final BluetoothGattCharacteristic waitCharacteristic = mWaitCharacteristic;
|
||||
if (characteristic != null && waitCharacteristic != null && characteristic.getUuid().equals(waitCharacteristic.getUuid())) {
|
||||
final CountDownLatch resultLatch = mWaitForActionResultLatch;
|
||||
if (resultLatch != null) {
|
||||
@@ -795,7 +900,7 @@ public final class BtLEQueue {
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
void reset() {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("internal gatt callback set to null");
|
||||
}
|
||||
@@ -803,7 +908,7 @@ public final class BtLEQueue {
|
||||
}
|
||||
|
||||
/// helper to emulate Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU on older APIs
|
||||
private byte[] emulateMemorySafeValue(BluetoothGattCharacteristic characteristic,
|
||||
private static byte[] emulateMemorySafeValue(BluetoothGattCharacteristic characteristic,
|
||||
int status){
|
||||
if(status == BluetoothGatt.GATT_SUCCESS) {
|
||||
byte[] value = characteristic.getValue();
|
||||
@@ -815,7 +920,7 @@ public final class BtLEQueue {
|
||||
}
|
||||
|
||||
/// helper to emulate Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU on older APIs
|
||||
private byte[] emulateMemorySafeValue(BluetoothGattDescriptor descriptor,
|
||||
private static byte[] emulateMemorySafeValue(BluetoothGattDescriptor descriptor,
|
||||
int status){
|
||||
if(status == BluetoothGatt.GATT_SUCCESS) {
|
||||
byte[] value = descriptor.getValue();
|
||||
|
||||
+20
-38
@@ -20,12 +20,16 @@ import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
/**
|
||||
* Callback interface handling gatt events.
|
||||
* Pretty much the same as {@link BluetoothGattCallback}, except it's an interface
|
||||
* instead of an abstract class. Some handlers commented out, because not used (yet).
|
||||
*
|
||||
* <p>
|
||||
* Note: the boolean return values indicate whether this callback "consumed" this event
|
||||
* or not. True means, the event was consumed by this instance and no further instances
|
||||
* shall be notified. False means, this instance could not handle the event.
|
||||
@@ -33,87 +37,65 @@ import android.bluetooth.BluetoothGattDescriptor;
|
||||
public interface GattCallback {
|
||||
|
||||
/**
|
||||
* @param gatt
|
||||
* @param status
|
||||
* @param newState
|
||||
* @see BluetoothGattCallback#onConnectionStateChange(BluetoothGatt, int, int)
|
||||
*/
|
||||
void onConnectionStateChange(BluetoothGatt gatt, int status, int newState);
|
||||
|
||||
/**
|
||||
* @param gatt
|
||||
* @see BluetoothGattCallback#onServicesDiscovered(BluetoothGatt, int)
|
||||
*/
|
||||
void onServicesDiscovered(BluetoothGatt gatt);
|
||||
|
||||
/**
|
||||
* @param gatt
|
||||
* @param characteristic
|
||||
* @param value
|
||||
* @param status
|
||||
* @see BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, BluetoothGattCharacteristic, byte[], int)
|
||||
*/
|
||||
boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
|
||||
byte[] value, int status);
|
||||
|
||||
/**
|
||||
* @param gatt
|
||||
* @param characteristic
|
||||
* @param status
|
||||
* @see BluetoothGattCallback#onCharacteristicWrite(BluetoothGatt, BluetoothGattCharacteristic, int)
|
||||
*/
|
||||
boolean onCharacteristicWrite(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic, int status);
|
||||
|
||||
/**
|
||||
* @param gatt
|
||||
* @param characteristic
|
||||
* @param value
|
||||
* @see BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt, BluetoothGattCharacteristic, byte[])
|
||||
*/
|
||||
boolean onCharacteristicChanged(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic, byte[] value);
|
||||
|
||||
/**
|
||||
* @param gatt
|
||||
* @param descriptor
|
||||
* @param status
|
||||
* @see BluetoothGattCallback#onDescriptorRead(BluetoothGatt, BluetoothGattDescriptor, int, byte[])
|
||||
*/
|
||||
boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
|
||||
int status, byte[] value);
|
||||
|
||||
/**
|
||||
* @param gatt
|
||||
* @param descriptor
|
||||
* @param status
|
||||
* @see BluetoothGattCallback#onDescriptorWrite(BluetoothGatt, BluetoothGattDescriptor, int)
|
||||
*/
|
||||
boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
|
||||
int status);
|
||||
//
|
||||
// /**
|
||||
// * @see BluetoothGattCallback#onReliableWriteCompleted(BluetoothGatt, int)
|
||||
// * @param gatt
|
||||
// * @param status
|
||||
// */
|
||||
// public void onReliableWriteCompleted(BluetoothGatt gatt, int status);
|
||||
|
||||
/**
|
||||
* @param gatt
|
||||
* @param rssi
|
||||
* @param status
|
||||
* @see BluetoothGattCallback#onReadRemoteRssi(BluetoothGatt, int, int)
|
||||
*/
|
||||
void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status);
|
||||
|
||||
/// @see BluetoothGattCallback#onMtuChanged(BluetoothGatt, int, int)
|
||||
void onMtuChanged(BluetoothGatt gatt, int mtu, int status);
|
||||
|
||||
// /**
|
||||
// * @see BluetoothGattCallback#onMtuChanged(BluetoothGatt, int, int)
|
||||
// * @param gatt
|
||||
// * @param mtu
|
||||
// * @param status
|
||||
// */
|
||||
// public void onMtuChanged(BluetoothGatt gatt, int mtu, int status);
|
||||
/// @see BluetoothGattCallback#onReliableWriteCompleted(BluetoothGatt, int)
|
||||
void onReliableWriteCompleted (BluetoothGatt gatt, int status);
|
||||
|
||||
/// @see BluetoothGattCallback#onPhyRead(BluetoothGatt, int, int, int)
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status);
|
||||
|
||||
/// @see BluetoothGattCallback#onPhyUpdate(BluetoothGatt, int, int, int)
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status);
|
||||
|
||||
/// @see BluetoothGattCallback#onServiceChanged(BluetoothGatt)
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
void onServiceChanged(@NonNull BluetoothGatt gatt);
|
||||
}
|
||||
|
||||
+31
-1
@@ -20,12 +20,14 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle;
|
||||
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -34,9 +36,11 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.BondAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.FunctionAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.NotifyAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ReadAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ReadPhyAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.RequestConnectionPriorityAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.RequestMtuAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetPreferredPhyAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction;
|
||||
|
||||
@@ -50,6 +54,7 @@ public class TransactionBuilder {
|
||||
mTransaction = new Transaction(taskName);
|
||||
}
|
||||
|
||||
/// @see ReadAction
|
||||
public TransactionBuilder read(BluetoothGattCharacteristic characteristic) {
|
||||
if (characteristic == null) {
|
||||
LOG.warn("Unable to read characteristic: null");
|
||||
@@ -59,6 +64,7 @@ public class TransactionBuilder {
|
||||
return add(action);
|
||||
}
|
||||
|
||||
/// @see WriteAction
|
||||
public TransactionBuilder write(BluetoothGattCharacteristic characteristic, byte[] data) {
|
||||
if (characteristic == null) {
|
||||
LOG.warn("Unable to write characteristic: null");
|
||||
@@ -79,23 +85,27 @@ public class TransactionBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/// @see RequestMtuAction
|
||||
public TransactionBuilder requestMtu(int mtu){
|
||||
return add(
|
||||
new RequestMtuAction(mtu)
|
||||
);
|
||||
}
|
||||
|
||||
/// @see RequestConnectionPriorityAction
|
||||
public TransactionBuilder requestConnectionPriority(int priority){
|
||||
return add(
|
||||
new RequestConnectionPriorityAction(priority)
|
||||
);
|
||||
}
|
||||
|
||||
/// @see BondAction
|
||||
public TransactionBuilder bond() {
|
||||
BondAction action = new BondAction();
|
||||
return add(action);
|
||||
}
|
||||
|
||||
/// @see NotifyAction
|
||||
public TransactionBuilder notify(BluetoothGattCharacteristic characteristic, boolean enable) {
|
||||
if (characteristic == null) {
|
||||
LOG.warn("Unable to notify characteristic: null");
|
||||
@@ -120,7 +130,7 @@ public class TransactionBuilder {
|
||||
return add(action);
|
||||
}
|
||||
|
||||
// Runs the given function/lambda
|
||||
/// @see FunctionAction
|
||||
public TransactionBuilder run(FunctionAction.Function function) {
|
||||
return add(new FunctionAction(function));
|
||||
}
|
||||
@@ -138,6 +148,26 @@ public class TransactionBuilder {
|
||||
return add(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the current transmitter PHY and receiver PHY of the connection.
|
||||
* @see ReadPhyAction
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public TransactionBuilder readPhy() {
|
||||
BtLEAction action = new ReadPhyAction();
|
||||
return add(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the preferred PHY of the connection.
|
||||
* @see SetPreferredPhyAction
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public TransactionBuilder setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
|
||||
BtLEAction action = new SetPreferredPhyAction(txPhy, rxPhy, phyOptions);
|
||||
return add(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a GattCallback instance that will be called when the transaction is executed,
|
||||
* resulting in GattCallback events.
|
||||
|
||||
+18
-9
@@ -18,7 +18,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothStatusCodes;
|
||||
@@ -29,20 +28,21 @@ import org.slf4j.LoggerFactory;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BleNamesResolver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCallback;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.btle.GattDescriptor.UUID_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION;
|
||||
|
||||
import androidx.annotation.RequiresPermission;
|
||||
|
||||
/**
|
||||
* Enables or disables notifications for a given GATT characteristic.
|
||||
* Enables or disables notifications for a given {@link BluetoothGattCharacteristic}.
|
||||
* The result will be made available asynchronously through the
|
||||
* {@link BluetoothGattCallback}.
|
||||
* {@link GattCallback#onDescriptorWrite(BluetoothGatt, BluetoothGattDescriptor, int)}.
|
||||
*/
|
||||
public class NotifyAction extends BtLEAction {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(NotifyAction.class);
|
||||
protected final boolean enableFlag;
|
||||
private final boolean enableFlag;
|
||||
private boolean hasWrittenDescriptor = false;
|
||||
|
||||
public NotifyAction(BluetoothGattCharacteristic characteristic, boolean enable) {
|
||||
@@ -107,25 +107,25 @@ public class NotifyAction extends BtLEAction {
|
||||
int properties = getCharacteristic().getProperties();
|
||||
|
||||
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
|
||||
LOG.debug("use NOTIFICATION for Characteristic " + getCharacteristic().getUuid());
|
||||
LOG.debug("use NOTIFICATION for Characteristic {}", getCharacteristic().getUuid());
|
||||
final byte[] value = enableFlag ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
|
||||
result = writeDescriptor(gatt, clientCharConfigDescriptor, value);
|
||||
hasWrittenDescriptor = true;
|
||||
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) {
|
||||
LOG.debug("use INDICATION for Characteristic " + getCharacteristic().getUuid());
|
||||
LOG.debug("use INDICATION for Characteristic {}", getCharacteristic().getUuid());
|
||||
final byte[] value = enableFlag ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
|
||||
result = writeDescriptor(gatt, clientCharConfigDescriptor, value);
|
||||
hasWrittenDescriptor = true;
|
||||
} else {
|
||||
LOG.debug("use neither NOTIFICATION nor INDICATION for Characteristic " + getCharacteristic().getUuid());
|
||||
LOG.debug("use neither NOTIFICATION nor INDICATION for Characteristic {}", getCharacteristic().getUuid());
|
||||
hasWrittenDescriptor = false;
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Descriptor CLIENT_CHARACTERISTIC_CONFIGURATION for characteristic " + getCharacteristic().getUuid() + " is null");
|
||||
LOG.warn("Descriptor CLIENT_CHARACTERISTIC_CONFIGURATION for characteristic {}", getCharacteristic().getUuid() + " is null");
|
||||
hasWrittenDescriptor = false;
|
||||
}
|
||||
} else {
|
||||
LOG.error("Unable to enable notifications for " + getCharacteristic().getUuid());
|
||||
LOG.error("Unable to enable notifications for {}", getCharacteristic().getUuid());
|
||||
hasWrittenDescriptor = false;
|
||||
}
|
||||
|
||||
@@ -136,4 +136,13 @@ public class NotifyAction extends BtLEAction {
|
||||
public boolean expectsResult() {
|
||||
return hasWrittenDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic();
|
||||
String uuid = characteristic == null ? "(null)" : characteristic.getUuid().toString();
|
||||
|
||||
return getCreationTime() + ": " + getClass().getSimpleName() + " " + uuid +
|
||||
(enableFlag ? " enable" : " disable");
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -16,11 +16,13 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
|
||||
|
||||
/**
|
||||
* An abstract non-BTLE action. It performs no bluetooth operation,
|
||||
* does not have a BluetoothGattCharacteristic instance and expects no result.
|
||||
* does not have a {@link BluetoothGattCharacteristic} instance and expects no result.
|
||||
*/
|
||||
public abstract class PlainAction extends BtLEAction {
|
||||
|
||||
|
||||
+10
-1
@@ -16,6 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
|
||||
@@ -26,7 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCallback;
|
||||
|
||||
/**
|
||||
* Invokes a read operation on a given GATT characteristic.
|
||||
* Invokes a read operation on a given {@link BluetoothGattCharacteristic}.
|
||||
* The result will be made available asynchronously through
|
||||
* {@link GattCallback#onCharacteristicRead}
|
||||
*/
|
||||
@@ -37,6 +38,7 @@ public class ReadAction extends BtLEAction {
|
||||
super(characteristic);
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
public boolean run(BluetoothGatt gatt) {
|
||||
int properties = getCharacteristic().getProperties();
|
||||
@@ -51,4 +53,11 @@ public class ReadAction extends BtLEAction {
|
||||
public boolean expectsResult() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic();
|
||||
String uuid = characteristic == null ? "(null)" : characteristic.getUuid().toString();
|
||||
return getCreationTime() + ": " + getClass().getSimpleName() + " " + uuid;
|
||||
}
|
||||
}
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/* Copyright (C) 2025 Thomas Kuehne
|
||||
|
||||
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 <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCallback;
|
||||
|
||||
|
||||
/// Calls {@link BluetoothGatt#readPhy()}. The result will be made available asynchronously through
|
||||
/// {@link GattCallback#onPhyRead(BluetoothGatt, int, int, int)}
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public class ReadPhyAction extends BtLEAction {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReadPhyAction.class);
|
||||
|
||||
public ReadPhyAction() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expectsResult() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
public boolean run(BluetoothGatt gatt) {
|
||||
try {
|
||||
gatt.readPhy();
|
||||
return true;
|
||||
} catch (final Throwable ex) {
|
||||
LOG.warn("BluetoothGatt.readPhy failed", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getCreationTime() + ": " + getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
+6
@@ -21,6 +21,7 @@ import android.bluetooth.BluetoothGatt;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
|
||||
|
||||
/// Calls {@link BluetoothGatt#requestConnectionPriority(int)}.
|
||||
public class RequestConnectionPriorityAction extends BtLEAction {
|
||||
private int priority;
|
||||
|
||||
@@ -39,4 +40,9 @@ public class RequestConnectionPriorityAction extends BtLEAction {
|
||||
public boolean run(final BluetoothGatt gatt) {
|
||||
return gatt.requestConnectionPriority(priority);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getCreationTime() + ": " + getClass().getSimpleName() + " priority=" + priority;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-5
@@ -16,16 +16,16 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCallback;
|
||||
|
||||
/// Calls {@link BluetoothGatt#requestMtu(int)}. Results are returned to
|
||||
/// {@link GattCallback#onMtuChanged(BluetoothGatt, int, int)}
|
||||
public class RequestMtuAction extends BtLEAction {
|
||||
private int mtu;
|
||||
private final int mtu;
|
||||
|
||||
public RequestMtuAction(int mtu) {
|
||||
super(null);
|
||||
@@ -38,8 +38,14 @@ public class RequestMtuAction extends BtLEAction {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
public boolean run(BluetoothGatt gatt) {
|
||||
return gatt.requestMtu(this.mtu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getCreationTime() + ": " + getClass().getSimpleName() + " mtu=" + mtu;
|
||||
}
|
||||
}
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/* Copyright (C) 2025 Thomas Kuehne
|
||||
|
||||
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 <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCallback;
|
||||
|
||||
|
||||
/// Calls {@link BluetoothGatt#setPreferredPhy(int, int, int)}. The result will be made available
|
||||
/// asynchronously through {@link GattCallback#onPhyUpdate(BluetoothGatt, int, int, int)}
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public class SetPreferredPhyAction extends BtLEAction {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SetPreferredPhyAction.class);
|
||||
|
||||
private final int mTxPhy;
|
||||
private final int mRxPhy;
|
||||
private final int mPhyOptions;
|
||||
|
||||
public SetPreferredPhyAction(int txPhy, int rxPhy, int phyOptions) {
|
||||
super(null);
|
||||
mTxPhy = txPhy;
|
||||
mRxPhy = rxPhy;
|
||||
mPhyOptions = phyOptions;
|
||||
}
|
||||
|
||||
/// {@link GattCallback#onPhyUpdate(BluetoothGatt, int, int, int)} is also triggered by device
|
||||
/// activity so can't use {@link BtLEQueue#mWaitForActionResultLatch} to wait for results
|
||||
@Override
|
||||
public boolean expectsResult() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
public boolean run(BluetoothGatt gatt) {
|
||||
try {
|
||||
gatt.setPreferredPhy(mTxPhy, mRxPhy, mPhyOptions);
|
||||
return true;
|
||||
} catch (final Throwable ex) {
|
||||
LOG.warn("BluetoothGatt.setPreferredPhy({}, {}, {}) failed", mTxPhy, mRxPhy,
|
||||
mPhyOptions, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getCreationTime() + ": " + getClass().getSimpleName() + " tx=" + mTxPhy
|
||||
+ " rx=" + mRxPhy + " opt=" + mPhyOptions;
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -19,7 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle.actions;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
|
||||
/**
|
||||
* An action that will cause the queue to sleep for the specified time.
|
||||
* An action that will cause the queue to {@link Thread#sleep(long) sleep} for the specified time.
|
||||
* Note that this is usually a bad idea, since it will not be able to process messages
|
||||
* during that time. It is also likely to cause race conditions.
|
||||
*/
|
||||
@@ -40,4 +40,9 @@ public class WaitAction extends PlainAction {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getCreationTime() + ": " + getClass().getSimpleName() + " " + mMillis + " ms";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user