From c3a0f5ddaded7f9b649228338febe0ef042eaefa Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sat, 23 Sep 2017 15:03:07 +0200 Subject: [PATCH] split BLE to separate class --- .../services/DanaRExecutionService.java | 2 +- .../services/DanaRKoreanExecutionService.java | 2 +- .../plugins/PumpDanaRS/services/BLEComm.java | 699 ++++++++++++++++ .../PumpDanaRS/services/DanaRSService.java | 745 ++---------------- .../services/DanaRv2ExecutionService.java | 2 +- 5 files changed, 758 insertions(+), 692 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java index 93ea79b3a0..c161a3954f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java @@ -109,7 +109,7 @@ public class DanaRExecutionService extends Service { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); String action = intent.getAction(); if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) { - log.debug("Device has disconnected " + device.getName());//Device has disconnected + log.debug("Device was disconnected " + device.getName());//Device was disconnected if (mBTDevice != null && mBTDevice.getName() != null && mBTDevice.getName().equals(device.getName())) { if (mSerialIOThread != null) { mSerialIOThread.disconnect("BT disconnection broadcast"); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java index 927cd11bdf..f77c03e337 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java @@ -105,7 +105,7 @@ public class DanaRKoreanExecutionService extends Service { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); String action = intent.getAction(); if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) { - log.debug("Device has disconnected " + device.getName());//Device has disconnected + log.debug("Device was disconnected " + device.getName());//Device was disconnected if (mBTDevice != null && mBTDevice.getName() != null && mBTDevice.getName().equals(device.getName())) { if (mSerialIOThread != null) { mSerialIOThread.disconnect("BT disconnection broadcast"); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java new file mode 100644 index 0000000000..872ea69fa7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java @@ -0,0 +1,699 @@ +package info.nightscout.androidaps.plugins.PumpDanaRS.services; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.SystemClock; + +import com.cozmo.danar.util.BleCommandUtil; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventPumpStatusChanged; +import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump; +import info.nightscout.androidaps.plugins.PumpDanaRS.activities.PairingHelperActivity; +import info.nightscout.androidaps.plugins.PumpDanaRS.activities.PairingProgressDialog; +import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRSMessageHashTable; +import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet; +import info.nightscout.androidaps.plugins.PumpDanaRS.events.EventDanaRSPacket; +import info.nightscout.androidaps.plugins.PumpDanaRS.events.EventDanaRSPairingSuccess; +import info.nightscout.utils.SP; + +/** + * Created by mike on 23.09.2017. + */ + +public class BLEComm { + private static Logger log = LoggerFactory.getLogger(BLEComm.class); + + private static final long WRITE_DELAY_MILLIS = 50; + + public static String UART_READ_UUID = "0000fff1-0000-1000-8000-00805f9b34fb"; + public static String UART_WRITE_UUID = "0000fff2-0000-1000-8000-00805f9b34fb"; + + private byte PACKET_START_BYTE = (byte) 0xA5; + private byte PACKET_END_BYTE = (byte) 0x5A; + private static BLEComm instance = null; + + public static BLEComm getInstance(DanaRSService service) { + if (instance == null) + instance = new BLEComm(service); + return instance; + } + + private Object mConfirmConnect = null; + + private final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor(); + private ScheduledFuture scheduledDisconnection = null; + + private DanaRS_Packet processsedMessage = null; + private ArrayList mSendQueue = new ArrayList<>(); + + // Variables for connection progress (elapsed time) + private Handler sHandler; + private HandlerThread sHandlerThread; + private long connectionStartTime = 0; + private final Runnable updateProgress = new Runnable() { + @Override + public void run() { + long secondsElapsed = (System.currentTimeMillis() - connectionStartTime) / 1000; + MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING, (int) secondsElapsed)); + sHandler.postDelayed(updateProgress, 1000); + } + }; + + private BluetoothManager mBluetoothManager = null; + private BluetoothAdapter mBluetoothAdapter = null; + private BluetoothDevice mBluetoothDevice = null; + private String mBluetoothDeviceAddress = null; + private String mBluetoothDeviceName = null; + private BluetoothGatt mBluetoothGatt = null; + + protected boolean isConnected = false; + protected boolean isConnecting = false; + + private BluetoothGattCharacteristic UART_Read; + private BluetoothGattCharacteristic UART_Write; + + private DanaRSService service; + + BLEComm(DanaRSService service) { + this.service = service; + initialize(); + + if (sHandlerThread == null) { + sHandlerThread = new HandlerThread(PairingProgressDialog.class.getSimpleName()); + sHandlerThread.start(); + sHandler = new Handler(sHandlerThread.getLooper()); + } + } + + private boolean initialize() { + log.debug("Initializing BLEComm."); + + if (mBluetoothManager == null) { + mBluetoothManager = ((BluetoothManager) MainApp.instance().getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE)); + if (mBluetoothManager == null) { + log.debug("Unable to initialize BluetoothManager."); + return false; + } + } + + mBluetoothAdapter = mBluetoothManager.getAdapter(); + if (mBluetoothAdapter == null) { + log.debug("Unable to obtain a BluetoothAdapter."); + return false; + } + + return true; + } + + public boolean isConnected() { + return isConnected; + } + + public boolean isConnecting() { + return isConnecting; + } + + public boolean connect(String from, String address, Object confirmConnect) { + mConfirmConnect = confirmConnect; + BluetoothManager tBluetoothManager = ((BluetoothManager) MainApp.instance().getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE)); + if (tBluetoothManager == null) { + return false; + } + + BluetoothAdapter tBluetoothAdapter = tBluetoothManager.getAdapter(); + if (tBluetoothAdapter == null) { + return false; + } + + if (mBluetoothAdapter == null) { + if (!initialize()) { + return false; + } + } + + if (address == null) { + log.debug("unspecified address."); + return false; + } + + connectionStartTime = System.currentTimeMillis(); + + MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING)); + isConnecting = true; + + // Following should be removed later because we close Gatt on disconnect and this should never happen + if ((mBluetoothDeviceAddress != null) && (address.equals(mBluetoothDeviceAddress)) && (mBluetoothGatt != null)) { + log.debug("Trying to use an existing mBluetoothGatt for connection."); + sHandler.post(updateProgress); + if (mBluetoothGatt.connect()) { + setCharacteristicNotification(getUARTReadBTGattChar(), true); + return true; + } + sHandler.removeCallbacks(updateProgress); + return false; + } + // end + + BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); + if (device == null) { + log.debug("Device not found. Unable to connect."); + return false; + } + + sHandler.post(updateProgress); + mBluetoothGatt = device.connectGatt(service.getApplicationContext(), false, mGattCallback); + setCharacteristicNotification(getUARTReadBTGattChar(), true); + log.debug("Trying to create a new connection."); + mBluetoothDevice = device; + mBluetoothDeviceAddress = address; + mBluetoothDeviceName = device.getName(); + return true; + } + + public void disconnect(String from) { + log.debug("disconnect from: " + from); + if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { + return; + } + setCharacteristicNotification(getUARTReadBTGattChar(), false); + mBluetoothGatt.disconnect(); + isConnected = false; + } + + public void close() { + log.debug("BluetoothAdapter close"); + if (mBluetoothGatt == null) { + return; + } + mBluetoothGatt.close(); + mBluetoothGatt = null; + } + + public BluetoothDevice getConnectDevice() { + return mBluetoothDevice; + } + + public String getConnectDeviceAddress() { + return mBluetoothDeviceAddress; + } + + public String getConnectDeviceName() { + return mBluetoothDeviceName; + } + + private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + log.debug("onConnectionStateChange"); + + if (newState == BluetoothProfile.STATE_CONNECTED) { + mBluetoothGatt.discoverServices(); + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + close(); + isConnected = false; + sHandler.removeCallbacks(updateProgress); // just to be sure + MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED)); + log.debug("Device was disconnected " + gatt.getDevice().getName());//Device was disconnected + } + } + + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + log.debug("onServicesDiscovered"); + + isConnecting = false; + + if (status == BluetoothGatt.GATT_SUCCESS) { + findCharacteristic(); + } + // stop sending connection progress + sHandler.removeCallbacks(updateProgress); + SendPumpCheck(); + // 1st message sent to pump after connect + } + + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + log.debug("onCharacteristicRead" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : "")); + addToReadBuffer(characteristic.getValue()); + readDataParsing(); + } + + public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + log.debug("onCharacteristicChanged" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : "")); + addToReadBuffer(characteristic.getValue()); + new Thread(new Runnable() { + @Override + public void run() { + readDataParsing(); + } + }).start(); + } + + public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + log.debug("onCharacteristicWrite" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : "")); + new Thread(new Runnable() { + @Override + public void run() { + synchronized (mSendQueue) { + // after message sent, check if there is the rest of the message waiting and send it + if (mSendQueue.size() > 0) { + byte[] bytes = mSendQueue.get(0); + mSendQueue.remove(0); + writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); + } + } + } + }).start(); + } + }; + + public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { + log.debug("setCharacteristicNotification"); + if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { + log.debug("BluetoothAdapter not initialized_ERROR"); + return; + } + mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); + } + + public void readCharacteristic(BluetoothGattCharacteristic characteristic) { + log.debug("readCharacteristic"); + if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { + log.debug("BluetoothAdapter not initialized_ERROR"); + return; + } + mBluetoothGatt.readCharacteristic(characteristic); + } + + public void writeCharacteristic_NO_RESPONSE(final BluetoothGattCharacteristic characteristic, final byte[] data) { + if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { + log.debug("BluetoothAdapter not initialized_ERROR"); + return; + } + + new Thread(new Runnable() { + public void run() { + SystemClock.sleep(WRITE_DELAY_MILLIS); + characteristic.setValue(data); + characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); + log.debug("writeCharacteristic:" + DanaRS_Packet.toHexString(data)); + mBluetoothGatt.writeCharacteristic(characteristic); + } + }).start(); + } + + public BluetoothGattCharacteristic getUARTReadBTGattChar() { + if (UART_Read == null) { + UART_Read = new BluetoothGattCharacteristic(UUID.fromString(UART_READ_UUID), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 0); + } + return UART_Read; + } + + public BluetoothGattCharacteristic getUARTWriteBTGattChar() { + if (UART_Write == null) { + UART_Write = new BluetoothGattCharacteristic(UUID.fromString(UART_WRITE_UUID), BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, 0); + } + return UART_Write; + } + + public List getSupportedGattServices() { + log.debug("getSupportedGattServices"); + if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { + log.debug("BluetoothAdapter not initialized_ERROR"); + return null; + } + + return mBluetoothGatt.getServices(); + } + + private void findCharacteristic() { + List gattServices = getSupportedGattServices(); + + if (gattServices == null) { + return; + } + String uuid = null; + + for (BluetoothGattService gattService : gattServices) { + List gattCharacteristics = gattService.getCharacteristics(); + for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { + uuid = gattCharacteristic.getUuid().toString(); + if (UART_READ_UUID.equals(uuid)) { + UART_Read = gattCharacteristic; + setCharacteristicNotification(UART_Read, true); + } + if (UART_WRITE_UUID.equals(uuid)) { + UART_Write = gattCharacteristic; + } + } + } + } + + private byte[] readBuffer = new byte[1024]; + private int bufferLength = 0; + + private void addToReadBuffer(byte[] buffer) { + //log.debug("addToReadBuffer " + DanaRS_Packet.toHexString(buffer)); + if (buffer == null || buffer.length == 0) { + return; + } + synchronized (readBuffer) { + // Append incomming data to input buffer + System.arraycopy(buffer, 0, readBuffer, bufferLength, buffer.length); + bufferLength += buffer.length; + } + } + + private void readDataParsing() { + boolean startSignatureFound = false, packetIsValid = false; + boolean isProcessing; + + isProcessing = true; + + while (isProcessing) { + int length = 0; + byte[] inputBuffer = null; + synchronized (readBuffer) { + // Find packet start [A5 A5] + if (bufferLength >= 6) { + for (int idxStartByte = 0; idxStartByte < bufferLength - 2; idxStartByte++) { + if ((readBuffer[idxStartByte] == PACKET_START_BYTE) && (readBuffer[idxStartByte + 1] == PACKET_START_BYTE)) { + if (idxStartByte > 0) { + // if buffer doesn't start with signature remove the leading trash + log.debug("Shifting the input buffer by " + idxStartByte + " bytes"); + System.arraycopy(readBuffer, idxStartByte, readBuffer, 0, bufferLength - idxStartByte); + bufferLength -= idxStartByte; + } + startSignatureFound = true; + break; + } + } + } + // A5 A5 LEN TYPE CODE PARAMS CHECKSUM1 CHECKSUM2 5A 5A + // ^---- LEN -----^ + // total packet length 2 + 1 + readBuffer[2] + 2 + 2 + if (startSignatureFound) { + length = readBuffer[2]; + // test if there is enough data loaded + if (length + 7 > bufferLength) + return; + // Verify packed end [5A 5A] + if ((readBuffer[length + 5] == PACKET_END_BYTE) && (readBuffer[length + 6] == PACKET_END_BYTE)) { + packetIsValid = true; + } + } + if (packetIsValid) { + inputBuffer = new byte[length + 7]; + // copy packet to input buffer + System.arraycopy(readBuffer, 0, inputBuffer, 0, length + 7); + // Cut off the message from readBuffer + try { + System.arraycopy(readBuffer, length + 7, readBuffer, 0, bufferLength - (length + 7)); + } catch (Exception e) { + log.debug("length: " + length + "bufferLength: " + bufferLength); + throw e; + } + bufferLength -= (length + 7); + // now we have encrypted packet in inputBuffer + } + } + if (packetIsValid) { + try { + // decrypt the packet + inputBuffer = BleCommandUtil.getInstance().getDecryptedPacket(inputBuffer); + + if (inputBuffer == null) { + log.debug("Null decryptedInputBuffer"); + return; + } + + switch (inputBuffer[0]) { + // initial handshake packet + case (byte) BleCommandUtil.DANAR_PACKET__TYPE_ENCRYPTION_RESPONSE: + switch (inputBuffer[1]) { + // 1st packet + case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK: + if (inputBuffer.length == 4 && inputBuffer[2] == 'O' && inputBuffer[3] == 'K') { + log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (OK)" + " " + DanaRS_Packet.toHexString(inputBuffer)); + // Grab pairing key from preferences if exists + String pairingKey = SP.getString(R.string.key_danars_pairingkey, null); + log.debug("Using stored pairing key: " + pairingKey); + if (pairingKey != null) { + byte[] encodedPairingKey = DanaRS_Packet.hexToBytes(pairingKey); + byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY, encodedPairingKey, null); + log.debug(">>>>> " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(bytes)); + writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); + } else { + // Stored pairing key does not exists, request pairing + SendPairingRequest(); + } + + } else if (inputBuffer.length == 6 && inputBuffer[2] == 'B' && inputBuffer[3] == 'U' && inputBuffer[4] == 'S' && inputBuffer[5] == 'Y') { + log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (BUSY)" + " " + DanaRS_Packet.toHexString(inputBuffer)); + mSendQueue.clear(); + MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.sResources.getString(R.string.pumpbusy))); + } else { + log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (ERROR)" + " " + DanaRS_Packet.toHexString(inputBuffer)); + mSendQueue.clear(); + MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.sResources.getString(R.string.connectionerror))); + } + break; + // 2nd packet, pairing key + case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY: + log.debug("<<<<< " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(inputBuffer)); + if (inputBuffer[2] == (byte) 0x00) { + // Paring is not requested, sending time info + SendTimeInfo(); + } else { + // Pairing on pump is requested + SendPairingRequest(); + } + break; + case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST: + log.debug("<<<<< " + "ENCRYPTION__PASSKEY_REQUEST " + DanaRS_Packet.toHexString(inputBuffer)); + if (inputBuffer[2] != (byte) 0x00) { + disconnect("passkey request failed"); + } + break; + // Paring response, OK button on pump pressed + case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_RETURN: + log.debug("<<<<< " + "ENCRYPTION__PASSKEY_RETURN " + DanaRS_Packet.toHexString(inputBuffer)); + // Paring is successfull, sending time info + MainApp.bus().post(new EventDanaRSPairingSuccess()); + SendTimeInfo(); + byte[] pairingKey = {inputBuffer[2], inputBuffer[3]}; + // store pairing key to preferences + SP.putString(R.string.key_danars_pairingkey, DanaRS_Packet.toHexString(pairingKey)); + log.debug("Got pairing key: " + DanaRS_Packet.toHexString(pairingKey)); + break; + // time and user password information. last packet in handshake + case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION: + log.debug("<<<<< " + "ENCRYPTION__TIME_INFORMATION " + /*message.getMessageName() + " " + */ DanaRS_Packet.toHexString(inputBuffer)); + int size = inputBuffer.length; + int pass = ((inputBuffer[size - 1] & 0x000000FF) << 8) + ((inputBuffer[size - 2] & 0x000000FF)); + pass = pass ^ 3463; + DanaRPump.getInstance().rs_password = Integer.toHexString(pass); + log.debug("Pump user password: " + Integer.toHexString(pass)); + MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTED)); + + isConnected = true; + isConnecting = false; + service.getPumpStatus(); + scheduleDisconnection(); + if (mConfirmConnect != null) { + synchronized (mConfirmConnect) { + mConfirmConnect.notify(); + mConfirmConnect = null; + } + } + break; + } + break; + // common data packet + default: + DanaRS_Packet message; + // Retrieve message code from received buffer and last message sent + int originalCommand = processsedMessage != null ? processsedMessage.getCommand() : 0xFFFF; + int receivedCommand = DanaRS_Packet.getCommand(inputBuffer); + if (originalCommand == receivedCommand) { + // it's response to last message + message = processsedMessage; + } else { + // it's not response to last message, create new instance + message = DanaRSMessageHashTable.findMessage(receivedCommand); + } + if (message != null) { + log.debug("<<<<< " + message.getFriendlyName() + " " + DanaRS_Packet.toHexString(inputBuffer)); + // process received data + message.handleMessage(inputBuffer); + message.setReceived(); + synchronized (message) { + // notify to sendMessage + message.notify(); + } + MainApp.bus().post(new EventDanaRSPacket(message)); + } else { + log.error("Unknown message received " + DanaRS_Packet.toHexString(inputBuffer)); + } + scheduleDisconnection(); + break; + } + } catch (Exception e) { + e.printStackTrace(); + } + startSignatureFound = false; + packetIsValid = false; + if (bufferLength < 6) { + // stop the loop + isProcessing = false; + } + } else { + // stop the loop + isProcessing = false; + } + } + } + + public void sendMessage(DanaRS_Packet message) { + processsedMessage = message; + if (message == null) + return; + + byte[] command = {(byte) message.getType(), (byte) message.getOpCode()}; + byte[] params = message.getRequestParams(); + log.debug(">>>>> " + message.getFriendlyName() + " " + DanaRS_Packet.toHexString(command) + " " + DanaRS_Packet.toHexString(params)); + byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(message.getOpCode(), params, null); + // If there is another message not completely sent, add to queue only + if (mSendQueue.size() > 0) { + // Split to parts per 20 bytes max + for (; ; ) { + if (bytes.length > 20) { + byte[] addBytes = new byte[20]; + System.arraycopy(bytes, 0, addBytes, 0, addBytes.length); + byte[] reBytes = new byte[bytes.length - addBytes.length]; + System.arraycopy(bytes, addBytes.length, reBytes, 0, reBytes.length); + bytes = reBytes; + synchronized (mSendQueue) { + mSendQueue.add(addBytes); + } + } else { + synchronized (mSendQueue) { + mSendQueue.add(bytes); + } + break; + } + } + + } else { + if (bytes.length > 20) { + // Cut first 20 bytes + byte[] sendBytes = new byte[20]; + System.arraycopy(bytes, 0, sendBytes, 0, sendBytes.length); + byte[] reBytes = new byte[bytes.length - sendBytes.length]; + System.arraycopy(bytes, sendBytes.length, reBytes, 0, reBytes.length); + bytes = reBytes; + // and send + writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), sendBytes); + // The rest split to parts per 20 bytes max + for (; ; ) { + if (bytes.length > 20) { + byte[] addBytes = new byte[20]; + System.arraycopy(bytes, 0, addBytes, 0, addBytes.length); + reBytes = new byte[bytes.length - addBytes.length]; + System.arraycopy(bytes, addBytes.length, reBytes, 0, reBytes.length); + bytes = reBytes; + synchronized (mSendQueue) { + mSendQueue.add(addBytes); + } + } else { + synchronized (mSendQueue) { + mSendQueue.add(bytes); + } + break; + } + } + } else { + writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); + } + } + // The rest from queue is send from onCharasteristicWrite (after sending 1st part) + synchronized (message) { + try { + message.wait(5000); + } catch (InterruptedException e) { + log.error("sendMessage InterruptedException", e); + e.printStackTrace(); + } + } + + //SystemClock.sleep(200); + if (!message.isReceived()) { + log.warn("Reply not received " + message.getFriendlyName()); + } + scheduleDisconnection(); + } + + private void SendPairingRequest() { + // Start activity which is waiting 20sec + // On pump pairing request is displayed and is waiting for conformation + Intent i = new Intent(); + i.setClass(MainApp.instance(), PairingHelperActivity.class); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + + byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST, null, null); + log.debug(">>>>> " + "ENCRYPTION__PASSKEY_REQUEST" + " " + DanaRS_Packet.toHexString(bytes)); + writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); + } + + protected void SendPumpCheck() { + // 1st message sent to pump after connect + byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK, null, getConnectDeviceName()); + log.debug(">>>>> " + "ENCRYPTION__PUMP_CHECK (0x00)" + " " + DanaRS_Packet.toHexString(bytes)); + writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); + } + + private void SendTimeInfo() { + byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION, null, null); + log.debug(">>>>> " + "ENCRYPTION__TIME_INFORMATION" + " " + DanaRS_Packet.toHexString(bytes)); + writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); + } + + public void scheduleDisconnection() { + class DisconnectRunnable implements Runnable { + public void run() { + disconnect("scheduleDisconnection"); + scheduledDisconnection = null; + } + } + // prepare task for execution in 5 sec + // cancel waiting task to prevent sending multiple disconnections + if (scheduledDisconnection != null) + scheduledDisconnection.cancel(false); + Runnable task = new DisconnectRunnable(); + final int sec = 5; + scheduledDisconnection = worker.schedule(task, sec, TimeUnit.SECONDS); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java index dba9704e32..543b41d8f5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java @@ -1,37 +1,19 @@ package info.nightscout.androidaps.plugins.PumpDanaRS.services; import android.app.Service; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCallback; -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattService; -import android.bluetooth.BluetoothManager; -import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.os.Binder; -import android.os.Handler; -import android.os.HandlerThread; import android.os.IBinder; import android.os.PowerManager; import android.os.SystemClock; -import com.cozmo.danar.util.BleCommandUtil; import com.squareup.otto.Subscribe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; import java.util.Date; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; @@ -48,9 +30,6 @@ import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump; import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes; import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRNewStatus; import info.nightscout.androidaps.plugins.PumpDanaRS.DanaRSPlugin; -import info.nightscout.androidaps.plugins.PumpDanaRS.activities.PairingHelperActivity; -import info.nightscout.androidaps.plugins.PumpDanaRS.activities.PairingProgressDialog; -import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRSMessageHashTable; import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet; import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Basal_Get_Basal_Rate; import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Basal_Get_Profile_Number; @@ -86,34 +65,13 @@ import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Notify_D import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Notify_Delivery_Rate_Display; import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Option_Get_Pump_Time; import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Option_Set_Pump_Time; -import info.nightscout.androidaps.plugins.PumpDanaRS.events.EventDanaRSPacket; -import info.nightscout.androidaps.plugins.PumpDanaRS.events.EventDanaRSPairingSuccess; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; public class DanaRSService extends Service { private static Logger log = LoggerFactory.getLogger(DanaRSService.class); - private static final long WRITE_DELAY_MILLIS = 50; - - public static String UART_READ_UUID = "0000fff1-0000-1000-8000-00805f9b34fb"; - public static String UART_WRITE_UUID = "0000fff2-0000-1000-8000-00805f9b34fb"; - - private byte PACKET_START_BYTE = (byte) 0xA5; - private byte PACKET_END_BYTE = (byte) 0x5A; - - private BluetoothManager mBluetoothManager = null; - private BluetoothAdapter mBluetoothAdapter = null; - private BluetoothDevice mBluetoothDevice = null; - private String mBluetoothDeviceAddress = null; - private String mBluetoothDeviceName = null; - private BluetoothGatt mBluetoothGatt = null; - - private boolean isConnected = false; - private boolean isConnecting = false; - - private BluetoothGattCharacteristic UART_Read; - private BluetoothGattCharacteristic UART_Write; + private BLEComm bleComm = BLEComm.getInstance(this); private PowerManager.WakeLock mWakeLock; private IBinder mBinder = new LocalBinder(); @@ -121,27 +79,6 @@ public class DanaRSService extends Service { private DanaRPump danaRPump = DanaRPump.getInstance(); private Treatment bolusingTreatment = null; - private Object mConfirmConnect = null; - - private final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor(); - private ScheduledFuture scheduledDisconnection = null; - - private DanaRS_Packet processsedMessage = null; - private ArrayList mSendQueue = new ArrayList<>(); - - // Variables pro connection progress (elapsed time) - private Handler sHandler; - private HandlerThread sHandlerThread; - private long connectionStartTime = 0; - private final Runnable updateProgress = new Runnable() { - @Override - public void run() { - long secondsElapsed = (System.currentTimeMillis() - connectionStartTime) / 1000; - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING, (int) secondsElapsed)); - sHandler.postDelayed(updateProgress, 1000); - } - }; - private long lastHistoryFetched = 0; public DanaRSService() { @@ -150,49 +87,63 @@ public class DanaRSService extends Service { } catch (RuntimeException x) { // Ignore } - initialize(); MainApp.bus().register(this); PowerManager powerManager = (PowerManager) MainApp.instance().getApplicationContext().getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, DanaRSService.class.getSimpleName()); - if (sHandlerThread == null) { - sHandlerThread = new HandlerThread(PairingProgressDialog.class.getSimpleName()); - sHandlerThread.start(); - sHandler = new Handler(sHandlerThread.getLooper()); - } } - private boolean getPumpStatus() { + public boolean isConnected() { + return bleComm.isConnected; + } + + public boolean isConnecting() { + return bleComm.isConnecting; + } + + public boolean connect(String from, String address, Object confirmConnect) { + return bleComm.connect(from, address, confirmConnect); + } + + public void disconnect(String from) { + bleComm.disconnect(from); + } + + public void sendMessage(DanaRS_Packet message) { + bleComm.sendMessage(message); + } + + protected boolean getPumpStatus() { try { MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingpumpstatus))); - sendMessage(new DanaRS_Packet_General_Initial_Screen_Information()); + bleComm.sendMessage(new DanaRS_Packet_General_Initial_Screen_Information()); MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingextendedbolusstatus))); - sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State()); + bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State()); MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingbolusstatus))); - sendMessage(new DanaRS_Packet_Bolus_Get_Step_Bolus_Information()); // last bolus + bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Step_Bolus_Information()); // last bolus MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingtempbasalstatus))); - sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); + bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); Date now = new Date(); if (danaRPump.lastSettingsRead.getTime() + 60 * 60 * 1000L < now.getTime() || !MainApp.getSpecificPlugin(DanaRSPlugin.class).isInitialized()) { MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingpumpsettings))); - sendMessage(new DanaRS_Packet_General_Get_Shipping_Information()); // serial no - sendMessage(new DanaRS_Packet_General_Get_Pump_Check()); // firmware - sendMessage(new DanaRS_Packet_Basal_Get_Profile_Number()); - sendMessage(new DanaRS_Packet_Bolus_Get_Bolus_Option()); // isExtendedEnabled - sendMessage(new DanaRS_Packet_Bolus_Get_Step_Bolus_Information()); // bolusStep, maxBolus - sendMessage(new DanaRS_Packet_Basal_Get_Basal_Rate()); // basal profile, basalStep, maxBasal - sendMessage(new DanaRS_Packet_Bolus_Get_Calculation_Information()); // target - sendMessage(new DanaRS_Packet_Bolus_Get_CIR_CF_Array()); + bleComm.sendMessage(new DanaRS_Packet_General_Get_Shipping_Information()); // serial no + bleComm.sendMessage(new DanaRS_Packet_General_Get_Pump_Check()); // firmware + bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Profile_Number()); + bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Bolus_Option()); // isExtendedEnabled + bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Step_Bolus_Information()); // bolusStep, maxBolus + bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Basal_Rate()); // basal profile, basalStep, maxBasal + bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Calculation_Information()); // target + bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_CIR_CF_Array()); MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.gettingpumptime))); - sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); + bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); long timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; log.debug("Pump time difference: " + timeDiff + " seconds"); if (Math.abs(timeDiff) > 10) { - sendMessage(new DanaRS_Packet_Option_Set_Pump_Time(new Date())); - sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); + bleComm.sendMessage(new DanaRS_Packet_Option_Set_Pump_Time(new Date())); + bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; log.debug("Pump time difference: " + timeDiff + " seconds"); } @@ -233,16 +184,16 @@ public class DanaRSService extends Service { if (carbs > 0) { // MsgSetCarbsEntry msg = new MsgSetCarbsEntry(carbtime, carbs); #### -// sendMessage(msg); +// bleComm.sendMessage(msg); // MsgSetHistoryEntry_v2 msgSetHistoryEntry_v2 = new MsgSetHistoryEntry_v2(DanaRPump.CARBS, carbtime, carbs, 0); -// sendMessage(msgSetHistoryEntry_v2); +// bleComm.sendMessage(msgSetHistoryEntry_v2); // lastHistoryFetched = carbtime - 60000; } if (insulin > 0) { DanaRS_Packet_Notify_Delivery_Rate_Display progress = new DanaRS_Packet_Notify_Delivery_Rate_Display(insulin, t); // initialize static variables if (!stop.stopped) { - sendMessage(start); + bleComm.sendMessage(start); } else { t.insulin = 0d; return false; @@ -268,9 +219,9 @@ public class DanaRSService extends Service { DanaRS_Packet_Bolus_Set_Step_Bolus_Stop stop = new DanaRS_Packet_Bolus_Set_Step_Bolus_Stop(); stop.forced = true; if (isConnected()) { - sendMessage(stop); + bleComm.sendMessage(stop); while (!stop.stopped) { - sendMessage(stop); + bleComm.sendMessage(stop); SystemClock.sleep(200); } } else { @@ -282,13 +233,13 @@ public class DanaRSService extends Service { if (!isConnected()) return false; if (danaRPump.isTempBasalInProgress) { MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.stoppingtempbasal))); - sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal()); + bleComm.sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal()); SystemClock.sleep(500); } MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.settingtempbasal))); - sendMessage(new DanaRS_Packet_Basal_Set_Temporary_Basal(percent, durationInHours)); + bleComm.sendMessage(new DanaRS_Packet_Basal_Set_Temporary_Basal(percent, durationInHours)); SystemClock.sleep(200); - sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); + bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); loadEvents(); MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); return true; @@ -301,8 +252,8 @@ public class DanaRSService extends Service { public boolean tempBasalStop() { if (!isConnected()) return false; MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.stoppingtempbasal))); - sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal()); - sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); + bleComm.sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal()); + bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); loadEvents(); MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); return true; @@ -311,9 +262,9 @@ public class DanaRSService extends Service { public boolean extendedBolus(Double insulin, int durationInHalfHours) { if (!isConnected()) return false; MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.settingextendedbolus))); - sendMessage(new DanaRS_Packet_Bolus_Set_Extended_Bolus(insulin, durationInHalfHours)); + bleComm.sendMessage(new DanaRS_Packet_Bolus_Set_Extended_Bolus(insulin, durationInHalfHours)); SystemClock.sleep(200); - sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State()); + bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State()); loadEvents(); MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); return true; @@ -322,8 +273,8 @@ public class DanaRSService extends Service { public boolean extendedBolusStop() { if (!isConnected()) return false; MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.stoppingextendedbolus))); - sendMessage(new DanaRS_Packet_Bolus_Set_Extended_Bolus_Cancel()); - sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State()); + bleComm.sendMessage(new DanaRS_Packet_Bolus_Set_Extended_Bolus_Cancel()); + bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State()); loadEvents(); MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); return true; @@ -334,9 +285,9 @@ public class DanaRSService extends Service { MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.updatingbasalrates))); double[] basal = DanaRPump.buildDanaRProfileRecord(profile); DanaRS_Packet_Basal_Set_Profile_Basal_Rate msgSet = new DanaRS_Packet_Basal_Set_Profile_Basal_Rate(0, basal); - sendMessage(msgSet); + bleComm.sendMessage(msgSet); DanaRS_Packet_Basal_Set_Profile_Number msgActivate = new DanaRS_Packet_Basal_Set_Profile_Number(0); - sendMessage(msgActivate); + bleComm.sendMessage(msgActivate); danaRPump.lastSettingsRead = new Date(0); // force read full settings getPumpStatus(); MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); @@ -376,14 +327,14 @@ public class DanaRSService extends Service { break; } if (msg != null) { - sendMessage(new DanaRS_Packet_General_Set_History_Upload_Mode(1)); + bleComm.sendMessage(new DanaRS_Packet_General_Set_History_Upload_Mode(1)); SystemClock.sleep(200); - sendMessage(msg); + bleComm.sendMessage(msg); while (!msg.done && isConnected()) { SystemClock.sleep(100); } SystemClock.sleep(200); - sendMessage(new DanaRS_Packet_General_Set_History_Upload_Mode(0)); + bleComm.sendMessage(new DanaRS_Packet_General_Set_History_Upload_Mode(0)); } return true; } @@ -416,588 +367,4 @@ public class DanaRSService extends Service { log.debug("EventAppExit finished"); } - private boolean initialize() { - log.debug("Initializing service."); - - if (mBluetoothManager == null) { - mBluetoothManager = ((BluetoothManager) MainApp.instance().getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE)); - if (mBluetoothManager == null) { - log.debug("Unable to initialize BluetoothManager."); - return false; - } - } - - mBluetoothAdapter = mBluetoothManager.getAdapter(); - if (mBluetoothAdapter == null) { - log.debug("Unable to obtain a BluetoothAdapter."); - return false; - } - - return true; - } - - public boolean isConnected() { - return isConnected; - } - - public boolean isConnecting() { - return isConnecting; - } - - public boolean connect(String from, String address, Object confirmConnect) { - mConfirmConnect = confirmConnect; - BluetoothManager tBluetoothManager = ((BluetoothManager) MainApp.instance().getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE)); - if (tBluetoothManager == null) { - return false; - } - - BluetoothAdapter tBluetoothAdapter = tBluetoothManager.getAdapter(); - if (tBluetoothAdapter == null) { - return false; - } - - if (mBluetoothAdapter == null) { - if (!initialize()) { - return false; - } - } - - if (address == null) { - log.debug("unspecified address."); - return false; - } - - connectionStartTime = System.currentTimeMillis(); - - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING)); - isConnecting = true; - - // Following should be removed later because we close Gatt on disconnect and this should never happen - if ((mBluetoothDeviceAddress != null) && (address.equals(mBluetoothDeviceAddress)) && (mBluetoothGatt != null)) { - log.debug("Trying to use an existing mBluetoothGatt for connection."); - sHandler.post(updateProgress); - if (mBluetoothGatt.connect()) { - setCharacteristicNotification(getUARTReadBTGattChar(), true); - return true; - } - sHandler.removeCallbacks(updateProgress); - return false; - } - // end - - BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); - if (device == null) { - log.debug("Device not found. Unable to connect."); - return false; - } - - sHandler.post(updateProgress); - mBluetoothGatt = device.connectGatt(getApplicationContext(), false, mGattCallback); - setCharacteristicNotification(getUARTReadBTGattChar(), true); - log.debug("Trying to create a new connection."); - mBluetoothDevice = device; - mBluetoothDeviceAddress = address; - mBluetoothDeviceName = device.getName(); - return true; - } - - public void disconnect(String from) { - log.debug("disconnect from: " + from); - if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { - return; - } - setCharacteristicNotification(getUARTReadBTGattChar(), false); - mBluetoothGatt.disconnect(); - isConnected = false; - } - - public void close() { - log.debug("BluetoothAdapter close"); - if (mBluetoothGatt == null) { - return; - } - mBluetoothGatt.close(); - mBluetoothGatt = null; - } - - public BluetoothDevice getConnectDevice() { - return mBluetoothDevice; - } - - public String getConnectDeviceAddress() { - return mBluetoothDeviceAddress; - } - - public String getConnectDeviceName() { - return mBluetoothDeviceName; - } - - private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { - public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - log.debug("onConnectionStateChange"); - - if (newState == BluetoothProfile.STATE_CONNECTED) { - mBluetoothGatt.discoverServices(); - } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - close(); - isConnected = false; - sHandler.removeCallbacks(updateProgress); // just to be sure - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED)); - log.debug("Device was disconnected " + gatt.getDevice().getName());//Device was disconnected - } - } - - public void onServicesDiscovered(BluetoothGatt gatt, int status) { - log.debug("onServicesDiscovered"); - - isConnecting = false; - - if (status == BluetoothGatt.GATT_SUCCESS) { - findCharacteristic(); - } - // stop sending connection progress - sHandler.removeCallbacks(updateProgress); - - // 1st message sent to pump after connect - byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK, null, getConnectDeviceName()); - log.debug(">>>>> " + "ENCRYPTION__PUMP_CHECK (0x00)" + " " + DanaRS_Packet.toHexString(bytes)); - writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); - } - - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - log.debug("onCharacteristicRead" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : "")); - addToReadBuffer(characteristic.getValue()); - readDataParsing(); - } - - public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { - log.debug("onCharacteristicChanged" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : "")); - addToReadBuffer(characteristic.getValue()); - new Thread(new Runnable() { - @Override - public void run() { - readDataParsing(); - } - }).start(); - } - - public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - log.debug("onCharacteristicWrite" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : "")); - new Thread(new Runnable() { - @Override - public void run() { - synchronized (mSendQueue) { - // after message sent, check if there is the rest of the message waiting and send it - if (mSendQueue.size() > 0) { - byte[] bytes = mSendQueue.get(0); - mSendQueue.remove(0); - writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); - } - } - } - }).start(); - } - }; - - public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { - log.debug("setCharacteristicNotification"); - if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { - log.debug("BluetoothAdapter not initialized_ERROR"); - return; - } - mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); - } - - public void readCharacteristic(BluetoothGattCharacteristic characteristic) { - log.debug("readCharacteristic"); - if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { - log.debug("BluetoothAdapter not initialized_ERROR"); - return; - } - mBluetoothGatt.readCharacteristic(characteristic); - } - - public void writeCharacteristic_NO_RESPONSE(final BluetoothGattCharacteristic characteristic, final byte[] data) { - if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { - log.debug("BluetoothAdapter not initialized_ERROR"); - return; - } - - new Thread(new Runnable() { - public void run() { - SystemClock.sleep(WRITE_DELAY_MILLIS); - characteristic.setValue(data); - characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); - log.debug("writeCharacteristic:" + DanaRS_Packet.toHexString(data)); - mBluetoothGatt.writeCharacteristic(characteristic); - } - }).start(); - } - - public BluetoothGattCharacteristic getUARTReadBTGattChar() { - if (UART_Read == null) { - UART_Read = new BluetoothGattCharacteristic(UUID.fromString(UART_READ_UUID), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 0); - } - return UART_Read; - } - - public BluetoothGattCharacteristic getUARTWriteBTGattChar() { - if (UART_Write == null) { - UART_Write = new BluetoothGattCharacteristic(UUID.fromString(UART_WRITE_UUID), BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, 0); - } - return UART_Write; - } - - public List getSupportedGattServices() { - log.debug("getSupportedGattServices"); - if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { - log.debug("BluetoothAdapter not initialized_ERROR"); - return null; - } - - return mBluetoothGatt.getServices(); - } - - private void findCharacteristic() { - List gattServices = getSupportedGattServices(); - - if (gattServices == null) { - return; - } - String uuid = null; - - for (BluetoothGattService gattService : gattServices) { - List gattCharacteristics = gattService.getCharacteristics(); - for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { - uuid = gattCharacteristic.getUuid().toString(); - if (UART_READ_UUID.equals(uuid)) { - UART_Read = gattCharacteristic; - setCharacteristicNotification(UART_Read, true); - } - if (UART_WRITE_UUID.equals(uuid)) { - UART_Write = gattCharacteristic; - } - } - } - } - - private byte[] readBuffer = new byte[1024]; - private int bufferLength = 0; - - private void addToReadBuffer(byte[] buffer) { - //log.debug("addToReadBuffer " + DanaRS_Packet.toHexString(buffer)); - if (buffer == null || buffer.length == 0) { - return; - } - synchronized (readBuffer) { - // Append incomming data to input buffer - System.arraycopy(buffer, 0, readBuffer, bufferLength, buffer.length); - bufferLength += buffer.length; - } - } - - private void readDataParsing() { - boolean startSignatureFound = false, packetIsValid = false; - boolean isProcessing; - - isProcessing = true; - - while (isProcessing) { - int length = 0; - byte[] inputBuffer = null; - synchronized (readBuffer) { - // Find packet start [A5 A5] - if (bufferLength >= 6) { - for (int idxStartByte = 0; idxStartByte < bufferLength - 2; idxStartByte++) { - if ((readBuffer[idxStartByte] == PACKET_START_BYTE) && (readBuffer[idxStartByte + 1] == PACKET_START_BYTE)) { - if (idxStartByte > 0) { - // if buffer doesn't start with signature remove the leading trash - log.debug("Shifting the input buffer by " + idxStartByte + " bytes"); - System.arraycopy(readBuffer, idxStartByte, readBuffer, 0, bufferLength - idxStartByte); - bufferLength -= idxStartByte; - } - startSignatureFound = true; - break; - } - } - } - // A5 A5 LEN TYPE CODE PARAMS CHECKSUM1 CHECKSUM2 5A 5A - // ^---- LEN -----^ - // total packet length 2 + 1 + readBuffer[2] + 2 + 2 - if (startSignatureFound) { - length = readBuffer[2]; - // test if there is enough data loaded - if (length + 7 > bufferLength) - return; - // Verify packed end [5A 5A] - if ((readBuffer[length + 5] == PACKET_END_BYTE) && (readBuffer[length + 6] == PACKET_END_BYTE)) { - packetIsValid = true; - } - } - if (packetIsValid) { - inputBuffer = new byte[length + 7]; - // copy packet to input buffer - System.arraycopy(readBuffer, 0, inputBuffer, 0, length + 7); - // Cut off the message from readBuffer - try { - System.arraycopy(readBuffer, length + 7, readBuffer, 0, bufferLength - (length + 7)); - } catch (Exception e) { - log.debug("length: " + length + "bufferLength: " + bufferLength); - throw e; - } - bufferLength -= (length + 7); - // now we have encrypted packet in inputBuffer - } - } - if (packetIsValid) { - try { - // decrypt the packet - inputBuffer = BleCommandUtil.getInstance().getDecryptedPacket(inputBuffer); - - if (inputBuffer == null) { - log.debug("Null decryptedInputBuffer"); - return; - } - - switch (inputBuffer[0]) { - // initial handshake packet - case (byte) BleCommandUtil.DANAR_PACKET__TYPE_ENCRYPTION_RESPONSE: - switch (inputBuffer[1]) { - // 1st packet - case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK: - if (inputBuffer.length == 4 && inputBuffer[2] == 'O' && inputBuffer[3] == 'K') { - log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (OK)" + " " + DanaRS_Packet.toHexString(inputBuffer)); - // Grab pairing key from preferences if exists - String pairingKey = SP.getString(R.string.key_danars_pairingkey, null); - log.debug("Using stored pairing key: " + pairingKey); - if (pairingKey != null) { - byte[] encodedPairingKey = DanaRS_Packet.hexToBytes(pairingKey); - byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY, encodedPairingKey, null); - log.debug(">>>>> " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(bytes)); - writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); - } else { - // Stored pairing key does not exists, request pairing - SendPairingRequest(); - } - - } else if (inputBuffer.length == 6 && inputBuffer[2] == 'B' && inputBuffer[3] == 'U' && inputBuffer[4] == 'S' && inputBuffer[5] == 'Y') { - log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (BUSY)" + " " + DanaRS_Packet.toHexString(inputBuffer)); - mSendQueue.clear(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.sResources.getString(R.string.pumpbusy))); - } else { - log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (ERROR)" + " " + DanaRS_Packet.toHexString(inputBuffer)); - mSendQueue.clear(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.sResources.getString(R.string.connectionerror))); - } - break; - // 2nd packet, pairing key - case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY: - log.debug("<<<<< " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(inputBuffer)); - if (inputBuffer[2] == (byte) 0x00) { - // Paring is not requested, sending time info - SendTimeInfo(); - } else { - // Pairing on pump is requested - SendPairingRequest(); - } - break; - case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST: - log.debug("<<<<< " + "ENCRYPTION__PASSKEY_REQUEST " + DanaRS_Packet.toHexString(inputBuffer)); - if (inputBuffer[2] != (byte) 0x00) { - disconnect("passkey request failed"); - } - break; - // Paring response, OK button on pump pressed - case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_RETURN: - log.debug("<<<<< " + "ENCRYPTION__PASSKEY_RETURN " + DanaRS_Packet.toHexString(inputBuffer)); - // Paring is successfull, sending time info - MainApp.bus().post(new EventDanaRSPairingSuccess()); - SendTimeInfo(); - byte[] pairingKey = {inputBuffer[2], inputBuffer[3]}; - // store pairing key to preferences - SP.putString(R.string.key_danars_pairingkey, DanaRS_Packet.toHexString(pairingKey)); - log.debug("Got pairing key: " + DanaRS_Packet.toHexString(pairingKey)); - break; - // time and user password information. last packet in handshake - case (byte) BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION: - log.debug("<<<<< " + "ENCRYPTION__TIME_INFORMATION " + /*message.getMessageName() + " " + */ DanaRS_Packet.toHexString(inputBuffer)); - int size = inputBuffer.length; - int pass = ((inputBuffer[size - 1] & 0x000000FF) << 8) + ((inputBuffer[size - 2] & 0x000000FF)); - pass = pass ^ 3463; - DanaRPump.getInstance().rs_password = Integer.toHexString(pass); - log.debug("Pump user password: " + Integer.toHexString(pass)); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTED)); - - isConnected = true; - isConnecting = false; - getPumpStatus(); - scheduleDisconnection(); - if (mConfirmConnect != null) { - synchronized (mConfirmConnect) { - mConfirmConnect.notify(); - mConfirmConnect = null; - } - } - break; - } - break; - // common data packet - default: - DanaRS_Packet message; - // Retrieve message code from received buffer and last message sent - int originalCommand = processsedMessage != null ? processsedMessage.getCommand() : 0xFFFF; - int receivedCommand = DanaRS_Packet.getCommand(inputBuffer); - if (originalCommand == receivedCommand) { - // it's response to last message - message = processsedMessage; - } else { - // it's not response to last message, create new instance - message = DanaRSMessageHashTable.findMessage(receivedCommand); - } - if (message != null) { - log.debug("<<<<< " + message.getFriendlyName() + " " + DanaRS_Packet.toHexString(inputBuffer)); - // process received data - message.handleMessage(inputBuffer); - message.setReceived(); - synchronized (message) { - // notify to sendMessage - message.notify(); - } - MainApp.bus().post(new EventDanaRSPacket(message)); - } else { - log.error("Unknown message received " + DanaRS_Packet.toHexString(inputBuffer)); - } - scheduleDisconnection(); - break; - } - } catch (Exception e) { - e.printStackTrace(); - } - startSignatureFound = false; - packetIsValid = false; - if (bufferLength < 6) { - // stop the loop - isProcessing = false; - } - } else { - // stop the loop - isProcessing = false; - } - } - } - - public void sendMessage(DanaRS_Packet message) { - processsedMessage = message; - if (message == null) - return; - - byte[] command = {(byte) message.getType(), (byte) message.getOpCode()}; - byte[] params = message.getRequestParams(); - log.debug(">>>>> " + message.getFriendlyName() + " " + DanaRS_Packet.toHexString(command) + " " + DanaRS_Packet.toHexString(params)); - byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(message.getOpCode(), params, null); - // If there is another message not completely sent, add to queue only - if (mSendQueue.size() > 0) { - // Split to parts per 20 bytes max - for (; ; ) { - if (bytes.length > 20) { - byte[] addBytes = new byte[20]; - System.arraycopy(bytes, 0, addBytes, 0, addBytes.length); - byte[] reBytes = new byte[bytes.length - addBytes.length]; - System.arraycopy(bytes, addBytes.length, reBytes, 0, reBytes.length); - bytes = reBytes; - synchronized (mSendQueue) { - mSendQueue.add(addBytes); - } - } else { - synchronized (mSendQueue) { - mSendQueue.add(bytes); - } - break; - } - } - - } else { - if (bytes.length > 20) { - // Cut first 20 bytes - byte[] sendBytes = new byte[20]; - System.arraycopy(bytes, 0, sendBytes, 0, sendBytes.length); - byte[] reBytes = new byte[bytes.length - sendBytes.length]; - System.arraycopy(bytes, sendBytes.length, reBytes, 0, reBytes.length); - bytes = reBytes; - // and send - writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), sendBytes); - // The rest split to parts per 20 bytes max - for (; ; ) { - if (bytes.length > 20) { - byte[] addBytes = new byte[20]; - System.arraycopy(bytes, 0, addBytes, 0, addBytes.length); - reBytes = new byte[bytes.length - addBytes.length]; - System.arraycopy(bytes, addBytes.length, reBytes, 0, reBytes.length); - bytes = reBytes; - synchronized (mSendQueue) { - mSendQueue.add(addBytes); - } - } else { - synchronized (mSendQueue) { - mSendQueue.add(bytes); - } - break; - } - } - } else { - writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); - } - } - // The rest from queue is send from onCharasteristicWrite (after sending 1st part) - synchronized (message) { - try { - message.wait(5000); - } catch (InterruptedException e) { - log.error("sendMessage InterruptedException", e); - e.printStackTrace(); - } - } - - //SystemClock.sleep(200); - if (!message.isReceived()) { - log.warn("Reply not received " + message.getFriendlyName()); - } - scheduleDisconnection(); - } - - private void SendPairingRequest() { - // Start activity which is waiting 20sec - // On pump pairing request is displayed and is waiting for conformation - Intent i = new Intent(); - i.setClass(MainApp.instance(), PairingHelperActivity.class); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); - - byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST, null, null); - log.debug(">>>>> " + "ENCRYPTION__PASSKEY_REQUEST" + " " + DanaRS_Packet.toHexString(bytes)); - writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); - } - - private void SendTimeInfo() { - byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION, null, null); - log.debug(">>>>> " + "ENCRYPTION__TIME_INFORMATION" + " " + DanaRS_Packet.toHexString(bytes)); - writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); - } - - public void scheduleDisconnection() { - class DisconnectRunnable implements Runnable { - public void run() { - disconnect("scheduleDisconnection"); - scheduledDisconnection = null; - } - } - // prepare task for execution in 5 sec - // cancel waiting task to prevent sending multiple disconnections - if (scheduledDisconnection != null) - scheduledDisconnection.cancel(false); - Runnable task = new DisconnectRunnable(); - final int sec = 5; - scheduledDisconnection = worker.schedule(task, sec, TimeUnit.SECONDS); - } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java index 47f047b90a..f388f83e0d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java @@ -79,7 +79,7 @@ public class DanaRv2ExecutionService extends Service { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); String action = intent.getAction(); if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) { - log.debug("Device has disconnected " + device.getName());//Device has disconnected + log.debug("Device was disconnected " + device.getName());//Device was disconnected if (mBTDevice != null && mBTDevice.getName() != null && mBTDevice.getName().equals(device.getName())) { if (mSerialIOThread != null) { mSerialIOThread.disconnect("BT disconnection broadcast");