Skip to content
Snippets Groups Projects
Commit fc0b862d authored by Lennart Goedhart's avatar Lennart Goedhart
Browse files

Current live and working version of CNL uploader as of 2016-05-05.

Removed all options to use Dexcom or Medtronic CGM (sorry arbox0).
parent 17c3f544
Branches
Tags
No related merge requests found
Showing
with 1388 additions and 922 deletions
.gradle .gradle
app/build app/build
build/generated
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>
\ No newline at end of file
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<GradleProjectSettings> <GradleProjectSettings>
<option name="distributionType" value="LOCAL" /> <option name="distributionType" value="LOCAL" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.8" /> <option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.10" />
<option name="gradleJvm" value="1.8" /> <option name="gradleJvm" value="1.8" />
<option name="modules"> <option name="modules">
<set> <set>
...@@ -13,6 +13,12 @@ ...@@ -13,6 +13,12 @@
<option value="$PROJECT_DIR$/app" /> <option value="$PROJECT_DIR$/app" />
</set> </set>
</option> </option>
<option name="myModules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings> </GradleProjectSettings>
</option> </option>
</component> </component>
......
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AndroidLintRtlHardcoded" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>
\ No newline at end of file
This diff is collapsed.
...@@ -12,10 +12,7 @@ ...@@ -12,10 +12,7 @@
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" /> <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" /> <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" /> <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
<afterSyncTasks> <afterSyncTasks>
<task>generateDebugAndroidTestSources</task>
<task>generateDebugSources</task> <task>generateDebugSources</task>
</afterSyncTasks> </afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" /> <option name="ALLOW_USER_CONFIGURATION" value="false" />
...@@ -28,7 +25,7 @@ ...@@ -28,7 +25,7 @@
</component> </component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" /> <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" /> <output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" /> <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
...@@ -50,6 +47,15 @@ ...@@ -50,6 +47,15 @@
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
...@@ -57,6 +63,7 @@ ...@@ -57,6 +63,7 @@
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
...@@ -64,16 +71,36 @@ ...@@ -64,16 +71,36 @@
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/.DS_Store" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/restart-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" /> <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
......
...@@ -25,20 +25,21 @@ import java.io.IOException; ...@@ -25,20 +25,21 @@ import java.io.IOException;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Closeable; import java.io.Closeable;
import java.util.Map;
import com.nightscout.android.R; import com.nightscout.android.R;
/** /**
* Displays an EULA ("End User License Agreement") that the user has to accept before * Displays an EULA ("End User License Agreement") that the user has to accept before
* using the application. Your application should call {@link Eula#show(android.app.Activity)} * using the application. Your application should call {@link Eula#show(android.app.Activity, SharedPreferences)}
* in the onCreate() method of the first activity. If the user accepts the EULA, it will never * in the onCreate() method of the first activity. If the user accepts the EULA, it will never
* be shown again. If the user refuses, {@link android.app.Activity#finish()} is invoked * be shown again. If the user refuses, {@link android.app.Activity#finish()} is invoked
* on your activity. * on your activity.
*/ */
public class Eula { public class Eula {
private static final String ASSET_EULA = "EULA"; private static final String ASSET_EULA = "EULA";
private static final String PREFERENCE_EULA_ACCEPTED = "eula.accepted"; private static final String PREFERENCE_EULA_ACCEPTED = "IUNDERSTAND";
private static final String PREFERENCES_EULA = "eula"; private static final String PREFERENCES_EULA = "Disclaimer";
private static AlertDialog aDialog = null; private static AlertDialog aDialog = null;
/** /**
...@@ -63,9 +64,11 @@ public class Eula { ...@@ -63,9 +64,11 @@ public class Eula {
* @param activity The Activity to finish if the user rejects the EULA. * @param activity The Activity to finish if the user rejects the EULA.
* @return Whether the user has agreed already. * @return Whether the user has agreed already.
*/ */
public static boolean show(final Activity activity) { public static boolean show(final Activity activity, final SharedPreferences preferences ) {
final SharedPreferences preferences = activity.getSharedPreferences(PREFERENCES_EULA, //final SharedPreferences preferences = activity.getSharedPreferences(PREFERENCES_EULA,
Activity.MODE_PRIVATE); // Activity.MODE_PRIVATE);
Map<String, ?> preferencesAll = preferences.getAll();
if (!preferences.getBoolean(PREFERENCE_EULA_ACCEPTED, false)) { if (!preferences.getBoolean(PREFERENCE_EULA_ACCEPTED, false)) {
final AlertDialog.Builder builder = new AlertDialog.Builder(activity); final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(R.string.eula_title); builder.setTitle(R.string.eula_title);
......
...@@ -4,18 +4,41 @@ import android.util.Log; ...@@ -4,18 +4,41 @@ import android.util.Log;
import com.nightscout.android.USB.UsbHidDriver; import com.nightscout.android.USB.UsbHidDriver;
import com.nightscout.android.dexcom.USB.HexDump; import com.nightscout.android.dexcom.USB.HexDump;
import com.nightscout.android.medtronic.message.BeginEHSMMessage;
import com.nightscout.android.medtronic.message.ChannelNegotiateMessage; import com.nightscout.android.medtronic.message.ChannelNegotiateMessage;
import com.nightscout.android.medtronic.message.ChecksumException;
import com.nightscout.android.medtronic.message.ContourNextLinkBinaryMessage; import com.nightscout.android.medtronic.message.ContourNextLinkBinaryMessage;
import com.nightscout.android.medtronic.message.ContourNextLinkCommandMessage;
import com.nightscout.android.medtronic.message.ContourNextLinkMessage;
import com.nightscout.android.medtronic.message.ContourNextLinkMessageHandler;
import com.nightscout.android.medtronic.message.EncryptionException;
import com.nightscout.android.medtronic.message.EndEHSMMessage;
import com.nightscout.android.medtronic.message.MedtronicMessage;
import com.nightscout.android.medtronic.message.MessageUtils;
import com.nightscout.android.medtronic.message.PumpStatusRequestMessage;
import com.nightscout.android.medtronic.message.PumpStatusResponseMessage;
import com.nightscout.android.medtronic.message.PumpTimeRequestMessage;
import com.nightscout.android.medtronic.message.PumpTimeResponseMessage;
import com.nightscout.android.medtronic.message.UnexpectedMessageException;
import com.nightscout.android.medtronic.service.MedtronicCNLService; import com.nightscout.android.medtronic.service.MedtronicCNLService;
import com.nightscout.android.upload.Medtronic640gPumpRecord;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeoutException;
/** /**
* Created by lgoedhart on 24/03/2016. * Created by lgoedhart on 24/03/2016.
*/ */
public class MedtronicCNLReader { public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
private static final String TAG = MedtronicCNLService.class.getSimpleName(); private static final String TAG = MedtronicCNLService.class.getSimpleName();
...@@ -24,14 +47,14 @@ public class MedtronicCNLReader { ...@@ -24,14 +47,14 @@ public class MedtronicCNLReader {
private static final String BAYER_USB_HEADER = "ABC"; private static final String BAYER_USB_HEADER = "ABC";
private static final byte[] RADIO_CHANNELS = {0x14, 0x11, 0x0e, 0x17, 0x1a}; private static final byte[] RADIO_CHANNELS = {0x14, 0x11, 0x0e, 0x17, 0x1a};
public String deviceInfo;
private UsbHidDriver mDevice; private UsbHidDriver mDevice;
private MedtronicCNLSession mPumpSession = new MedtronicCNLSession(); private MedtronicCNLSession mPumpSession = new MedtronicCNLSession();
public MedtronicCNLReader(UsbHidDriver device) { public MedtronicCNLReader(UsbHidDriver device) {
mDevice = device; mDevice = device;
} }
public byte[] readMessage() throws IOException { public byte[] readMessage() throws IOException, TimeoutException {
ByteArrayOutputStream responseMessage = new ByteArrayOutputStream(); ByteArrayOutputStream responseMessage = new ByteArrayOutputStream();
byte[] responseBuffer = new byte[USB_BLOCKSIZE]; byte[] responseBuffer = new byte[USB_BLOCKSIZE];
...@@ -40,6 +63,12 @@ public class MedtronicCNLReader { ...@@ -40,6 +63,12 @@ public class MedtronicCNLReader {
do { do {
bytesRead = mDevice.read(responseBuffer, READ_TIMEOUT_MS); bytesRead = mDevice.read(responseBuffer, READ_TIMEOUT_MS);
if (bytesRead == 0) {
throw new TimeoutException("Timeout waiting for response from pump");
}
// Validate the header
ByteBuffer header = ByteBuffer.allocate(3); ByteBuffer header = ByteBuffer.allocate(3);
header.put(responseBuffer, 0, 3); header.put(responseBuffer, 0, 3);
String headerString = new String(header.array()); String headerString = new String(header.array());
...@@ -51,12 +80,30 @@ public class MedtronicCNLReader { ...@@ -51,12 +80,30 @@ public class MedtronicCNLReader {
} while (bytesRead > 0 && (messageSize + 4) == bytesRead); } while (bytesRead > 0 && (messageSize + 4) == bytesRead);
// TODO - how to deal with messages that finish on the boundary? // TODO - how to deal with messages that finish on the boundary?
// FIXME - remove debugging
String responseString = HexDump.dumpHexString(responseMessage.toByteArray()); String responseString = HexDump.dumpHexString(responseMessage.toByteArray());
Log.d(TAG, "READ: " + responseString); Log.d(TAG, "READ: " + responseString);
return responseMessage.toByteArray(); return responseMessage.toByteArray();
} }
@Override
public void sendMessage(ContourNextLinkMessage message) throws IOException {
sendMessage(message.encode());
if (message instanceof ContourNextLinkBinaryMessage) {
mPumpSession.incrBayerSequenceNumber();
}
if (message instanceof MedtronicMessage) {
mPumpSession.incrMedtronicSequenceNumber();
}
}
@Override
public ContourNextLinkMessage receiveMessage() {
return null;
}
public void sendMessage(byte[] message) throws IOException { public void sendMessage(byte[] message) throws IOException {
int pos = 0; int pos = 0;
...@@ -76,65 +123,210 @@ public class MedtronicCNLReader { ...@@ -76,65 +123,210 @@ public class MedtronicCNLReader {
} }
} }
public void sendMessage(byte message) throws IOException { // TODO - get rid of this - it should be in a message decoder
byte[] msg = {message}; private void checkControlMessage(byte[] msg, byte controlCharacter) throws IOException, TimeoutException, UnexpectedMessageException {
sendMessage(msg);
}
private void checkControlMessage(byte controlCharacter) throws IOException {
byte[] msg = readMessage();
if (msg.length != 1 || msg[0] != controlCharacter) { if (msg.length != 1 || msg[0] != controlCharacter) {
throw new RuntimeException(String.format("Expected to get an %d control character.", (int) controlCharacter)); throw new UnexpectedMessageException(String.format("Expected to get control character '%d' Got '%d'.",
(int) controlCharacter, (int) msg[0]));
} }
} }
public void requestDeviceInfo() throws IOException { public void requestDeviceInfo() throws IOException, TimeoutException, UnexpectedMessageException {
sendMessage((byte) 'X'); new ContourNextLinkCommandMessage("X").send(this);
// TODO - parse this into an ASTM record for the device info. boolean gotTimeout = false;
byte[] response = readMessage();
checkControlMessage(ASCII.ENQ.value); // TODO - parse this into an ASTM record for the device info.
try {
// The stick will return either the ASTM message, or the ENQ first. The order can change,
// so we need to handle both cases
byte[] response1 = readMessage();
byte[] response2 = readMessage();
if (response1[0] == ASCII.EOT.value) {
// response 1 is the ASTM message
checkControlMessage(response2, ASCII.ENQ.value);
} else {
// response 2 is the ASTM message
checkControlMessage(response1, ASCII.ENQ.value);
}
} catch (TimeoutException e) {
// Terminate comms with the pump, then try again
new ContourNextLinkCommandMessage(ASCII.EOT.value).send(this);
gotTimeout = true;
} finally {
// If we timed out - try to start the session again.
if (gotTimeout) {
requestDeviceInfo();
}
}
} }
public void enterControlMode() throws IOException { public void enterControlMode() throws IOException, TimeoutException, UnexpectedMessageException {
sendMessage(ASCII.NAK.value); new ContourNextLinkCommandMessage(ASCII.NAK.value).send(this);
checkControlMessage(ASCII.EOT.value); checkControlMessage(readMessage(), ASCII.EOT.value);
sendMessage(ASCII.ENQ.value); new ContourNextLinkCommandMessage(ASCII.ENQ.value).send(this);
checkControlMessage(ASCII.ACK.value); checkControlMessage(readMessage(), ASCII.ACK.value);
} }
public void enterPassthroughMode() throws IOException { public void enterPassthroughMode() throws IOException, TimeoutException, UnexpectedMessageException {
sendMessage("W|".getBytes()); new ContourNextLinkCommandMessage("W|").send(this);
checkControlMessage(ASCII.ACK.value); checkControlMessage(readMessage(), ASCII.ACK.value);
sendMessage("Q|".getBytes()); new ContourNextLinkCommandMessage("Q|").send(this);
checkControlMessage(ASCII.ACK.value); checkControlMessage(readMessage(), ASCII.ACK.value);
sendMessage("1|".getBytes()); new ContourNextLinkCommandMessage("1|").send(this);
checkControlMessage(ASCII.ACK.value); checkControlMessage(readMessage(), ASCII.ACK.value);
} }
public void openConnection() throws IOException { public void openConnection() throws IOException, TimeoutException {
ContourNextLinkBinaryMessage message = new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.OPEN_CONNECTION, mPumpSession, mPumpSession.getHMAC()); new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.OPEN_CONNECTION, mPumpSession, mPumpSession.getHMAC()).send(this);
//message.send(this);
sendMessage(message.encode());
// FIXME - We need to care what the response message is - wrong MAC and all that // FIXME - We need to care what the response message is - wrong MAC and all that
readMessage(); readMessage();
} }
public void requestReadInfo() throws IOException { public void requestReadInfo() throws IOException, TimeoutException {
ContourNextLinkBinaryMessage message = new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.READ_INFO, mPumpSession, null); new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.READ_INFO, mPumpSession, null).send(this);
sendMessage(message.encode()); // FIXME - pull the linkMAC and pumpMAC from here. It needs to go into the session.
// Don't care what the response message is at this stage
readMessage(); readMessage();
} }
public void negotiateChannel() throws IOException { public byte negotiateChannel() throws IOException, ChecksumException, TimeoutException {
for (byte channel : RADIO_CHANNELS) { for (byte channel : RADIO_CHANNELS) {
ChannelNegotiateMessage message = new ChannelNegotiateMessage( mPumpSession ); mPumpSession.setRadioChannel(channel);
sendMessage(message.encode()); new ChannelNegotiateMessage(mPumpSession).send(this);
// Don't care what the 0x81 response message is at this stage
readMessage();
// The 0x80 message
ContourNextLinkMessage response = ContourNextLinkBinaryMessage.fromBytes(readMessage());
byte[] responseBytes = response.encode();
if (responseBytes.length > 46) {
// Looks promising, let's check the last byte of the payload to make sure
if (responseBytes[76] == mPumpSession.getRadioChannel()) {
break;
} else {
throw new IOException(String.format("Expected to get a message for channel %d. Got %d", mPumpSession.getRadioChannel(), responseBytes[76]));
}
} else {
mPumpSession.setRadioChannel((byte) 0);
}
} }
return mPumpSession.getRadioChannel();
}
public void beginEHSMSession() throws EncryptionException, IOException, TimeoutException {
new BeginEHSMMessage(mPumpSession).send(this);
// The Begin EHSM Session only has an 0x81 response
readMessage();
}
public void getPumpTime(Medtronic640gPumpRecord pumpRecord) throws EncryptionException, IOException, ChecksumException, TimeoutException {
// FIXME - throw if not in EHSM mode (add a state machine)
new PumpTimeRequestMessage(mPumpSession).send(this);
// Read the 0x81
readMessage();
// Read the 0x80
ContourNextLinkMessage response = PumpTimeResponseMessage.fromBytes(mPumpSession, readMessage());
if (response.encode().length < 57) {
// Invalid message. Don't try and parse it
return;
}
// FIXME - this needs to go into PumpTimeResponseMessage
ByteBuffer dateBuffer = ByteBuffer.allocate(8);
dateBuffer.order(ByteOrder.BIG_ENDIAN);
dateBuffer.put(response.encode(), 61, 8);
long rtc = dateBuffer.getInt(0) & 0x00000000ffffffffL;
long offset = dateBuffer.getInt(4);
Date pumpDate = MessageUtils.decodeDateTime(rtc, offset);
// FIXME - HAX! Add 1 hour during AEST until we can figure out what Medtronic is doing
Calendar cal = Calendar.getInstance();
cal.setTime(pumpDate);
cal.add(Calendar.HOUR_OF_DAY, 1);
pumpDate = cal.getTime();
// Set displayTime to be an ISO 8601 date (so that it's parsable).
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
pumpRecord.displayTime = dateFormat.format(pumpDate);
pumpRecord.pumpDate = pumpDate;
}
public void getPumpStatus(Medtronic640gPumpRecord pumpRecord) throws IOException, EncryptionException, ChecksumException, TimeoutException {
// FIXME - throw if not in EHSM mode (add a state machine)
new PumpStatusRequestMessage(mPumpSession).send(this);
// Read the 0x81
readMessage();
// Read the 0x80
ContourNextLinkMessage response = PumpStatusResponseMessage.fromBytes(mPumpSession, readMessage());
if (response.encode().length < 57) {
// Invalid message. Don't try and parse it
return;
}
// FIXME - this needs to go into PumpStatusResponseMessage
ByteBuffer statusBuffer = ByteBuffer.allocate(96);
statusBuffer.order(ByteOrder.BIG_ENDIAN);
statusBuffer.put(response.encode(), 57, 96);
// Read the data into the record
pumpRecord.activeInsulin = (statusBuffer.getShort(51) & 0x0000ffff) / 10000f;
pumpRecord.sensorBGL = statusBuffer.getShort(53) & 0x0000ffff; // In mg/DL. 0 means no CGM reading
long rtc;
long offset;
if( ( pumpRecord.sensorBGL & 0x200 ) == 0x200 ) {
// Sensor error. Let's reset. FIXME - solve this more elegantly later
pumpRecord.sensorBGL = 0;
rtc = 0;
offset = 0;
} else {
rtc = statusBuffer.getInt(55) & 0x00000000ffffffffL;
offset = statusBuffer.getInt(59);
pumpRecord.setTrend(Medtronic640gPumpRecord.fromMessageByte( statusBuffer.get(64)));
}
pumpRecord.sensorBGLDate = MessageUtils.decodeDateTime(rtc, offset);
// FIXME - HAX! Add 1 hour during AEST until we can figure out what Medtronic is doing
Calendar cal = Calendar.getInstance();
cal.setTime(pumpRecord.sensorBGLDate);
cal.add(Calendar.HOUR_OF_DAY, 1);
pumpRecord.sensorBGLDate = cal.getTime();
pumpRecord.recentBolusWizard = statusBuffer.get(72) != 0;
pumpRecord.bolusWizardBGL = statusBuffer.getShort(73); // In mg/DL
}
public void endEHSMSession() throws EncryptionException, IOException, TimeoutException {
new EndEHSMMessage(mPumpSession).send(this);
// The End EHSM Session only has an 0x81 response
readMessage();
}
public void closeConnection() throws IOException, TimeoutException {
new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.CLOSE_CONNECTION, mPumpSession, null).send(this);
// FIXME - We need to care what the response message is - wrong MAC and all that
readMessage();
}
public void endPassthroughMode() throws IOException, TimeoutException, UnexpectedMessageException {
new ContourNextLinkCommandMessage("W|").send(this);
checkControlMessage(readMessage(), ASCII.ACK.value);
new ContourNextLinkCommandMessage("Q|").send(this);
checkControlMessage(readMessage(), ASCII.ACK.value);
new ContourNextLinkCommandMessage("0|").send(this);
checkControlMessage(readMessage(), ASCII.ACK.value);
}
public void endControlMode() throws IOException, TimeoutException, UnexpectedMessageException {
new ContourNextLinkCommandMessage(ASCII.EOT.value).send(this);
checkControlMessage(readMessage(), ASCII.ENQ.value);
} }
public enum ASCII { public enum ASCII {
......
...@@ -6,9 +6,11 @@ import com.nightscout.android.medtronic.message.MessageUtils; ...@@ -6,9 +6,11 @@ import com.nightscout.android.medtronic.message.MessageUtils;
* Created by lgoedhart on 26/03/2016. * Created by lgoedhart on 26/03/2016.
*/ */
public class MedtronicCNLSession { public class MedtronicCNLSession {
// FIXME - Lennart's hard coded key and HMAC
private final static byte[] HMAC = MessageUtils.hexStringToByteArray("e28fe4e5cf3c1eb6d6a2ec5a093093d4f397237dc60b3f2c1ef64f31e32077c4"); private final static byte[] HMAC = MessageUtils.hexStringToByteArray("e28fe4e5cf3c1eb6d6a2ec5a093093d4f397237dc60b3f2c1ef64f31e32077c4");
private final static byte[] KEY = MessageUtils.hexStringToByteArray("57833334130906a587b7a0437bc28a69"); private final static byte[] KEY = MessageUtils.hexStringToByteArray("57833334130906a587b7a0437bc28a69");
// FIXME - Lennart's hard coded serial numbers
private final static long linkMAC = 1055866 + 0x0023F70682000000L; private final static long linkMAC = 1055866 + 0x0023F70682000000L;
private final static long pumpMAC = 1057941 + 0x0023F745EE000000L; private final static long pumpMAC = 1057941 + 0x0023F745EE000000L;
...@@ -20,12 +22,13 @@ public class MedtronicCNLSession { ...@@ -20,12 +22,13 @@ public class MedtronicCNLSession {
return HMAC; return HMAC;
} }
public static byte[] getKEY() { public static byte[] getKey() {
return KEY; return KEY;
} }
public byte[] getIV() { public byte[] getIV() {
byte[] iv = HMAC; byte[] iv = new byte[KEY.length];
iv[0] = (byte) radioChannel; System.arraycopy(KEY,0,iv,0,KEY.length);
iv[0] = radioChannel;
return iv; return iv;
} }
...@@ -48,4 +51,16 @@ public class MedtronicCNLSession { ...@@ -48,4 +51,16 @@ public class MedtronicCNLSession {
public byte getRadioChannel() { public byte getRadioChannel() {
return radioChannel; return radioChannel;
} }
public void incrBayerSequenceNumber() {
bayerSequenceNumber++;
}
public void incrMedtronicSequenceNumber() {
medtronicSequenceNumber++;
}
public void setRadioChannel(byte radioChannel) {
this.radioChannel = radioChannel;
}
} }
package com.nightscout.android.medtronic.message;
import com.nightscout.android.medtronic.MedtronicCNLSession;
/**
* Created by lgoedhart on 26/03/2016.
*/
public class BeginEHSMMessage extends MedtronicSendMessage {
public BeginEHSMMessage(MedtronicCNLSession pumpSession) throws EncryptionException {
super(SendMessageType.BEGIN_EHSM_SESSION, pumpSession, buildPayload());
}
protected static byte[] buildPayload() {
// Not sure what the payload of a null byte means, but it's the same every time.
return new byte[] { 0x00 };
}
}
...@@ -10,19 +10,21 @@ import java.nio.ByteOrder; ...@@ -10,19 +10,21 @@ import java.nio.ByteOrder;
*/ */
public class ChannelNegotiateMessage extends MedtronicMessage { public class ChannelNegotiateMessage extends MedtronicMessage {
public ChannelNegotiateMessage(MedtronicCNLSession pumpSession) { public ChannelNegotiateMessage(MedtronicCNLSession pumpSession) {
super(CommandType.SEND_MESSAGE, CommandAction.CHANNEL_NEGOTIATE, pumpSession); super(CommandType.SEND_MESSAGE, CommandAction.CHANNEL_NEGOTIATE, pumpSession, buildPayload(pumpSession));
}
protected static byte[] buildPayload( MedtronicCNLSession pumpSession ) {
ByteBuffer payload = ByteBuffer.allocate(26); ByteBuffer payload = ByteBuffer.allocate(26);
payload.order(ByteOrder.LITTLE_ENDIAN); payload.order(ByteOrder.LITTLE_ENDIAN);
// The MedtronicMessage sequence number is always sent as 1 for this message, // The MedtronicMessage sequence number is always sent as 1 for this message,
// even though the sequence should keep incrementing as normal // even though the sequence should keep incrementing as normal
payload.put((byte) 1); payload.put((byte) 1);
payload.put(mPumpSession.getRadioChannel()); payload.put(pumpSession.getRadioChannel());
byte[] unknownBytes = {0, 0, 0, 0x07, 0x07, 0, 0, 0x02}; byte[] unknownBytes = {0, 0, 0, 0x07, 0x07, 0, 0, 0x02};
payload.put(unknownBytes); payload.put(unknownBytes);
payload.putLong(mPumpSession.getLinkMAC()); payload.putLong(pumpSession.getLinkMAC());
payload.putLong(mPumpSession.getPumpMAC()); payload.putLong(pumpSession.getPumpMAC());
setPayload(payload.array()); return payload.array();
} }
} }
...@@ -8,11 +8,11 @@ import java.nio.ByteOrder; ...@@ -8,11 +8,11 @@ import java.nio.ByteOrder;
/** /**
* Created by lgoedhart on 26/03/2016. * Created by lgoedhart on 26/03/2016.
*/ */
public class ContourNextLinkBinaryMessage { public class ContourNextLinkBinaryMessage extends ContourNextLinkMessage{
protected ByteBuffer mBayerEnvelope; //protected ByteBuffer mBayerEnvelope;
protected ByteBuffer mBayerPayload; //protected ByteBuffer mBayerPayload;
protected MedtronicCNLSession mPumpSession; //protected MedtronicCNLSession mPumpSession;
protected CommandType mCommandType = CommandType.NO_TYPE; //protected CommandType mCommandType = CommandType.NO_TYPE;
static int ENVELOPE_SIZE = 33; static int ENVELOPE_SIZE = 33;
...@@ -36,64 +36,44 @@ public class ContourNextLinkBinaryMessage { ...@@ -36,64 +36,44 @@ public class ContourNextLinkBinaryMessage {
} }
public ContourNextLinkBinaryMessage(CommandType commandType, MedtronicCNLSession pumpSession, byte[] payload) { public ContourNextLinkBinaryMessage(CommandType commandType, MedtronicCNLSession pumpSession, byte[] payload) {
mPumpSession = pumpSession; super(buildPayload(commandType, pumpSession, payload));
setPayload(payload);
} }
protected void setPayload(byte[] payload) { protected static byte[] buildPayload(CommandType commandType, MedtronicCNLSession pumpSession, byte[] payload) {
if( payload != null ) { int payloadLength = payload == null ? 0 : payload.length;
mBayerPayload = ByteBuffer.allocate( payload.length);
mBayerPayload.put(payload);
}
mBayerEnvelope = ByteBuffer.allocate(ENVELOPE_SIZE); ByteBuffer payloadBuffer = ByteBuffer.allocate( ENVELOPE_SIZE + payloadLength );
mBayerEnvelope.order(ByteOrder.LITTLE_ENDIAN); payloadBuffer.order(ByteOrder.LITTLE_ENDIAN);
mBayerEnvelope.put((byte) 0x51);
mBayerEnvelope.put((byte) 0x3); payloadBuffer.put((byte) 0x51);
mBayerEnvelope.put("000000".getBytes()); // Text of Pump serial, but 000000 for 640g payloadBuffer.put((byte) 0x3);
payloadBuffer.put("000000".getBytes()); // Text of Pump serial, but 000000 for 640g
byte[] unknownBytes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; byte[] unknownBytes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
mBayerEnvelope.put(unknownBytes); payloadBuffer.put(unknownBytes);
mBayerEnvelope.put(mCommandType.value); payloadBuffer.put(commandType.value);
mBayerEnvelope.putInt(mPumpSession.getBayerSequenceNumber()); payloadBuffer.putInt(pumpSession.getBayerSequenceNumber());
byte[] unknownBytes2 = {0, 0, 0, 0, 0}; byte[] unknownBytes2 = {0, 0, 0, 0, 0};
mBayerEnvelope.put(unknownBytes2); payloadBuffer.put(unknownBytes2);
mBayerEnvelope.putInt(mBayerPayload == null ? 0 : mBayerPayload.capacity()); payloadBuffer.putInt(payloadLength);
mBayerEnvelope.put(messageCrc()); payloadBuffer.put((byte) 0); // Placeholder for the CRC
}
private byte messageCrc() { if( payloadLength != 0 ) {
byte sum = MessageUtils.oneByteSum(mBayerEnvelope.array()); payloadBuffer.put(payload);
// Don't include the checkum byte in the checksum calculation!
sum -= mBayerEnvelope.get(32);
if (mBayerPayload != null) {
sum += MessageUtils.oneByteSum(mBayerPayload.array());
}
return sum;
} }
public byte[] encode() { // Now that we have the payload, calculate the message CRC
if (mBayerPayload != null) { payloadBuffer.position(32);
ByteBuffer out = ByteBuffer.allocate(mBayerEnvelope.capacity() + mBayerPayload.capacity()); payloadBuffer.put(MessageUtils.oneByteSum(payloadBuffer.array()));
out.put(mBayerEnvelope.array());
out.put(mBayerPayload.array()); return payloadBuffer.array();
return out.array();
} else {
return mBayerEnvelope.array();
}
} }
public static ContourNextLinkBinaryMessage fromBytes(byte[] bytes) throws ChecksumException { public static ContourNextLinkMessage fromBytes(byte[] bytes) throws ChecksumException {
ContourNextLinkBinaryMessage message = new ContourNextLinkBinaryMessage(CommandType.NO_TYPE, null, null); ContourNextLinkMessage message = new ContourNextLinkMessage(bytes);
message.mBayerEnvelope = ByteBuffer.allocate(ENVELOPE_SIZE);
message.mBayerEnvelope.put(bytes, 0, ENVELOPE_SIZE);
int payloadSize = bytes.length - ENVELOPE_SIZE;
message.mBayerPayload = ByteBuffer.allocate( payloadSize);
message.mBayerPayload.put(bytes, ENVELOPE_SIZE, payloadSize);
// Validate checksum // Validate checksum
byte messageChecksum = message.mBayerEnvelope.get(32); byte messageChecksum = message.mPayload.get(32);
byte calculatedChecksum = message.messageCrc(); byte calculatedChecksum = (byte) (MessageUtils.oneByteSum(message.mPayload.array()) - messageChecksum);
if (messageChecksum != calculatedChecksum) { if (messageChecksum != calculatedChecksum) {
throw new ChecksumException(String.format("Expected to get %d. Got %d", (int) calculatedChecksum, (int) messageChecksum)); throw new ChecksumException(String.format("Expected to get %d. Got %d", (int) calculatedChecksum, (int) messageChecksum));
......
package com.nightscout.android.medtronic.message;
import java.nio.ByteBuffer;
/**
* Created by lgoedhart on 26/03/2016.
*/
public class ContourNextLinkCommandMessage extends ContourNextLinkMessage {
public ContourNextLinkCommandMessage(byte command) {
super(new byte[]{command});
}
public ContourNextLinkCommandMessage(String command) {
super(command.getBytes());
}
}
package com.nightscout.android.medtronic.message; package com.nightscout.android.medtronic.message;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
...@@ -7,4 +8,25 @@ import java.nio.ByteBuffer; ...@@ -7,4 +8,25 @@ import java.nio.ByteBuffer;
*/ */
public class ContourNextLinkMessage { public class ContourNextLinkMessage {
protected ByteBuffer mPayload; protected ByteBuffer mPayload;
public ContourNextLinkMessage(byte[] bytes) {
if (bytes != null) {
this.mPayload = ByteBuffer.allocate(bytes.length);
this.mPayload.put(bytes);
}
}
public byte[] encode() {
return mPayload.array();
}
public void send(ContourNextLinkMessageHandler handler) throws IOException {
handler.sendMessage(this);
}
// FIXME - get rid of this - make a Builder instead
protected void setPayload(byte[] payload) {
mPayload = ByteBuffer.allocate(payload.length);
mPayload.put(payload);
}
} }
package com.nightscout.android.medtronic.message;
import java.io.IOException;
/**
* Created by lgoedhart on 26/03/2016.
*/
public interface ContourNextLinkMessageHandler {
void sendMessage( ContourNextLinkMessage message ) throws IOException;
ContourNextLinkMessage receiveMessage();
}
...@@ -3,7 +3,8 @@ package com.nightscout.android.medtronic.message; ...@@ -3,7 +3,8 @@ package com.nightscout.android.medtronic.message;
/** /**
* Created by lgoedhart on 26/03/2016. * Created by lgoedhart on 26/03/2016.
*/ */
public interface MessageHandler { public class EncryptionException extends Throwable {
void send( ContourNextLinkMessage message ); public EncryptionException(String message) {
ContourNextLinkMessage receive(); super(message);
}
} }
package com.nightscout.android.medtronic.message;
import com.nightscout.android.medtronic.MedtronicCNLSession;
/**
* Created by lgoedhart on 26/03/2016.
*/
public class EndEHSMMessage extends MedtronicSendMessage {
public EndEHSMMessage(MedtronicCNLSession pumpSession) throws EncryptionException {
super(SendMessageType.END_EHSM_SESSION, pumpSession, buildPayload());
}
protected static byte[] buildPayload() {
// Not sure what the payload byte means, but it's the same every time.
return new byte[] { 0x01 };
}
}
...@@ -4,6 +4,16 @@ import com.nightscout.android.medtronic.MedtronicCNLSession; ...@@ -4,6 +4,16 @@ import com.nightscout.android.medtronic.MedtronicCNLSession;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/** /**
* Created by lgoedhart on 26/03/2016. * Created by lgoedhart on 26/03/2016.
...@@ -11,7 +21,6 @@ import java.nio.ByteOrder; ...@@ -11,7 +21,6 @@ import java.nio.ByteOrder;
public class MedtronicMessage extends ContourNextLinkBinaryMessage { public class MedtronicMessage extends ContourNextLinkBinaryMessage {
static int ENVELOPE_SIZE = 2; static int ENVELOPE_SIZE = 2;
static int CRC_SIZE = 2; static int CRC_SIZE = 2;
protected CommandAction mCommandAction = CommandAction.NO_TYPE;
public enum CommandAction { public enum CommandAction {
NO_TYPE(0x0), NO_TYPE(0x0),
...@@ -26,32 +35,68 @@ public class MedtronicMessage extends ContourNextLinkBinaryMessage { ...@@ -26,32 +35,68 @@ public class MedtronicMessage extends ContourNextLinkBinaryMessage {
} }
} }
protected MedtronicMessage(CommandType commandType, CommandAction commandAction, MedtronicCNLSession pumpSession) { protected MedtronicMessage(CommandType commandType, CommandAction commandAction, MedtronicCNLSession pumpSession, byte[] payload) {
super(commandType, pumpSession, null); super(commandType, pumpSession, buildPayload(commandAction, payload));
mCommandAction = commandAction;
} }
@Override /**
protected void setPayload(byte[] payload) { * MedtronicMessage:
/* * +---------------+-------------------+----------------------+--------------------+
MedtronicMessage: * | CommandAction | byte Payload Size | byte[] Payload bytes | LE short CCITT CRC |
+---------------+-------------------+----------------------+--------------------+ * +---------------+-------------------+----------------------+--------------------+
| CommandAction | byte Payload Size | byte[] Payload bytes | LE short CCITT CRC |
+---------------+-------------------+----------------------+--------------------+
*/ */
protected static byte[] buildPayload(CommandAction commandAction, byte[] payload) {
byte payloadLength = (byte) (payload == null ? 0 : payload.length); byte payloadLength = (byte) (payload == null ? 0 : payload.length);
ByteBuffer payloadBuffer = ByteBuffer.allocate(ENVELOPE_SIZE + payloadLength + CRC_SIZE); ByteBuffer payloadBuffer = ByteBuffer.allocate(ENVELOPE_SIZE + payloadLength + CRC_SIZE);
payloadBuffer.order(ByteOrder.LITTLE_ENDIAN); payloadBuffer.order(ByteOrder.LITTLE_ENDIAN);
payloadBuffer.put(mCommandAction.value); payloadBuffer.put(commandAction.value);
payloadBuffer.put(payloadLength); payloadBuffer.put((byte) (ENVELOPE_SIZE + payloadLength));
if (payloadLength != 0) { if (payloadLength != 0) {
payloadBuffer.put(payload); payloadBuffer.put(payload != null ? payload : new byte[0]);
}
payloadBuffer.putShort((short) MessageUtils.CRC16CCITT(payloadBuffer.array(), 0xffff, 0x1021, ENVELOPE_SIZE + payloadLength));
return payloadBuffer.array();
} }
payloadBuffer.putShort(MessageUtils.ccittChecksum(payloadBuffer.array()));
super.setPayload( payloadBuffer.array() ); public static ContourNextLinkMessage fromBytes(byte[] bytes) throws ChecksumException {
ContourNextLinkMessage message = ContourNextLinkBinaryMessage.fromBytes(bytes);
// TODO - Validate the CCITT
return message;
}
// TODO - maybe move the SecretKeySpec, IvParameterSpec and Cipher construction into the PumpSession?
protected static byte[] encrypt(byte[] key, byte[] iv, byte[] clear) throws EncryptionException {
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
byte[] encrypted = new byte[0];
try {
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
encrypted = cipher.doFinal(clear);
} catch (Exception e) {
throw new EncryptionException( "Could not encrypt Medtronic Message" );
}
return encrypted;
}
protected static byte[] decrypt(byte[] key, byte[] iv, byte[] encrypted) throws EncryptionException {
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
byte[] decrypted;
try {
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
decrypted = cipher.doFinal(encrypted);
} catch (Exception e ) {
throw new EncryptionException( "Could not decrypt Medtronic Message" );
}
return decrypted;
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment