RileyLinkBLE -> kt
This commit is contained in:
parent
8d21e849ae
commit
8e6f39dbe7
5 changed files with 454 additions and 648 deletions
|
@ -116,7 +116,7 @@ public class RFSpy {
|
|||
}
|
||||
|
||||
public Integer retrieveBatteryLevel() {
|
||||
BLECommOperationResult result = rileyLinkBle.readCharacteristic_blocking(batteryServiceUUID, batteryLevelUUID);
|
||||
BLECommOperationResult result = rileyLinkBle.readCharacteristicBlocking(batteryServiceUUID, batteryLevelUUID);
|
||||
if (result.resultCode == BLECommOperationResult.RESULT_SUCCESS) {
|
||||
if (ArrayUtils.isNotEmpty(result.value)) {
|
||||
int value = result.value[0];
|
||||
|
@ -134,7 +134,7 @@ public class RFSpy {
|
|||
// This gets the version from the BLE113, not from the CC1110.
|
||||
// I.e., this gets the version from the BLE interface, not from the radio.
|
||||
public String getVersion() {
|
||||
BLECommOperationResult result = rileyLinkBle.readCharacteristic_blocking(radioServiceUUID, radioVersionUUID);
|
||||
BLECommOperationResult result = rileyLinkBle.readCharacteristicBlocking(radioServiceUUID, radioVersionUUID);
|
||||
if (result.resultCode == BLECommOperationResult.RESULT_SUCCESS) {
|
||||
String version = StringUtil.fromBytes(result.value);
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "BLE Version: " + version);
|
||||
|
@ -208,7 +208,7 @@ public class RFSpy {
|
|||
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "writeToData (raw=%s)", ByteUtil.shortHexString(prepended)));
|
||||
|
||||
BLECommOperationResult writeCheck = rileyLinkBle.writeCharacteristic_blocking(radioServiceUUID, radioDataUUID,
|
||||
BLECommOperationResult writeCheck = rileyLinkBle.writeCharacteristicBlocking(radioServiceUUID, radioDataUUID,
|
||||
prepended);
|
||||
if (writeCheck.resultCode != BLECommOperationResult.RESULT_SUCCESS) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "BLE Write operation failed, code=" + writeCheck.resultCode);
|
||||
|
|
|
@ -107,7 +107,7 @@ public class RFSpyReader {
|
|||
+ SystemClock.uptimeMillis());
|
||||
SystemClock.sleep(100);
|
||||
SystemClock.sleep(1);
|
||||
result = rileyLinkBle.readCharacteristic_blocking(serviceUUID, radioDataUUID);
|
||||
result = rileyLinkBle.readCharacteristicBlocking(serviceUUID, radioDataUUID);
|
||||
SystemClock.sleep(100);
|
||||
|
||||
if (result.resultCode == BLECommOperationResult.RESULT_SUCCESS) {
|
||||
|
|
|
@ -1,639 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.shared.logging.AAPSLogger;
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.device.OrangeLinkImpl;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperation;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperationResult;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.CharacteristicReadOperation;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.CharacteristicWriteOperation;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.DescriptorWriteOperation;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState;
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData;
|
||||
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
|
||||
import info.nightscout.androidaps.plugins.pump.common.utils.ThreadUtil;
|
||||
import info.nightscout.shared.sharedPreferences.SP;
|
||||
|
||||
/**
|
||||
* Created by geoff on 5/26/16.
|
||||
* Added: State handling, configuration of RF for different configuration ranges, connection handling
|
||||
*/
|
||||
@Singleton
|
||||
public class RileyLinkBLE {
|
||||
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject RileyLinkServiceData rileyLinkServiceData;
|
||||
@Inject RileyLinkUtil rileyLinkUtil;
|
||||
@Inject SP sp;
|
||||
@Inject OrangeLinkImpl orangeLink;
|
||||
|
||||
private final Context context;
|
||||
private final boolean gattDebugEnabled = true;
|
||||
private boolean manualDisconnect = false;
|
||||
private final BluetoothAdapter bluetoothAdapter;
|
||||
private final BluetoothGattCallback bluetoothGattCallback;
|
||||
private BluetoothDevice rileyLinkDevice;
|
||||
private BluetoothGatt bluetoothConnectionGatt = null;
|
||||
private BLECommOperation mCurrentOperation;
|
||||
private final Semaphore gattOperationSema = new Semaphore(1, true);
|
||||
private Runnable radioResponseCountNotified;
|
||||
private boolean mIsConnected = false;
|
||||
|
||||
@Inject
|
||||
public RileyLinkBLE(final Context context) {
|
||||
this.context = context;
|
||||
this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
//orangeLink.rileyLinkBLE = this;
|
||||
|
||||
bluetoothGattCallback = new BluetoothGattCallback() {
|
||||
|
||||
@Override
|
||||
public void onCharacteristicChanged(final BluetoothGatt gatt,
|
||||
final BluetoothGattCharacteristic characteristic) {
|
||||
super.onCharacteristicChanged(gatt, characteristic);
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, ThreadUtil.sig() + "onCharacteristicChanged "
|
||||
+ GattAttributes.lookup(characteristic.getUuid()) + " "
|
||||
+ ByteUtil.getHex(characteristic.getValue()));
|
||||
if (characteristic.getUuid().equals(UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT))) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Response Count is " + ByteUtil.shortHexString(characteristic.getValue()));
|
||||
}
|
||||
}
|
||||
if (characteristic.getUuid().equals(UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT))) {
|
||||
if (radioResponseCountNotified != null) {
|
||||
radioResponseCountNotified.run();
|
||||
}
|
||||
}
|
||||
orangeLink.onCharacteristicChanged(characteristic);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCharacteristicRead(final BluetoothGatt gatt,
|
||||
final BluetoothGattCharacteristic characteristic, int status) {
|
||||
super.onCharacteristicRead(gatt, characteristic, status);
|
||||
|
||||
final String statusMessage = getGattStatusMessage(status);
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, ThreadUtil.sig() + "onCharacteristicRead ("
|
||||
+ GattAttributes.lookup(characteristic.getUuid()) + ") " + statusMessage + ":"
|
||||
+ ByteUtil.getHex(characteristic.getValue()));
|
||||
}
|
||||
mCurrentOperation.gattOperationCompletionCallback(characteristic.getUuid(), characteristic.getValue());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCharacteristicWrite(final BluetoothGatt gatt,
|
||||
final BluetoothGattCharacteristic characteristic, int status) {
|
||||
super.onCharacteristicWrite(gatt, characteristic, status);
|
||||
|
||||
final String uuidString = GattAttributes.lookup(characteristic.getUuid());
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, ThreadUtil.sig() + "onCharacteristicWrite " + getGattStatusMessage(status) + " "
|
||||
+ uuidString + " " + ByteUtil.shortHexString(characteristic.getValue()));
|
||||
}
|
||||
mCurrentOperation.gattOperationCompletionCallback(characteristic.getUuid(), characteristic.getValue());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
|
||||
super.onConnectionStateChange(gatt, status, newState);
|
||||
|
||||
// https://github.com/NordicSemiconductor/puck-central-android/blob/master/PuckCentral/app/src/main/java/no/nordicsemi/puckcentral/bluetooth/gatt/GattManager.java#L117
|
||||
if (status == 133) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "Got the status 133 bug, closing gatt");
|
||||
disconnect();
|
||||
SystemClock.sleep(500);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gattDebugEnabled) {
|
||||
final String stateMessage;
|
||||
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||
stateMessage = "CONNECTED";
|
||||
} else if (newState == BluetoothProfile.STATE_CONNECTING) {
|
||||
stateMessage = "CONNECTING";
|
||||
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||||
stateMessage = "DISCONNECTED";
|
||||
} else if (newState == BluetoothProfile.STATE_DISCONNECTING) {
|
||||
stateMessage = "DISCONNECTING";
|
||||
} else {
|
||||
stateMessage = "UNKNOWN newState (" + newState + ")";
|
||||
}
|
||||
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onConnectionStateChange " + getGattStatusMessage(status) + " " + stateMessage);
|
||||
}
|
||||
|
||||
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
rileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.BluetoothConnected, context);
|
||||
} else {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "BT State connected, GATT status %d (%s)", status, getGattStatusMessage(status)));
|
||||
}
|
||||
|
||||
} else if ((newState == BluetoothProfile.STATE_CONNECTING) || //
|
||||
(newState == BluetoothProfile.STATE_DISCONNECTING)) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "We are in %s state.", status == BluetoothProfile.STATE_CONNECTING ? "Connecting" :
|
||||
"Disconnecting"));
|
||||
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||||
rileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkDisconnected, context);
|
||||
if (manualDisconnect)
|
||||
close();
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "RileyLink Disconnected.");
|
||||
} else {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "Some other state: (status=%d, newState=%d)", status, newState));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
super.onDescriptorWrite(gatt, descriptor, status);
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onDescriptorWrite " + GattAttributes.lookup(descriptor.getUuid()) + " "
|
||||
+ getGattStatusMessage(status) + " written: " + ByteUtil.getHex(descriptor.getValue()));
|
||||
}
|
||||
mCurrentOperation.gattOperationCompletionCallback(descriptor.getUuid(), descriptor.getValue());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
super.onDescriptorRead(gatt, descriptor, status);
|
||||
mCurrentOperation.gattOperationCompletionCallback(descriptor.getUuid(), descriptor.getValue());
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onDescriptorRead " + getGattStatusMessage(status) + " status " + descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||
super.onMtuChanged(gatt, mtu, status);
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onMtuChanged " + mtu + " status " + status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onReadRemoteRssi(final BluetoothGatt gatt, int rssi, int status) {
|
||||
super.onReadRemoteRssi(gatt, rssi, status);
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onReadRemoteRssi " + getGattStatusMessage(status) + ": " + rssi);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||
super.onReliableWriteCompleted(gatt, status);
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onReliableWriteCompleted status " + status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
|
||||
super.onServicesDiscovered(gatt, status);
|
||||
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
final List<BluetoothGattService> services = gatt.getServices();
|
||||
|
||||
boolean rileyLinkFound = false;
|
||||
orangeLink.resetOrangeLinkData();
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder("RileyLink Device Debug\n");
|
||||
|
||||
for (BluetoothGattService service : services) {
|
||||
final UUID uuidService = service.getUuid();
|
||||
|
||||
if (isAnyRileyLinkServiceFound(service)) {
|
||||
rileyLinkFound = true;
|
||||
}
|
||||
|
||||
if (gattDebugEnabled) {
|
||||
debugService(service, 0, stringBuilder);
|
||||
}
|
||||
|
||||
orangeLink.checkIsOrange(uuidService);
|
||||
}
|
||||
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, stringBuilder.toString());
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onServicesDiscovered " + getGattStatusMessage(status));
|
||||
}
|
||||
|
||||
aapsLogger.info(LTag.PUMPBTCOMM, "Gatt device is RileyLink device: " + rileyLinkFound);
|
||||
|
||||
if (rileyLinkFound) {
|
||||
mIsConnected = true;
|
||||
rileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkReady, context);
|
||||
} else {
|
||||
mIsConnected = false;
|
||||
rileyLinkServiceData.setServiceState(RileyLinkServiceState.RileyLinkError,
|
||||
RileyLinkError.DeviceIsNotRileyLink);
|
||||
}
|
||||
|
||||
} else {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "onServicesDiscovered " + getGattStatusMessage(status));
|
||||
rileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkGattFailed, context);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void onInit() {
|
||||
//aapsLogger.debug(LTag.PUMPBTCOMM, "BT Adapter: " + this.bluetoothAdapter);
|
||||
this.orangeLink.rileyLinkBLE = this;
|
||||
}
|
||||
|
||||
|
||||
private boolean isAnyRileyLinkServiceFound(BluetoothGattService service) {
|
||||
|
||||
boolean found = GattAttributes.isRileyLink(service.getUuid());
|
||||
|
||||
if (found) {
|
||||
return true;
|
||||
} else {
|
||||
List<BluetoothGattService> includedServices = service.getIncludedServices();
|
||||
|
||||
for (BluetoothGattService serviceI : includedServices) {
|
||||
if (isAnyRileyLinkServiceFound(serviceI)) {
|
||||
return true;
|
||||
}
|
||||
orangeLink.checkIsOrange(serviceI.getUuid());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public BluetoothDevice getRileyLinkDevice() {
|
||||
return this.rileyLinkDevice;
|
||||
}
|
||||
|
||||
|
||||
public void debugService(BluetoothGattService service, int indentCount, StringBuilder stringBuilder) {
|
||||
|
||||
String indentString = StringUtils.repeat(' ', indentCount);
|
||||
|
||||
final UUID uuidService = service.getUuid();
|
||||
|
||||
if (gattDebugEnabled) {
|
||||
final String uuidServiceString = uuidService.toString();
|
||||
|
||||
//StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
stringBuilder.append(indentString);
|
||||
stringBuilder.append(GattAttributes.lookup(uuidServiceString, "Unknown service"));
|
||||
stringBuilder.append(" (" + uuidServiceString + ")");
|
||||
|
||||
for (BluetoothGattCharacteristic character : service.getCharacteristics()) {
|
||||
final String uuidCharacteristicString = character.getUuid().toString();
|
||||
|
||||
stringBuilder.append("\n ");
|
||||
stringBuilder.append(indentString);
|
||||
stringBuilder.append(" - " + GattAttributes.lookup(uuidCharacteristicString, "Unknown Characteristic"));
|
||||
stringBuilder.append(" (" + uuidCharacteristicString + ")");
|
||||
}
|
||||
|
||||
stringBuilder.append("\n\n");
|
||||
|
||||
//aapsLogger.warn(LTag.PUMPBTCOMM, stringBuilder.toString());
|
||||
|
||||
List<BluetoothGattService> includedServices = service.getIncludedServices();
|
||||
|
||||
for (BluetoothGattService serviceI : includedServices) {
|
||||
debugService(serviceI, indentCount + 4, stringBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void registerRadioResponseCountNotification(Runnable notifier) {
|
||||
radioResponseCountNotified = notifier;
|
||||
}
|
||||
|
||||
|
||||
public boolean isConnected() {
|
||||
return mIsConnected;
|
||||
}
|
||||
|
||||
|
||||
public boolean discoverServices() {
|
||||
|
||||
if (bluetoothConnectionGatt == null) {
|
||||
// shouldn't happen, but if it does we exit
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bluetoothConnectionGatt.discoverServices()) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Starting to discover GATT Services.");
|
||||
return true;
|
||||
} else {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "Cannot discover GATT Services.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean enableNotifications() {
|
||||
BLECommOperationResult result = setNotification_blocking(UUID.fromString(GattAttributes.SERVICE_RADIO), //
|
||||
UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT));
|
||||
if (result.resultCode != BLECommOperationResult.RESULT_SUCCESS) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "Error setting response count notification");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rileyLinkServiceData.isOrange) {
|
||||
return orangeLink.enableNotifications();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void findRileyLink(String rileyLinkAddress) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "RileyLink address: " + rileyLinkAddress);
|
||||
// Must verify that this is a valid MAC, or crash.
|
||||
//macAddress = RileyLinkAddress;
|
||||
boolean useScanning = sp.getBoolean(RileyLinkConst.Prefs.OrangeUseScanning, false);
|
||||
if (useScanning) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Start scan for OrangeLink device.");
|
||||
orangeLink.startScan();
|
||||
} else {
|
||||
rileyLinkDevice = bluetoothAdapter.getRemoteDevice(rileyLinkAddress);
|
||||
// if this succeeds, we get a connection state change callback?
|
||||
if (rileyLinkDevice != null) {
|
||||
connectGattInternal();
|
||||
} else {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "RileyLink device not found with address: " + rileyLinkAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void connectGatt() {
|
||||
boolean useScanning = sp.getBoolean(RileyLinkConst.Prefs.OrangeUseScanning, false);
|
||||
if (useScanning) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Start scan for OrangeLink device.");
|
||||
orangeLink.startScan();
|
||||
} else {
|
||||
connectGattInternal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This function must be run on UI thread.
|
||||
public void connectGattInternal() {
|
||||
if (this.rileyLinkDevice == null) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "RileyLink device is null, can't do connectGatt.");
|
||||
return;
|
||||
}
|
||||
|
||||
bluetoothConnectionGatt = rileyLinkDevice.connectGatt(context, true, bluetoothGattCallback);
|
||||
// , BluetoothDevice.TRANSPORT_LE
|
||||
if (bluetoothConnectionGatt == null) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "Failed to connect to Bluetooth Low Energy device at " + bluetoothAdapter.getAddress());
|
||||
} else {
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Gatt Connected.");
|
||||
}
|
||||
|
||||
String deviceName = bluetoothConnectionGatt.getDevice().getName();
|
||||
if (StringUtils.isNotEmpty(deviceName)) {
|
||||
// Update stored name upon connecting (also for backwards compatibility for device where a name was not yet stored)
|
||||
sp.putString(RileyLinkConst.Prefs.RileyLinkName, deviceName);
|
||||
} else {
|
||||
sp.remove(RileyLinkConst.Prefs.RileyLinkName);
|
||||
}
|
||||
|
||||
rileyLinkServiceData.rileyLinkName = deviceName;
|
||||
rileyLinkServiceData.rileyLinkAddress = bluetoothConnectionGatt.getDevice().getAddress();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void disconnect() {
|
||||
mIsConnected = false;
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Closing GATT connection");
|
||||
// Close old conenction
|
||||
if (bluetoothConnectionGatt != null) {
|
||||
// Not sure if to disconnect or to close first..
|
||||
bluetoothConnectionGatt.disconnect();
|
||||
manualDisconnect = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void close() {
|
||||
if (bluetoothConnectionGatt != null) {
|
||||
bluetoothConnectionGatt.close();
|
||||
bluetoothConnectionGatt = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public BLECommOperationResult setNotification_blocking(UUID serviceUUID, UUID charaUUID) {
|
||||
BLECommOperationResult rval = new BLECommOperationResult();
|
||||
if (bluetoothConnectionGatt != null) {
|
||||
|
||||
try {
|
||||
gattOperationSema.acquire();
|
||||
SystemClock.sleep(1); // attempting to yield thread, to make sequence of events easier to follow
|
||||
} catch (InterruptedException e) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "setNotification_blocking: interrupted waiting for gattOperationSema");
|
||||
return rval;
|
||||
}
|
||||
if (mCurrentOperation != null) {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_BUSY;
|
||||
} else {
|
||||
if (bluetoothConnectionGatt.getService(serviceUUID) == null) {
|
||||
// Catch if the service is not supported by the BLE device
|
||||
rval.resultCode = BLECommOperationResult.RESULT_NONE;
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "BT Device not supported");
|
||||
// TODO: 11/07/2016 UI update for user
|
||||
// xyz rileyLinkServiceData.setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.NoBluetoothAdapter);
|
||||
} else {
|
||||
BluetoothGattCharacteristic chara = bluetoothConnectionGatt.getService(serviceUUID)
|
||||
.getCharacteristic(charaUUID);
|
||||
// Tell Android that we want the notifications
|
||||
bluetoothConnectionGatt.setCharacteristicNotification(chara, true);
|
||||
List<BluetoothGattDescriptor> list = chara.getDescriptors();
|
||||
if (gattDebugEnabled) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Found descriptor: " + list.get(i).toString());
|
||||
}
|
||||
}
|
||||
BluetoothGattDescriptor descr = list.get(0);
|
||||
// Tell the remote device to send the notifications
|
||||
mCurrentOperation = new DescriptorWriteOperation(aapsLogger, bluetoothConnectionGatt, descr,
|
||||
BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
|
||||
mCurrentOperation.execute(this);
|
||||
if (mCurrentOperation.timedOut) {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_TIMEOUT;
|
||||
} else if (mCurrentOperation.interrupted) {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_INTERRUPTED;
|
||||
} else {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_SUCCESS;
|
||||
}
|
||||
}
|
||||
mCurrentOperation = null;
|
||||
gattOperationSema.release();
|
||||
}
|
||||
} else {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "setNotification_blocking: not configured!");
|
||||
rval.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
// call from main
|
||||
BLECommOperationResult writeCharacteristic_blocking(UUID serviceUUID, UUID charaUUID, byte[] value) {
|
||||
BLECommOperationResult rval = new BLECommOperationResult();
|
||||
if (bluetoothConnectionGatt != null) {
|
||||
rval.value = value;
|
||||
try {
|
||||
gattOperationSema.acquire();
|
||||
SystemClock.sleep(1); // attempting to yield thread, to make sequence of events easier to follow
|
||||
} catch (InterruptedException e) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "writeCharacteristic_blocking: interrupted waiting for gattOperationSema");
|
||||
return rval;
|
||||
}
|
||||
|
||||
if (mCurrentOperation != null) {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_BUSY;
|
||||
} else {
|
||||
if (bluetoothConnectionGatt.getService(serviceUUID) == null) {
|
||||
// Catch if the service is not supported by the BLE device
|
||||
// GGW: Tue Jul 12 01:14:01 UTC 2016: This can also happen if the
|
||||
// app that created the bluetoothConnectionGatt has been destroyed/created,
|
||||
// e.g. when the user switches from portrait to landscape.
|
||||
rval.resultCode = BLECommOperationResult.RESULT_NONE;
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "BT Device not supported");
|
||||
// TODO: 11/07/2016 UI update for user
|
||||
// xyz rileyLinkServiceData.setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.NoBluetoothAdapter);
|
||||
} else {
|
||||
BluetoothGattCharacteristic chara = bluetoothConnectionGatt.getService(serviceUUID)
|
||||
.getCharacteristic(charaUUID);
|
||||
mCurrentOperation = new CharacteristicWriteOperation(aapsLogger, bluetoothConnectionGatt, chara, value);
|
||||
mCurrentOperation.execute(this);
|
||||
if (mCurrentOperation.timedOut) {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_TIMEOUT;
|
||||
} else if (mCurrentOperation.interrupted) {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_INTERRUPTED;
|
||||
} else {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_SUCCESS;
|
||||
}
|
||||
}
|
||||
mCurrentOperation = null;
|
||||
gattOperationSema.release();
|
||||
}
|
||||
} else {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "writeCharacteristic_blocking: not configured!");
|
||||
rval.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
BLECommOperationResult readCharacteristic_blocking(UUID serviceUUID, UUID charaUUID) {
|
||||
BLECommOperationResult rval = new BLECommOperationResult();
|
||||
if (bluetoothConnectionGatt != null) {
|
||||
try {
|
||||
gattOperationSema.acquire();
|
||||
SystemClock.sleep(1); // attempting to yield thread, to make sequence of events easier to follow
|
||||
} catch (InterruptedException e) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "readCharacteristic_blocking: Interrupted waiting for gattOperationSema");
|
||||
return rval;
|
||||
}
|
||||
if (mCurrentOperation != null) {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_BUSY;
|
||||
} else {
|
||||
if (bluetoothConnectionGatt.getService(serviceUUID) == null) {
|
||||
// Catch if the service is not supported by the BLE device
|
||||
rval.resultCode = BLECommOperationResult.RESULT_NONE;
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "BT Device not supported");
|
||||
// TODO: 11/07/2016 UI update for user
|
||||
// xyz rileyLinkServiceData.setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.NoBluetoothAdapter);
|
||||
} else {
|
||||
BluetoothGattCharacteristic chara = bluetoothConnectionGatt.getService(serviceUUID).getCharacteristic(
|
||||
charaUUID);
|
||||
mCurrentOperation = new CharacteristicReadOperation(aapsLogger, bluetoothConnectionGatt, chara);
|
||||
mCurrentOperation.execute(this);
|
||||
if (mCurrentOperation.timedOut) {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_TIMEOUT;
|
||||
} else if (mCurrentOperation.interrupted) {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_INTERRUPTED;
|
||||
} else {
|
||||
rval.resultCode = BLECommOperationResult.RESULT_SUCCESS;
|
||||
rval.value = mCurrentOperation.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
mCurrentOperation = null;
|
||||
gattOperationSema.release();
|
||||
} else {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "readCharacteristic_blocking: not configured!");
|
||||
rval.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
private String getGattStatusMessage(final int status) {
|
||||
final String statusMessage;
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
statusMessage = "SUCCESS";
|
||||
} else if (status == BluetoothGatt.GATT_FAILURE) {
|
||||
statusMessage = "FAILED";
|
||||
} else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) {
|
||||
statusMessage = "NOT PERMITTED";
|
||||
} else if (status == 133) {
|
||||
statusMessage = "Found the strange 133 bug";
|
||||
} else {
|
||||
statusMessage = "UNKNOWN (" + status + ")";
|
||||
}
|
||||
|
||||
return statusMessage;
|
||||
}
|
||||
|
||||
public void setRileyLinkDevice(BluetoothDevice device) {
|
||||
this.rileyLinkDevice = device;
|
||||
}
|
||||
|
||||
public BluetoothAdapter getBluetoothAdapter() {
|
||||
return bluetoothAdapter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,447 @@
|
|||
package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.bluetooth.*
|
||||
import android.content.Context
|
||||
import android.os.SystemClock
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.device.OrangeLinkImpl
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperation
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperationResult
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.CharacteristicReadOperation
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.CharacteristicWriteOperation
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.DescriptorWriteOperation
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState
|
||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData
|
||||
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil
|
||||
import info.nightscout.androidaps.plugins.pump.common.utils.ThreadUtil
|
||||
import info.nightscout.shared.logging.AAPSLogger
|
||||
import info.nightscout.shared.logging.LTag
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.util.*
|
||||
import java.util.concurrent.Semaphore
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Created by geoff on 5/26/16.
|
||||
* Added: State handling, configuration of RF for different configuration ranges, connection handling
|
||||
*/
|
||||
@Singleton
|
||||
class RileyLinkBLE @Inject constructor(private val context: Context) {
|
||||
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var rileyLinkServiceData: RileyLinkServiceData
|
||||
@Inject lateinit var rileyLinkUtil: RileyLinkUtil
|
||||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var orangeLink: OrangeLinkImpl
|
||||
|
||||
private val gattDebugEnabled = true
|
||||
private var manualDisconnect = false
|
||||
val bluetoothAdapter: BluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
|
||||
private val bluetoothGattCallback: BluetoothGattCallback
|
||||
var rileyLinkDevice: BluetoothDevice? = null
|
||||
private var bluetoothConnectionGatt: BluetoothGatt? = null
|
||||
private var mCurrentOperation: BLECommOperation? = null
|
||||
private val gattOperationSema = Semaphore(1, true)
|
||||
private var radioResponseCountNotified: Runnable? = null
|
||||
var isConnected = false
|
||||
private set
|
||||
|
||||
@Inject fun onInit() {
|
||||
//aapsLogger.debug(LTag.PUMPBTCOMM, "BT Adapter: " + this.bluetoothAdapter);
|
||||
orangeLink.rileyLinkBLE = this
|
||||
}
|
||||
|
||||
private fun isAnyRileyLinkServiceFound(service: BluetoothGattService): Boolean {
|
||||
val found = GattAttributes.isRileyLink(service.uuid)
|
||||
if (found) return true
|
||||
else
|
||||
for (serviceI in service.includedServices) {
|
||||
if (isAnyRileyLinkServiceFound(serviceI)) return true
|
||||
orangeLink.checkIsOrange(serviceI.uuid)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun debugService(service: BluetoothGattService, indentCount: Int, stringBuilder: StringBuilder) {
|
||||
val indentString = StringUtils.repeat(' ', indentCount)
|
||||
if (gattDebugEnabled) {
|
||||
val uuidServiceString = service.uuid.toString()
|
||||
|
||||
stringBuilder.append(indentString)
|
||||
stringBuilder.append(GattAttributes.lookup(uuidServiceString, "Unknown service"))
|
||||
stringBuilder.append(" ($uuidServiceString)")
|
||||
for (character in service.characteristics) {
|
||||
val uuidCharacteristicString = character.uuid.toString()
|
||||
stringBuilder.append("\n ")
|
||||
stringBuilder.append(indentString)
|
||||
stringBuilder.append(" - " + GattAttributes.lookup(uuidCharacteristicString, "Unknown Characteristic"))
|
||||
stringBuilder.append(" ($uuidCharacteristicString)")
|
||||
}
|
||||
stringBuilder.append("\n\n")
|
||||
|
||||
//aapsLogger.warn(LTag.PUMPBTCOMM, stringBuilder.toString());
|
||||
for (serviceI in service.includedServices) {
|
||||
debugService(serviceI, indentCount + 4, stringBuilder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun registerRadioResponseCountNotification(notifier: Runnable?) {
|
||||
radioResponseCountNotified = notifier
|
||||
}
|
||||
|
||||
fun discoverServices(): Boolean {
|
||||
// shouldn't happen, but if it does we exit
|
||||
bluetoothConnectionGatt ?: return false
|
||||
|
||||
return if (bluetoothConnectionGatt?.discoverServices() == true) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Starting to discover GATT Services.")
|
||||
true
|
||||
} else {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "Cannot discover GATT Services.")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun enableNotifications(): Boolean {
|
||||
val result = setNotificationBlocking(UUID.fromString(GattAttributes.SERVICE_RADIO), UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT))
|
||||
if (result.resultCode != BLECommOperationResult.RESULT_SUCCESS) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "Error setting response count notification")
|
||||
return false
|
||||
}
|
||||
return if (rileyLinkServiceData.isOrange) orangeLink.enableNotifications()
|
||||
else true
|
||||
}
|
||||
|
||||
fun findRileyLink(rileyLinkAddress: String) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "RileyLink address: $rileyLinkAddress")
|
||||
// Must verify that this is a valid MAC, or crash.
|
||||
//macAddress = RileyLinkAddress;
|
||||
val useScanning = sp.getBoolean(RileyLinkConst.Prefs.OrangeUseScanning, false)
|
||||
if (useScanning) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Start scan for OrangeLink device.")
|
||||
orangeLink.startScan()
|
||||
} else {
|
||||
rileyLinkDevice = bluetoothAdapter.getRemoteDevice(rileyLinkAddress)
|
||||
// if this succeeds, we get a connection state change callback?
|
||||
if (rileyLinkDevice != null) connectGattInternal()
|
||||
else aapsLogger.error(LTag.PUMPBTCOMM, "RileyLink device not found with address: $rileyLinkAddress")
|
||||
}
|
||||
}
|
||||
|
||||
fun connectGatt() {
|
||||
val useScanning = sp.getBoolean(RileyLinkConst.Prefs.OrangeUseScanning, false)
|
||||
if (useScanning) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Start scan for OrangeLink device.")
|
||||
orangeLink.startScan()
|
||||
} else {
|
||||
connectGattInternal()
|
||||
}
|
||||
}
|
||||
|
||||
// This function must be run on UI thread.
|
||||
@SuppressLint("HardwareIds")
|
||||
fun connectGattInternal() {
|
||||
if (rileyLinkDevice == null) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "RileyLink device is null, can't do connectGatt.")
|
||||
return
|
||||
}
|
||||
bluetoothConnectionGatt = rileyLinkDevice?.connectGatt(context, true, bluetoothGattCallback)
|
||||
// , BluetoothDevice.TRANSPORT_LE
|
||||
if (bluetoothConnectionGatt == null)
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "Failed to connect to Bluetooth Low Energy device at " + bluetoothAdapter.address)
|
||||
else {
|
||||
if (gattDebugEnabled) aapsLogger.debug(LTag.PUMPBTCOMM, "Gatt Connected.")
|
||||
val deviceName = bluetoothConnectionGatt?.device?.name
|
||||
// Update stored name upon connecting (also for backwards compatibility for device where a name was not yet stored)
|
||||
if (StringUtils.isNotEmpty(deviceName)) sp.putString(RileyLinkConst.Prefs.RileyLinkName, deviceName!!)
|
||||
else sp.remove(RileyLinkConst.Prefs.RileyLinkName)
|
||||
rileyLinkServiceData.rileyLinkName = deviceName
|
||||
rileyLinkServiceData.rileyLinkAddress = bluetoothConnectionGatt?.device?.address
|
||||
}
|
||||
}
|
||||
|
||||
fun disconnect() {
|
||||
isConnected = false
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Closing GATT connection")
|
||||
// Close old connection
|
||||
if (bluetoothConnectionGatt != null) {
|
||||
// Not sure if to disconnect or to close first..
|
||||
bluetoothConnectionGatt?.disconnect()
|
||||
manualDisconnect = true
|
||||
}
|
||||
}
|
||||
|
||||
fun close() {
|
||||
bluetoothConnectionGatt?.close()
|
||||
bluetoothConnectionGatt = null
|
||||
}
|
||||
|
||||
fun setNotificationBlocking(serviceUUID: UUID?, charaUUID: UUID?): BLECommOperationResult {
|
||||
val retValue = BLECommOperationResult()
|
||||
if (bluetoothConnectionGatt == null) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "setNotification_blocking: not configured!")
|
||||
retValue.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED
|
||||
return retValue
|
||||
}
|
||||
gattOperationSema.acquire()
|
||||
SystemClock.sleep(1) // attempting to yield thread, to make sequence of events easier to follow
|
||||
if (mCurrentOperation != null) retValue.resultCode = BLECommOperationResult.RESULT_BUSY
|
||||
else {
|
||||
if (bluetoothConnectionGatt?.getService(serviceUUID) == null) {
|
||||
// Catch if the service is not supported by the BLE device
|
||||
retValue.resultCode = BLECommOperationResult.RESULT_NONE
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "BT Device not supported")
|
||||
// TODO: 11/07/2016 UI update for user
|
||||
// xyz rileyLinkServiceData.setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.NoBluetoothAdapter);
|
||||
} else {
|
||||
val chara = bluetoothConnectionGatt?.getService(serviceUUID)?.getCharacteristic(charaUUID) ?: return retValue.apply { resultCode = BLECommOperationResult.RESULT_NONE }
|
||||
// Tell Android that we want the notifications
|
||||
bluetoothConnectionGatt?.setCharacteristicNotification(chara, true)
|
||||
val list = chara.descriptors
|
||||
if (gattDebugEnabled) for (i in list.indices) aapsLogger.debug(LTag.PUMPBTCOMM, "Found descriptor: " + list[i].toString())
|
||||
// Tell the remote device to send the notifications
|
||||
mCurrentOperation = DescriptorWriteOperation(aapsLogger, bluetoothConnectionGatt, list[0], BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
|
||||
mCurrentOperation?.execute(this)
|
||||
when {
|
||||
mCurrentOperation?.timedOut == true -> retValue.resultCode = BLECommOperationResult.RESULT_TIMEOUT
|
||||
mCurrentOperation?.interrupted == true -> retValue.resultCode = BLECommOperationResult.RESULT_INTERRUPTED
|
||||
else -> retValue.resultCode = BLECommOperationResult.RESULT_SUCCESS
|
||||
}
|
||||
}
|
||||
mCurrentOperation = null
|
||||
gattOperationSema.release()
|
||||
}
|
||||
return retValue
|
||||
}
|
||||
|
||||
// call from main
|
||||
fun writeCharacteristicBlocking(serviceUUID: UUID?, charaUUID: UUID?, value: ByteArray?): BLECommOperationResult {
|
||||
val retValue = BLECommOperationResult()
|
||||
if (bluetoothConnectionGatt == null) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "writeCharacteristic_blocking: not configured!")
|
||||
retValue.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED
|
||||
return retValue
|
||||
}
|
||||
retValue.value = value
|
||||
gattOperationSema.acquire()
|
||||
SystemClock.sleep(1) // attempting to yield thread, to make sequence of events easier to follow
|
||||
if (mCurrentOperation != null) retValue.resultCode = BLECommOperationResult.RESULT_BUSY
|
||||
else {
|
||||
if (bluetoothConnectionGatt?.getService(serviceUUID) == null) {
|
||||
// Catch if the service is not supported by the BLE device
|
||||
// GGW: Tue Jul 12 01:14:01 UTC 2016: This can also happen if the
|
||||
// app that created the bluetoothConnectionGatt has been destroyed/created,
|
||||
// e.g. when the user switches from portrait to landscape.
|
||||
retValue.resultCode = BLECommOperationResult.RESULT_NONE
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "BT Device not supported")
|
||||
// TODO: 11/07/2016 UI update for user
|
||||
// xyz rileyLinkServiceData.setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.NoBluetoothAdapter);
|
||||
} else {
|
||||
val chara = bluetoothConnectionGatt?.getService(serviceUUID)?.getCharacteristic(charaUUID) ?: return retValue.apply { resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED }
|
||||
mCurrentOperation = CharacteristicWriteOperation(aapsLogger, bluetoothConnectionGatt, chara, value)
|
||||
mCurrentOperation?.execute(this)
|
||||
when {
|
||||
mCurrentOperation?.timedOut == true -> retValue.resultCode = BLECommOperationResult.RESULT_TIMEOUT
|
||||
mCurrentOperation?.interrupted == true -> retValue.resultCode = BLECommOperationResult.RESULT_INTERRUPTED
|
||||
else -> retValue.resultCode = BLECommOperationResult.RESULT_SUCCESS
|
||||
}
|
||||
}
|
||||
mCurrentOperation = null
|
||||
gattOperationSema.release()
|
||||
}
|
||||
return retValue
|
||||
}
|
||||
|
||||
fun readCharacteristicBlocking(serviceUUID: UUID?, charaUUID: UUID?): BLECommOperationResult {
|
||||
val retValue = BLECommOperationResult()
|
||||
if (bluetoothConnectionGatt == null) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "readCharacteristic_blocking: not configured!")
|
||||
retValue.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED
|
||||
return retValue
|
||||
}
|
||||
|
||||
gattOperationSema.acquire()
|
||||
SystemClock.sleep(1) // attempting to yield thread, to make sequence of events easier to follow
|
||||
if (mCurrentOperation != null) retValue.resultCode = BLECommOperationResult.RESULT_BUSY
|
||||
else {
|
||||
if (bluetoothConnectionGatt?.getService(serviceUUID) == null) {
|
||||
// Catch if the service is not supported by the BLE device
|
||||
retValue.resultCode = BLECommOperationResult.RESULT_NONE
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "BT Device not supported")
|
||||
// TODO: 11/07/2016 UI update for user
|
||||
// xyz rileyLinkServiceData.setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.NoBluetoothAdapter);
|
||||
} else {
|
||||
val chara = bluetoothConnectionGatt?.getService(serviceUUID)?.getCharacteristic(charaUUID) ?: return retValue.apply { resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED }
|
||||
mCurrentOperation = CharacteristicReadOperation(aapsLogger, bluetoothConnectionGatt, chara)
|
||||
mCurrentOperation?.execute(this)
|
||||
when {
|
||||
mCurrentOperation?.timedOut == true -> retValue.resultCode = BLECommOperationResult.RESULT_TIMEOUT
|
||||
mCurrentOperation?.interrupted == true -> retValue.resultCode = BLECommOperationResult.RESULT_INTERRUPTED
|
||||
|
||||
else -> {
|
||||
retValue.resultCode = BLECommOperationResult.RESULT_SUCCESS
|
||||
retValue.value = mCurrentOperation?.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mCurrentOperation = null
|
||||
gattOperationSema.release()
|
||||
|
||||
return retValue
|
||||
}
|
||||
|
||||
private fun getGattStatusMessage(status: Int): String =
|
||||
when (status) {
|
||||
BluetoothGatt.GATT_SUCCESS -> "SUCCESS"
|
||||
BluetoothGatt.GATT_FAILURE -> "FAILED"
|
||||
BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> "NOT PERMITTED"
|
||||
133 -> "Found the strange 133 bug"
|
||||
else -> "UNKNOWN ($status)"
|
||||
}
|
||||
|
||||
init {
|
||||
//orangeLink.rileyLinkBLE = this;
|
||||
bluetoothGattCallback = object : BluetoothGattCallback() {
|
||||
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
|
||||
super.onCharacteristicChanged(gatt, characteristic)
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "${ThreadUtil.sig()}onCharacteristicChanged ${GattAttributes.lookup(characteristic.uuid)} ${ByteUtil.getHex(characteristic.value)}")
|
||||
if (characteristic.uuid == UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT))
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Response Count is " + ByteUtil.shortHexString(characteristic.value))
|
||||
}
|
||||
if (characteristic.uuid == UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT))
|
||||
radioResponseCountNotified?.run()
|
||||
orangeLink.onCharacteristicChanged(characteristic)
|
||||
}
|
||||
|
||||
override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
|
||||
super.onCharacteristicRead(gatt, characteristic, status)
|
||||
val statusMessage = getGattStatusMessage(status)
|
||||
if (gattDebugEnabled)
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "${ThreadUtil.sig()}onCharacteristicRead (${GattAttributes.lookup(characteristic.uuid)}) $statusMessage:${ByteUtil.getHex(characteristic.value)}")
|
||||
mCurrentOperation?.gattOperationCompletionCallback(characteristic.uuid, characteristic.value)
|
||||
}
|
||||
|
||||
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
|
||||
super.onCharacteristicWrite(gatt, characteristic, status)
|
||||
val uuidString = GattAttributes.lookup(characteristic.uuid)
|
||||
if (gattDebugEnabled)
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "${ThreadUtil.sig()}onCharacteristicWrite ${getGattStatusMessage(status)} $uuidString ${ByteUtil.shortHexString(characteristic.value)}")
|
||||
mCurrentOperation?.gattOperationCompletionCallback(characteristic.uuid, characteristic.value)
|
||||
}
|
||||
|
||||
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
||||
super.onConnectionStateChange(gatt, status, newState)
|
||||
|
||||
// https://github.com/NordicSemiconductor/puck-central-android/blob/master/PuckCentral/app/src/main/java/no/nordicsemi/puckcentral/bluetooth/gatt/GattManager.java#L117
|
||||
if (status == 133) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "Got the status 133 bug, closing gatt")
|
||||
disconnect()
|
||||
SystemClock.sleep(500)
|
||||
return
|
||||
}
|
||||
if (gattDebugEnabled) {
|
||||
val stateMessage: String = when (newState) {
|
||||
BluetoothProfile.STATE_CONNECTED -> "CONNECTED"
|
||||
BluetoothProfile.STATE_CONNECTING -> "CONNECTING"
|
||||
BluetoothProfile.STATE_DISCONNECTED -> "DISCONNECTED"
|
||||
BluetoothProfile.STATE_DISCONNECTING -> "DISCONNECTING"
|
||||
else -> "UNKNOWN newState ($newState)"
|
||||
}
|
||||
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onConnectionStateChange " + getGattStatusMessage(status) + " " + stateMessage)
|
||||
}
|
||||
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) rileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.BluetoothConnected, context)
|
||||
else aapsLogger.debug(LTag.PUMPBTCOMM, "BT State connected, GATT status ${status} (${getGattStatusMessage(status)})")
|
||||
} else if (newState == BluetoothProfile.STATE_CONNECTING || newState == BluetoothProfile.STATE_DISCONNECTING) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "We are in ${if (status == BluetoothProfile.STATE_CONNECTING) "Connecting" else "Disconnecting"} state.")
|
||||
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||||
rileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkDisconnected, context)
|
||||
if (manualDisconnect) close()
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "RileyLink Disconnected.")
|
||||
} else {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "Some other state: (status=%d, newState=%d)", status, newState))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
|
||||
super.onDescriptorWrite(gatt, descriptor, status)
|
||||
if (gattDebugEnabled)
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onDescriptorWrite ${GattAttributes.lookup(descriptor.uuid)} ${getGattStatusMessage(status)} written: ${ByteUtil.getHex(descriptor.value)}")
|
||||
mCurrentOperation?.gattOperationCompletionCallback(descriptor.uuid, descriptor.value)
|
||||
}
|
||||
|
||||
override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
|
||||
super.onDescriptorRead(gatt, descriptor, status)
|
||||
mCurrentOperation?.gattOperationCompletionCallback(descriptor.uuid, descriptor.value)
|
||||
if (gattDebugEnabled)
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onDescriptorRead " + getGattStatusMessage(status) + " status " + descriptor)
|
||||
}
|
||||
|
||||
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
|
||||
super.onMtuChanged(gatt, mtu, status)
|
||||
if (gattDebugEnabled)
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onMtuChanged $mtu status $status")
|
||||
}
|
||||
|
||||
override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
|
||||
super.onReadRemoteRssi(gatt, rssi, status)
|
||||
if (gattDebugEnabled)
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onReadRemoteRssi " + getGattStatusMessage(status) + ": " + rssi)
|
||||
}
|
||||
|
||||
override fun onReliableWriteCompleted(gatt: BluetoothGatt, status: Int) {
|
||||
super.onReliableWriteCompleted(gatt, status)
|
||||
if (gattDebugEnabled)
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onReliableWriteCompleted status $status")
|
||||
}
|
||||
|
||||
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
||||
super.onServicesDiscovered(gatt, status)
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
val services = gatt.services
|
||||
var rileyLinkFound = false
|
||||
orangeLink.resetOrangeLinkData()
|
||||
val stringBuilder = StringBuilder("RileyLink Device Debug\n")
|
||||
for (service in services) {
|
||||
val uuidService = service.uuid
|
||||
if (isAnyRileyLinkServiceFound(service)) {
|
||||
rileyLinkFound = true
|
||||
}
|
||||
if (gattDebugEnabled) {
|
||||
debugService(service, 0, stringBuilder)
|
||||
}
|
||||
orangeLink.checkIsOrange(uuidService)
|
||||
}
|
||||
if (gattDebugEnabled) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, stringBuilder.toString())
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "onServicesDiscovered " + getGattStatusMessage(status))
|
||||
}
|
||||
aapsLogger.info(LTag.PUMPBTCOMM, "Gatt device is RileyLink device: $rileyLinkFound")
|
||||
if (rileyLinkFound) {
|
||||
isConnected = true
|
||||
rileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkReady, context)
|
||||
} else {
|
||||
isConnected = false
|
||||
rileyLinkServiceData.setServiceState(
|
||||
RileyLinkServiceState.RileyLinkError,
|
||||
RileyLinkError.DeviceIsNotRileyLink
|
||||
)
|
||||
}
|
||||
} else {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "onServicesDiscovered " + getGattStatusMessage(status))
|
||||
rileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkGattFailed, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ class OrangeLinkImpl @Inject constructor(
|
|||
|
||||
fun enableNotifications(): Boolean {
|
||||
aapsLogger.info(LTag.PUMPBTCOMM, "OrangeLinkImpl::enableNotifications")
|
||||
val result: BLECommOperationResult = rileyLinkBLE.setNotification_blocking(
|
||||
val result: BLECommOperationResult = rileyLinkBLE.setNotificationBlocking(
|
||||
UUID.fromString(GattAttributes.SERVICE_RADIO_ORANGE), //
|
||||
UUID.fromString(GattAttributes.CHARA_NOTIFICATION_ORANGE)
|
||||
)
|
||||
|
@ -156,7 +156,7 @@ class OrangeLinkImpl @Inject constructor(
|
|||
fun stopScan() {
|
||||
handler.removeMessages(TIME_OUT_WHAT)
|
||||
|
||||
val bluetoothAdapter = rileyLinkBLE.bluetoothAdapter ?: return
|
||||
val bluetoothAdapter = rileyLinkBLE.bluetoothAdapter
|
||||
|
||||
try {
|
||||
val bluetoothLeScanner: BluetoothLeScanner = bluetoothAdapter.bluetoothLeScanner
|
||||
|
@ -183,9 +183,7 @@ class OrangeLinkImpl @Inject constructor(
|
|||
|
||||
private fun isBluetoothAvailable(): Boolean {
|
||||
val bluetoothAdapter = rileyLinkBLE.bluetoothAdapter
|
||||
return bluetoothAdapter != null &&
|
||||
bluetoothAdapter.isEnabled &&
|
||||
bluetoothAdapter.state == BluetoothAdapter.STATE_ON
|
||||
return bluetoothAdapter.isEnabled && bluetoothAdapter.state == BluetoothAdapter.STATE_ON
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
Loading…
Reference in a new issue