RS v3 encryption

This commit is contained in:
Milos Kozak 2020-03-26 20:25:20 +01:00
parent d1d49a8db2
commit 5ce8b998d9
18 changed files with 549 additions and 108 deletions

View file

@ -84,6 +84,8 @@
<activity <activity
android:name=".plugins.pump.danaRS.activities.PairingHelperActivity" android:name=".plugins.pump.danaRS.activities.PairingHelperActivity"
android:launchMode="singleTask" /> android:launchMode="singleTask" />
<activity android:name=".plugins.pump.danaRS.activities.EnterPinActivity"
android:launchMode="singleTask" />
<activity android:name=".historyBrowser.HistoryBrowseActivity" /> <activity android:name=".historyBrowser.HistoryBrowseActivity" />
<activity android:name=".activities.SurveyActivity" /> <activity android:name=".activities.SurveyActivity" />
<activity android:name=".activities.StatsActivity" /> <activity android:name=".activities.StatsActivity" />

View file

@ -13,6 +13,7 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyL
import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRHistoryActivity import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRHistoryActivity
import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRUserOptionsActivity import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRUserOptionsActivity
import info.nightscout.androidaps.plugins.pump.danaRS.activities.BLEScanActivity import info.nightscout.androidaps.plugins.pump.danaRS.activities.BLEScanActivity
import info.nightscout.androidaps.plugins.pump.danaRS.activities.EnterPinActivity
import info.nightscout.androidaps.plugins.pump.danaRS.activities.PairingHelperActivity import info.nightscout.androidaps.plugins.pump.danaRS.activities.PairingHelperActivity
import info.nightscout.androidaps.plugins.pump.insight.activities.InsightAlertActivity import info.nightscout.androidaps.plugins.pump.insight.activities.InsightAlertActivity
import info.nightscout.androidaps.plugins.pump.insight.activities.InsightPairingActivity import info.nightscout.androidaps.plugins.pump.insight.activities.InsightPairingActivity
@ -28,6 +29,7 @@ abstract class ActivitiesModule {
@ContributesAndroidInjector abstract fun contributeBolusProgressHelperActivity(): BolusProgressHelperActivity @ContributesAndroidInjector abstract fun contributeBolusProgressHelperActivity(): BolusProgressHelperActivity
@ContributesAndroidInjector abstract fun contributeDanaRHistoryActivity(): DanaRHistoryActivity @ContributesAndroidInjector abstract fun contributeDanaRHistoryActivity(): DanaRHistoryActivity
@ContributesAndroidInjector abstract fun contributeDanaRUserOptionsActivity(): DanaRUserOptionsActivity @ContributesAndroidInjector abstract fun contributeDanaRUserOptionsActivity(): DanaRUserOptionsActivity
@ContributesAndroidInjector abstract fun contributeEnterPinActivity(): EnterPinActivity
@ContributesAndroidInjector abstract fun contributeErrorHelperActivity(): ErrorHelperActivity @ContributesAndroidInjector abstract fun contributeErrorHelperActivity(): ErrorHelperActivity
@ContributesAndroidInjector abstract fun contributesHistoryBrowseActivity(): HistoryBrowseActivity @ContributesAndroidInjector abstract fun contributesHistoryBrowseActivity(): HistoryBrowseActivity
@ContributesAndroidInjector abstract fun contributesInsightAlertActivity(): InsightAlertActivity @ContributesAndroidInjector abstract fun contributesInsightAlertActivity(): InsightAlertActivity

View file

@ -15,6 +15,8 @@ import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ExtendedBolus;
import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.EventConfigBuilderChange;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.interfaces.CommandQueueProvider; import info.nightscout.androidaps.interfaces.CommandQueueProvider;
import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.ConstraintsInterface;
@ -42,6 +44,8 @@ import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.Round; import info.nightscout.androidaps.utils.Round;
import info.nightscout.androidaps.utils.resources.ResourceHelper; import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP; import info.nightscout.androidaps.utils.sharedPreferences.SP;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
/** /**
* Created by mike on 28.01.2018. * Created by mike on 28.01.2018.
@ -50,6 +54,8 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP;
public abstract class AbstractDanaRPlugin extends PumpPluginBase implements PumpInterface, DanaRInterface, ConstraintsInterface { public abstract class AbstractDanaRPlugin extends PumpPluginBase implements PumpInterface, DanaRInterface, ConstraintsInterface {
protected AbstractDanaRExecutionService sExecutionService; protected AbstractDanaRExecutionService sExecutionService;
protected CompositeDisposable disposable = new CompositeDisposable();
protected boolean useExtendedBoluses = false; protected boolean useExtendedBoluses = false;
protected PumpDescription pumpDescription = new PumpDescription(); protected PumpDescription pumpDescription = new PumpDescription();
@ -86,6 +92,28 @@ public abstract class AbstractDanaRPlugin extends PumpPluginBase implements Pump
this.sp = sp; this.sp = sp;
} }
@Override protected void onStart() {
super.onStart();
disposable.add(rxBus
.toObservable(EventConfigBuilderChange.class)
.observeOn(Schedulers.io())
.subscribe(event -> danaRPump.setLastConnection(0))
);
disposable.add(rxBus
.toObservable(EventPreferenceChange.class)
.observeOn(Schedulers.io())
.subscribe(event -> {
if (event.isChanged(getResourceHelper(), R.string.key_danar_bt_name))
danaRPump.setLastConnection(0);
})
);
}
@Override protected void onStop() {
super.onStop();
disposable.clear();
}
@Override @Override
public boolean isSuspended() { public boolean isSuspended() {
return danaRPump.getPumpSuspended(); return danaRPump.getPumpSuspended();
@ -488,6 +516,4 @@ public abstract class AbstractDanaRPlugin extends PumpPluginBase implements Pump
public void timeDateOrTimeZoneChanged() { public void timeDateOrTimeZoneChanged() {
} }
} }

View file

@ -11,6 +11,7 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.TDDStatsActivity import info.nightscout.androidaps.activities.TDDStatsActivity
import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.events.EventExtendedBolusChange import info.nightscout.androidaps.events.EventExtendedBolusChange
import info.nightscout.androidaps.events.EventInitializationChanged
import info.nightscout.androidaps.events.EventPumpStatusChanged import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.events.EventTempBasalChange import info.nightscout.androidaps.events.EventTempBasalChange
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
@ -24,13 +25,16 @@ import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRHistoryActi
import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRUserOptionsActivity import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRUserOptionsActivity
import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRNewStatus import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRNewStatus
import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin
import info.nightscout.androidaps.queue.events.EventQueueChanged import info.nightscout.androidaps.queue.events.EventQueueChanged
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.OKDialog
import info.nightscout.androidaps.utils.SetWarnColor import info.nightscout.androidaps.utils.SetWarnColor
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.extensions.plusAssign import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.danar_fragment.* import kotlinx.android.synthetic.main.danar_fragment.*
@ -43,8 +47,10 @@ class DanaRFragment : DaggerFragment() {
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var danaRKoreanPlugin: DanaRKoreanPlugin @Inject lateinit var danaRKoreanPlugin: DanaRKoreanPlugin
@Inject lateinit var danaRSPlugin: DanaRSPlugin
@Inject lateinit var danaRPump: DanaRPump @Inject lateinit var danaRPump: DanaRPump
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var sp: SP
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
@ -93,12 +99,28 @@ class DanaRFragment : DaggerFragment() {
danaRPump.lastConnection = 0 danaRPump.lastConnection = 0
commandQueue.readStatus("Clicked connect to pump", null) commandQueue.readStatus("Clicked connect to pump", null)
} }
if (danaRSPlugin.isEnabled())
danar_btconnection.setOnLongClickListener {
activity?.let {
OKDialog.showConfirmation(it, resourceHelper.gs(R.string.resetpairing), Runnable {
sp.remove(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName)
sp.remove(resourceHelper.gs(R.string.key_danars_v3_randompairingkey) + danaRSPlugin.mDeviceName)
sp.remove(resourceHelper.gs(R.string.key_danars_v3_pairingkey) + danaRSPlugin.mDeviceName)
sp.remove(resourceHelper.gs(R.string.key_danars_v3_randomsynckey) + danaRSPlugin.mDeviceName)
})
}
true
}
} }
@Synchronized @Synchronized
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
loopHandler.postDelayed(refreshLoop, T.mins(1).msecs()) loopHandler.postDelayed(refreshLoop, T.mins(1).msecs())
disposable += rxBus
.toObservable(EventInitializationChanged::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ updateGUI() }, { fabricPrivacy.logException(it) })
disposable += rxBus disposable += rxBus
.toObservable(EventDanaRNewStatus::class.java) .toObservable(EventDanaRNewStatus::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View file

@ -27,6 +27,7 @@ import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ExtendedBolus;
import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.events.EventConfigBuilderChange;
import info.nightscout.androidaps.interfaces.CommandQueueProvider; import info.nightscout.androidaps.interfaces.CommandQueueProvider;
import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.ConstraintsInterface;
@ -157,6 +158,11 @@ public class DanaRSPlugin extends PumpPluginBase implements PumpInterface, DanaR
.observeOn(Schedulers.io()) .observeOn(Schedulers.io())
.subscribe(event -> context.unbindService(mConnection), fabricPrivacy::logException) .subscribe(event -> context.unbindService(mConnection), fabricPrivacy::logException)
); );
disposable.add(rxBus
.toObservable(EventConfigBuilderChange.class)
.observeOn(Schedulers.io())
.subscribe(event -> danaRPump.setLastConnection(0))
);
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventDanaRSDeviceChange.class) .toObservable(EventDanaRSDeviceChange.class)
.observeOn(Schedulers.io()) .observeOn(Schedulers.io())
@ -191,6 +197,7 @@ public class DanaRSPlugin extends PumpPluginBase implements PumpInterface, DanaR
private void loadAddress() { private void loadAddress() {
mDeviceAddress = sp.getString(R.string.key_danars_address, ""); mDeviceAddress = sp.getString(R.string.key_danars_address, "");
mDeviceName = sp.getString(R.string.key_danars_name, ""); mDeviceName = sp.getString(R.string.key_danars_name, "");
danaRPump.setLastConnection(0);
} }
@Override @Override

View file

@ -0,0 +1,99 @@
package info.nightscout.androidaps.plugins.pump.danaRS.activities
import android.os.Bundle
import android.util.Base64
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin
import info.nightscout.androidaps.plugins.pump.danaRS.services.BLEComm
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.OKDialog
import info.nightscout.androidaps.utils.extensions.hexStringToByteArray
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.textValidator.DefaultEditTextValidator
import info.nightscout.androidaps.utils.textValidator.EditTextValidator
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.danars_enter_pin_activity.*
import kotlinx.android.synthetic.main.okcancel.*
import javax.inject.Inject
import kotlin.experimental.xor
class EnterPinActivity : NoSplashAppCompatActivity() {
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var danaRSPlugin: DanaRSPlugin
@Inject lateinit var sp: SP
@Inject lateinit var bleComm: BLEComm
private val disposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.danars_enter_pin_activity)
val p1 = DefaultEditTextValidator(rs_v3_pin1, this)
.setTestErrorString(resourceHelper.gs(R.string.error_mustbe12hexadidits), this)
.setCustomRegexp(resourceHelper.gs(R.string.twelvehexanumber), this)
.setTestType(EditTextValidator.TEST_REGEXP, this)
val p2 = DefaultEditTextValidator(rs_v3_pin2, this)
.setTestErrorString(resourceHelper.gs(R.string.error_mustbe8hexadidits), this)
.setCustomRegexp(resourceHelper.gs(R.string.eighthexanumber), this)
.setTestType(EditTextValidator.TEST_REGEXP, this)
ok.setOnClickListener {
if (p1.testValidity(false) && p2.testValidity(false)) {
val result = checkPairingCheckSum(
rs_v3_pin1.text.toString().hexStringToByteArray(),
rs_v3_pin2.text.toString().substring(0..5).hexStringToByteArray(),
rs_v3_pin2.text.toString().substring(6..7).hexStringToByteArray())
if (result) {
bleComm.finishV3Pairing()
finish()
}
else OKDialog.show(this, resourceHelper.gs(R.string.error), resourceHelper.gs(R.string.danar_invalidinput))
}
}
cancel.setOnClickListener { finish() }
}
override fun onResume() {
super.onResume()
disposable.add(rxBus
.toObservable(EventPumpStatusChanged::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ if (it.status == EventPumpStatusChanged.Status.DISCONNECTED) finish() }) { fabricPrivacy.logException(it) }
)
}
override fun onPause() {
super.onPause()
disposable.clear()
}
private fun checkPairingCheckSum(pairingKey: ByteArray, randomPairingKey: ByteArray, checksum: ByteArray): Boolean {
// pairingKey ByteArray(6)
// randomPairingKey ByteArray(3)
// checksum ByteArray(1)
var pairingKeyCheckSum: Byte = 0
for (i in pairingKey.indices)
pairingKeyCheckSum = pairingKeyCheckSum xor pairingKey[i]
sp.putString(resourceHelper.gs(R.string.key_danars_v3_pairingkey) + danaRSPlugin.mDeviceName, Base64.encodeToString(pairingKey, Base64.DEFAULT))
for (i in randomPairingKey.indices)
pairingKeyCheckSum = pairingKeyCheckSum xor randomPairingKey[i]
sp.putString(resourceHelper.gs(R.string.key_danars_v3_randompairingkey) + danaRSPlugin.mDeviceName, Base64.encodeToString(randomPairingKey, Base64.DEFAULT))
return checksum[0] == pairingKeyCheckSum
}
}

View file

@ -69,7 +69,7 @@ class DanaRSMessageHashTable @Inject constructor(
put(DanaRS_Packet_General_Delivery_Status(aapsLogger)) put(DanaRS_Packet_General_Delivery_Status(aapsLogger))
put(DanaRS_Packet_General_Get_Password(aapsLogger, danaRPump)) put(DanaRS_Packet_General_Get_Password(aapsLogger, danaRPump))
put(DanaRS_Packet_General_Initial_Screen_Information(aapsLogger, danaRPump)) put(DanaRS_Packet_General_Initial_Screen_Information(aapsLogger, danaRPump))
put(DanaRS_Packet_Notify_Alarm(aapsLogger, resourceHelper)) put(DanaRS_Packet_Notify_Alarm(aapsLogger, resourceHelper, rxBus))
put(DanaRS_Packet_Notify_Delivery_Complete(aapsLogger, rxBus, resourceHelper, danaRSPlugin)) put(DanaRS_Packet_Notify_Delivery_Complete(aapsLogger, rxBus, resourceHelper, danaRSPlugin))
put(DanaRS_Packet_Notify_Delivery_Rate_Display(aapsLogger, rxBus, resourceHelper, danaRSPlugin)) put(DanaRS_Packet_Notify_Delivery_Rate_Display(aapsLogger, rxBus, resourceHelper, danaRSPlugin))
put(DanaRS_Packet_Notify_Missed_Bolus_Alarm(aapsLogger)) put(DanaRS_Packet_Notify_Missed_Bolus_Alarm(aapsLogger))

View file

@ -11,7 +11,6 @@ class DanaRS_Packet_General_Initial_Screen_Information(
) : DanaRS_Packet() { ) : DanaRS_Packet() {
init { init {
type = BleEncryption.DANAR_PACKET__TYPE_RESPONSE
opCode = BleEncryption.DANAR_PACKET__OPCODE_REVIEW__INITIAL_SCREEN_INFORMATION opCode = BleEncryption.DANAR_PACKET__OPCODE_REVIEW__INITIAL_SCREEN_INFORMATION
aapsLogger.debug(LTag.PUMPCOMM, "New message") aapsLogger.debug(LTag.PUMPCOMM, "New message")
} }

View file

@ -4,12 +4,16 @@ import info.nightscout.androidaps.plugins.pump.danaRS.encryption.BleEncryption
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
class DanaRS_Packet_Notify_Alarm( class DanaRS_Packet_Notify_Alarm(
private val aapsLogger: AAPSLogger, private val aapsLogger: AAPSLogger,
private val resourceHelper: ResourceHelper private val resourceHelper: ResourceHelper,
private val rxBus: RxBusWrapper
) : DanaRS_Packet() { ) : DanaRS_Packet() {
init { init {
@ -29,11 +33,11 @@ class DanaRS_Packet_Notify_Alarm(
0x03 -> // Occlusion 0x03 -> // Occlusion
errorString = resourceHelper.gs(R.string.occlusion) errorString = resourceHelper.gs(R.string.occlusion)
0x04 -> // LOW BATTERY 0x04 -> // LOW BATTERY
errorString = resourceHelper.gs(R.string.lowbattery) errorString = resourceHelper.gs(R.string.pumpshutdown)
0x05 -> // Shutdown 0x05 -> // Shutdown
errorString = resourceHelper.gs(R.string.lowbattery) errorString = resourceHelper.gs(R.string.lowbattery)
0x06 -> // Basal Compare 0x06 -> // Basal Compare
errorString = "BasalCompare ????" errorString = resourceHelper.gs(R.string.basalcompare)
0x09 -> // Empty Reservoir 0x09 -> // Empty Reservoir
errorString = resourceHelper.gs(R.string.emptyreservoir) errorString = resourceHelper.gs(R.string.emptyreservoir)
0x07, 0xFF -> // Blood sugar measurement alert 0x07, 0xFF -> // Blood sugar measurement alert
@ -41,7 +45,7 @@ class DanaRS_Packet_Notify_Alarm(
0x08, 0xFE -> // Remaining insulin level 0x08, 0xFE -> // Remaining insulin level
errorString = resourceHelper.gs(R.string.remaininsulinalert) errorString = resourceHelper.gs(R.string.remaininsulinalert)
0xFD -> // Blood sugar check miss alarm 0xFD -> // Blood sugar check miss alarm
errorString = "Blood sugar check miss alarm ???" errorString = resourceHelper.gs(R.string.missedbolus)
} }
// No error no need to upload anything // No error no need to upload anything
if (errorString == "") { if (errorString == "") {
@ -49,6 +53,8 @@ class DanaRS_Packet_Notify_Alarm(
aapsLogger.debug(LTag.PUMPCOMM, "Error detected: $errorString") aapsLogger.debug(LTag.PUMPCOMM, "Error detected: $errorString")
return return
} }
val notification = Notification(Notification.USERMESSAGE, errorString, Notification.URGENT)
rxBus.send(EventNewNotification(notification))
NSUpload.uploadError(errorString) NSUpload.uploadError(errorString)
} }

View file

@ -140,7 +140,7 @@ public class BleEncryption {
return encryptSecondLevelPacketJni(context, bytes); return encryptSecondLevelPacketJni(context, bytes);
} }
public byte[] ecryptSecondLevelPacket(byte[] bytes) { public byte[] decryptSecondLevelPacket(byte[] bytes) {
return decryptSecondLevelPacketJni(context, bytes); return decryptSecondLevelPacketJni(context, bytes);
} }
} }

View file

@ -4,6 +4,7 @@ import android.bluetooth.*
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.SystemClock import android.os.SystemClock
import android.util.Base64
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.EventPumpStatusChanged import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
@ -14,11 +15,13 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotifi
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin
import info.nightscout.androidaps.plugins.pump.danaRS.activities.EnterPinActivity
import info.nightscout.androidaps.plugins.pump.danaRS.activities.PairingHelperActivity import info.nightscout.androidaps.plugins.pump.danaRS.activities.PairingHelperActivity
import info.nightscout.androidaps.plugins.pump.danaRS.comm.DanaRSMessageHashTable import info.nightscout.androidaps.plugins.pump.danaRS.comm.DanaRSMessageHashTable
import info.nightscout.androidaps.plugins.pump.danaRS.comm.DanaRS_Packet import info.nightscout.androidaps.plugins.pump.danaRS.comm.DanaRS_Packet
import info.nightscout.androidaps.plugins.pump.danaRS.encryption.BleEncryption import info.nightscout.androidaps.plugins.pump.danaRS.encryption.BleEncryption
import info.nightscout.androidaps.plugins.pump.danaRS.events.EventDanaRSPairingSuccess import info.nightscout.androidaps.plugins.pump.danaRS.events.EventDanaRSPairingSuccess
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import okhttp3.internal.notify import okhttp3.internal.notify
@ -58,8 +61,17 @@ class BLEComm @Inject internal constructor(
private var connectDeviceName: String? = null private var connectDeviceName: String? = null
private var bluetoothGatt: BluetoothGatt? = null private var bluetoothGatt: BluetoothGatt? = null
private var v3Encryption: Boolean = false
set(newValue) {
bleEncryption.setEnhancedEncryption(newValue)
field = newValue
}
private var isEasyMode: Boolean = false
private var isUnitUD: Boolean = false
var isConnected = false var isConnected = false
var isConnecting = false var isConnecting = false
private var encryptedDataRead = false
private var uartRead: BluetoothGattCharacteristic? = null private var uartRead: BluetoothGattCharacteristic? = null
private var uartWrite: BluetoothGattCharacteristic? = null private var uartWrite: BluetoothGattCharacteristic? = null
@ -106,6 +118,14 @@ class BLEComm @Inject internal constructor(
fun disconnect(from: String) { fun disconnect(from: String) {
aapsLogger.debug(LTag.PUMPBTCOMM, "disconnect from: $from") aapsLogger.debug(LTag.PUMPBTCOMM, "disconnect from: $from")
if (!encryptedDataRead) {
// there was no response from pump after started encryption
// assume pairing keys are invalid
sp.remove(resourceHelper.gs(R.string.key_danars_v3_randompairingkey) + danaRSPlugin.mDeviceName)
sp.remove(resourceHelper.gs(R.string.key_danars_v3_pairingkey) + danaRSPlugin.mDeviceName)
sp.remove(resourceHelper.gs(R.string.key_danars_v3_randomsynckey) + danaRSPlugin.mDeviceName)
ToastUtils.showToastInUiThread(context, R.string.invalidpairing)
}
// cancel previous scheduled disconnection to prevent closing upcoming connection // cancel previous scheduled disconnection to prevent closing upcoming connection
scheduledDisconnection?.cancel(false) scheduledDisconnection?.cancel(false)
scheduledDisconnection = null scheduledDisconnection = null
@ -118,6 +138,7 @@ class BLEComm @Inject internal constructor(
setCharacteristicNotification(uartReadBTGattChar, false) setCharacteristicNotification(uartReadBTGattChar, false)
bluetoothGatt?.disconnect() bluetoothGatt?.disconnect()
isConnected = false isConnected = false
encryptedDataRead = false
SystemClock.sleep(2000) SystemClock.sleep(2000)
} }
@ -142,19 +163,20 @@ class BLEComm @Inject internal constructor(
} }
override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
aapsLogger.debug(LTag.PUMPBTCOMM, "onCharacteristicRead: " + DanaRS_Packet.toHexString(characteristic.value)) // for v3 after initial handshake it's encrypted - useless
addToReadBuffer(characteristic.value) // aapsLogger.debug(LTag.PUMPBTCOMM, "onCharacteristicRead: " + DanaRS_Packet.toHexString(characteristic.value))
readDataParsing() readDataParsing(characteristic.value)
} }
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
aapsLogger.debug(LTag.PUMPBTCOMM, "onCharacteristicChanged: " + DanaRS_Packet.toHexString(characteristic.value)) // for v3 after initial handshake it's encrypted - useless
addToReadBuffer(characteristic.value) // aapsLogger.debug(LTag.PUMPBTCOMM, "onCharacteristicChanged: " + DanaRS_Packet.toHexString(characteristic.value))
Thread(Runnable { readDataParsing() }).start() readDataParsing(characteristic.value)
} }
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
aapsLogger.debug(LTag.PUMPBTCOMM, "onCharacteristicWrite: " + DanaRS_Packet.toHexString(characteristic.value)) // for v3 after initial handshake it's encrypted - useless
// aapsLogger.debug(LTag.PUMPBTCOMM, "onCharacteristicWrite: " + DanaRS_Packet.toHexString(characteristic.value))
Thread(Runnable { Thread(Runnable {
synchronized(mSendQueue) { synchronized(mSendQueue) {
// after message sent, check if there is the rest of the message waiting and send it // after message sent, check if there is the rest of the message waiting and send it
@ -175,9 +197,10 @@ class BLEComm @Inject internal constructor(
aapsLogger.error("BluetoothAdapter not initialized_ERROR") aapsLogger.error("BluetoothAdapter not initialized_ERROR")
isConnecting = false isConnecting = false
isConnected = false isConnected = false
encryptedDataRead = false
return return
} }
bluetoothGatt!!.setCharacteristicNotification(characteristic, enabled) bluetoothGatt?.setCharacteristicNotification(characteristic, enabled)
} }
@Synchronized @Synchronized
@ -188,13 +211,15 @@ class BLEComm @Inject internal constructor(
aapsLogger.error("BluetoothAdapter not initialized_ERROR") aapsLogger.error("BluetoothAdapter not initialized_ERROR")
isConnecting = false isConnecting = false
isConnected = false isConnected = false
encryptedDataRead = false
return@Runnable return@Runnable
} }
characteristic.value = data characteristic.value = data
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
aapsLogger.debug("writeCharacteristic:" + DanaRS_Packet.toHexString(data)) //aapsLogger.debug("writeCharacteristic:" + DanaRS_Packet.toHexString(data))
bluetoothGatt!!.writeCharacteristic(characteristic) bluetoothGatt?.writeCharacteristic(characteristic)
}).start() }).start()
waitMillis(50)
} }
private val uartReadBTGattChar: BluetoothGattCharacteristic private val uartReadBTGattChar: BluetoothGattCharacteristic
@ -211,6 +236,7 @@ class BLEComm @Inject internal constructor(
aapsLogger.error("BluetoothAdapter not initialized_ERROR") aapsLogger.error("BluetoothAdapter not initialized_ERROR")
isConnecting = false isConnecting = false
isConnected = false isConnected = false
encryptedDataRead = false
return null return null
} }
return bluetoothGatt?.services return bluetoothGatt?.services
@ -243,6 +269,8 @@ class BLEComm @Inject internal constructor(
close() close()
isConnected = false isConnected = false
isConnecting = false isConnecting = false
v3Encryption = false
encryptedDataRead = false
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)) rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED))
aapsLogger.debug(LTag.PUMPBTCOMM, "Device was disconnected " + gatt.device.name) //Device was disconnected aapsLogger.debug(LTag.PUMPBTCOMM, "Device was disconnected " + gatt.device.name) //Device was disconnected
} }
@ -263,13 +291,20 @@ class BLEComm @Inject internal constructor(
} }
} }
private fun readDataParsing() { private fun readDataParsing(receviedData: ByteArray) {
//aapsLogger.debug(LTag.PUMPBTCOMM, "readDataParsing")
var startSignatureFound = false var startSignatureFound = false
var packetIsValid = false var packetIsValid = false
var isProcessing: Boolean var isProcessing: Boolean
isProcessing = true isProcessing = true
var inputBuffer: ByteArray? = null var inputBuffer: ByteArray? = null
// decrypt 2nd level after successful connection
val incomingBuffer = if (v3Encryption && isConnected)
bleEncryption.decryptSecondLevelPacket(receviedData).also { encryptedDataRead = true }
else receviedData
addToReadBuffer(incomingBuffer)
while (isProcessing) { while (isProcessing) {
var length = 0 var length = 0
synchronized(readBuffer) { synchronized(readBuffer) {
@ -321,85 +356,163 @@ class BLEComm @Inject internal constructor(
// decrypt the packet // decrypt the packet
bleEncryption.getDecryptedPacket(inputBuffer)?.let { decryptedBuffer -> bleEncryption.getDecryptedPacket(inputBuffer)?.let { decryptedBuffer ->
when (decryptedBuffer[0]) { when (decryptedBuffer[0]) {
BleEncryption.DANAR_PACKET__TYPE_ENCRYPTION_RESPONSE.toByte() -> when (decryptedBuffer[1]) { BleEncryption.DANAR_PACKET__TYPE_ENCRYPTION_RESPONSE.toByte() ->
BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK.toByte() -> if (decryptedBuffer.size == 4 && decryptedBuffer[2] == 'O'.toByte() && decryptedBuffer[3] == 'K'.toByte()) { when (decryptedBuffer[1]) {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (OK)" + " " + DanaRS_Packet.toHexString(decryptedBuffer)) BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK.toByte() ->
// Grab pairing key from preferences if exists if (decryptedBuffer.size == 4 && decryptedBuffer[2] == 'O'.toByte() && decryptedBuffer[3] == 'K'.toByte()) {
val pairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName, "") aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (OK)" + " " + DanaRS_Packet.toHexString(decryptedBuffer))
aapsLogger.debug(LTag.PUMPBTCOMM, "Using stored pairing key: $pairingKey") v3Encryption = false
if (pairingKey.isNotEmpty()) { // Grab pairing key from preferences if exists
val encodedPairingKey = DanaRS_Packet.hexToBytes(pairingKey) val pairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName, "")
val bytes = bleEncryption.getEncryptedPacket(BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY, encodedPairingKey, null) aapsLogger.debug(LTag.PUMPBTCOMM, "Using stored pairing key: $pairingKey")
aapsLogger.debug(LTag.PUMPBTCOMM, ">>>>> " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(bytes)) if (pairingKey.isNotEmpty()) {
writeCharacteristicNoResponse(uartWriteBTGattChar, bytes) val encodedPairingKey = DanaRS_Packet.hexToBytes(pairingKey)
} else { val bytes = bleEncryption.getEncryptedPacket(BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY, encodedPairingKey, null)
// Stored pairing key does not exists, request pairing aapsLogger.debug(LTag.PUMPBTCOMM, ">>>>> " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(bytes))
sendPairingRequest() writeCharacteristicNoResponse(uartWriteBTGattChar, bytes)
} } else {
} else if (decryptedBuffer.size == 6 && decryptedBuffer[2] == 'P'.toByte() && decryptedBuffer[3] == 'U'.toByte() && decryptedBuffer[4] == 'M'.toByte() && decryptedBuffer[5] == 'P'.toByte()) { // Stored pairing key does not exists, request pairing
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (PUMP)" + " " + DanaRS_Packet.toHexString(decryptedBuffer)) sendPairingRequest()
mSendQueue.clear() }
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED, resourceHelper.gs(R.string.pumperror))) } else if (decryptedBuffer.size == 9 && decryptedBuffer[2] == 'O'.toByte() && decryptedBuffer[3] == 'K'.toByte()) {
NSUpload.uploadError(resourceHelper.gs(R.string.pumperror)) // v3 2nd layer encryption
val n = Notification(Notification.PUMPERROR, resourceHelper.gs(R.string.pumperror), Notification.URGENT) v3Encryption = true
rxBus.send(EventNewNotification(n)) val model = decryptedBuffer[5]
} else if (decryptedBuffer.size == 6 && decryptedBuffer[2] == 'B'.toByte() && decryptedBuffer[3] == 'U'.toByte() && decryptedBuffer[4] == 'S'.toByte() && decryptedBuffer[5] == 'Y'.toByte()) { // val protocol = decryptedBuffer[7]
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (BUSY)" + " " + DanaRS_Packet.toHexString(decryptedBuffer)) if (model == 0x05.toByte()) {
mSendQueue.clear() aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK V3 (OK)" + " " + DanaRS_Packet.toHexString(decryptedBuffer))
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED, resourceHelper.gs(R.string.pumpbusy))) // Dana RS Pump
} else { val randomPairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_v3_randompairingkey) + danaRSPlugin.mDeviceName, "")
// ERROR in response, wrong serial number val pairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_v3_pairingkey) + danaRSPlugin.mDeviceName, "")
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (ERROR)" + " " + DanaRS_Packet.toHexString(decryptedBuffer)) if (randomPairingKey.isNotEmpty() && pairingKey.isNotEmpty()) {
mSendQueue.clear() val randomSyncKey = String.format("%02x", decryptedBuffer[decryptedBuffer.size - 1])
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED, resourceHelper.gs(R.string.connectionerror))) sp.putString(resourceHelper.gs(R.string.key_danars_v3_randomsynckey) + danaRSPlugin.mDeviceName, randomSyncKey)
sp.remove(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName) val tPairingKey = Base64.decode(pairingKey, Base64.DEFAULT)
val n = Notification(Notification.WRONGSERIALNUMBER, resourceHelper.gs(R.string.wrongpassword), Notification.URGENT) val tRandomPairingKey = Base64.decode(randomPairingKey, Base64.DEFAULT)
rxBus.send(EventNewNotification(n)) var tRandomSyncKey: Byte = 0
} if (randomSyncKey.isNotEmpty()) {
tRandomSyncKey = randomSyncKey.toInt(16).toByte()
}
bleEncryption.setPairingKeys(tPairingKey, tRandomPairingKey, tRandomSyncKey)
sendV3PairingInformation(0)
} else {
sendV3PairingInformation(1)
}
} else if (model == 0x06.toByte()) {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK V3 EASY (OK)" + " " + DanaRS_Packet.toHexString(decryptedBuffer))
// Dana RS Easy
val bytes: ByteArray = bleEncryption.getEncryptedPacket(BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__GET_EASYMENU_CHECK, null, null)
writeCharacteristicNoResponse(uartWriteBTGattChar, bytes)
}
} else if (decryptedBuffer.size == 6 && decryptedBuffer[2] == 'P'.toByte() && decryptedBuffer[3] == 'U'.toByte() && decryptedBuffer[4] == 'M'.toByte() && decryptedBuffer[5] == 'P'.toByte()) {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (PUMP)" + " " + DanaRS_Packet.toHexString(decryptedBuffer))
mSendQueue.clear()
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED, resourceHelper.gs(R.string.pumperror)))
NSUpload.uploadError(resourceHelper.gs(R.string.pumperror))
val n = Notification(Notification.PUMPERROR, resourceHelper.gs(R.string.pumperror), Notification.URGENT)
rxBus.send(EventNewNotification(n))
} else if (decryptedBuffer.size == 6 && decryptedBuffer[2] == 'B'.toByte() && decryptedBuffer[3] == 'U'.toByte() && decryptedBuffer[4] == 'S'.toByte() && decryptedBuffer[5] == 'Y'.toByte()) {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (BUSY)" + " " + DanaRS_Packet.toHexString(decryptedBuffer))
mSendQueue.clear()
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED, resourceHelper.gs(R.string.pumpbusy)))
} else {
// ERROR in response, wrong serial number
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (ERROR)" + " " + DanaRS_Packet.toHexString(decryptedBuffer))
mSendQueue.clear()
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED, resourceHelper.gs(R.string.connectionerror)))
sp.remove(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName)
val n = Notification(Notification.WRONGSERIALNUMBER, resourceHelper.gs(R.string.wrongpassword), Notification.URGENT)
rxBus.send(EventNewNotification(n))
}
BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY.toByte() -> { BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION.toByte() -> {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(decryptedBuffer)) aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__TIME_INFORMATION " + /*message.getMessageName() + " " + */DanaRS_Packet.toHexString(decryptedBuffer))
if (decryptedBuffer[2] == 0x00.toByte()) { if (v3Encryption) {
// Paring is not requested, sending time info // decryptedBuffer[2] : 0x00 OK 0x01 Error, No pairing
if (decryptedBuffer[2] == 0x00.toByte()) {
val randomPairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_v3_randompairingkey) + danaRSPlugin.mDeviceName, "")
val pairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_v3_pairingkey) + danaRSPlugin.mDeviceName, "")
if (randomPairingKey.isNotEmpty() && pairingKey.isNotEmpty()) {
// expecting successful connect
isConnected = true
isConnecting = false
aapsLogger.debug(LTag.PUMPBTCOMM, "Connect !!")
} else {
context.startActivity(Intent(context, EnterPinActivity::class.java).also { it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) })
aapsLogger.debug(LTag.PUMPBTCOMM, "Request pairing keys !!")
}
} else {
sendV3PairingInformation(1)
}
} else {
val size = decryptedBuffer.size
var pass: Int = (decryptedBuffer[size - 1].toInt() and 0x000000FF shl 8) + (decryptedBuffer[size - 2].toInt() and 0x000000FF)
pass = pass xor 3463
danaRPump.rsPassword = Integer.toHexString(pass)
aapsLogger.debug(LTag.PUMPBTCOMM, "Pump user password: " + Integer.toHexString(pass))
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTED))
isConnected = true
isConnecting = false
aapsLogger.debug(LTag.PUMPBTCOMM, "RS connected and status read")
}
}
BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__CHECK_PASSKEY.toByte() -> {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__CHECK_PASSKEY" + " " + DanaRS_Packet.toHexString(decryptedBuffer))
if (decryptedBuffer[2] == 0x00.toByte()) {
// Paring is not requested, sending time info
sendTimeInfo()
} else {
// Pairing on pump is requested
sendPairingRequest()
}
}
BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST.toByte() -> {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PASSKEY_REQUEST " + DanaRS_Packet.toHexString(decryptedBuffer))
if (decryptedBuffer[2] != 0x00.toByte()) {
disconnect("passkey request failed")
}
}
BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_RETURN.toByte() -> {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PASSKEY_RETURN " + DanaRS_Packet.toHexString(decryptedBuffer))
// Paring is successful, sending time info
rxBus.send(EventDanaRSPairingSuccess())
sendTimeInfo() sendTimeInfo()
} else { val pairingKey = byteArrayOf(decryptedBuffer[2], decryptedBuffer[3])
// Pairing on pump is requested // store pairing key to preferences
sendPairingRequest() sp.putString(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName, DanaRS_Packet.bytesToHex(pairingKey))
aapsLogger.debug(LTag.PUMPBTCOMM, "Got pairing key: " + DanaRS_Packet.bytesToHex(pairingKey))
}
BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__GET_PUMP_CHECK.toByte() -> {
if (decryptedBuffer[2] == 0x05.toByte()) {
// not easy mode, request time info
val bytes: ByteArray = bleEncryption.getEncryptedPacket(BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION, null, null)
writeCharacteristicNoResponse(uartWriteBTGattChar, bytes)
} else {
// easy mode
val bytes: ByteArray = bleEncryption.getEncryptedPacket(BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__GET_EASYMENU_CHECK, null, null)
writeCharacteristicNoResponse(uartWriteBTGattChar, bytes)
}
}
BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__GET_EASYMENU_CHECK.toByte() -> {
isEasyMode = decryptedBuffer[2] == 0x01.toByte()
isUnitUD = decryptedBuffer[3] == 0x01.toByte()
// request time information
if (v3Encryption) {
sendV3PairingInformation(1)
} else {
val bytes: ByteArray = bleEncryption.getEncryptedPacket(BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION, null, null)
writeCharacteristicNoResponse(uartWriteBTGattChar, bytes)
}
} }
} }
BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST.toByte() -> {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PASSKEY_REQUEST " + DanaRS_Packet.toHexString(decryptedBuffer))
if (decryptedBuffer[2] != 0x00.toByte()) {
disconnect("passkey request failed")
}
}
BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_RETURN.toByte() -> {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PASSKEY_RETURN " + DanaRS_Packet.toHexString(decryptedBuffer))
// Paring is successful, sending time info
rxBus.send(EventDanaRSPairingSuccess())
sendTimeInfo()
val pairingKey = byteArrayOf(decryptedBuffer[2], decryptedBuffer[3])
// store pairing key to preferences
sp.putString(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName, DanaRS_Packet.bytesToHex(pairingKey))
aapsLogger.debug(LTag.PUMPBTCOMM, "Got pairing key: " + DanaRS_Packet.bytesToHex(pairingKey))
}
BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION.toByte() -> {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__TIME_INFORMATION " + /*message.getMessageName() + " " + */DanaRS_Packet.toHexString(decryptedBuffer))
val size = decryptedBuffer.size
var pass: Int = (decryptedBuffer[size - 1].toInt() and 0x000000FF shl 8) + (decryptedBuffer[size - 2].toInt() and 0x000000FF)
pass = pass xor 3463
danaRPump.rsPassword = Integer.toHexString(pass)
aapsLogger.debug(LTag.PUMPBTCOMM, "Pump user password: " + Integer.toHexString(pass))
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTED))
isConnected = true
isConnecting = false
aapsLogger.debug(LTag.PUMPBTCOMM, "RS connected and status read")
}
}
else -> { else -> {
// Retrieve message code from received buffer and last message sent // Retrieve message code from received buffer and last message sent
val originalCommand = processedMessage?.command ?: 0xFFFF val originalCommand = processedMessage?.command ?: 0xFFFF
@ -449,6 +562,8 @@ class BLEComm @Inject internal constructor(
val params = message.requestParams val params = message.requestParams
aapsLogger.debug(LTag.PUMPBTCOMM, ">>>>> " + message.friendlyName + " " + DanaRS_Packet.toHexString(command) + " " + DanaRS_Packet.toHexString(params)) aapsLogger.debug(LTag.PUMPBTCOMM, ">>>>> " + message.friendlyName + " " + DanaRS_Packet.toHexString(command) + " " + DanaRS_Packet.toHexString(params))
var bytes = bleEncryption.getEncryptedPacket(message.opCode, params, null) var bytes = bleEncryption.getEncryptedPacket(message.opCode, params, null)
if (v3Encryption)
bytes = bleEncryption.encryptSecondLevelPacket(bytes)
// If there is another message not completely sent, add to queue only // If there is another message not completely sent, add to queue only
if (mSendQueue.size > 0) { if (mSendQueue.size > 0) {
// Split to parts per 20 bytes max // Split to parts per 20 bytes max
@ -509,13 +624,30 @@ class BLEComm @Inject internal constructor(
} }
} }
fun finishV3Pairing() {
val randomPairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_v3_randompairingkey) + danaRSPlugin.mDeviceName, "")
val pairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_v3_pairingkey) + danaRSPlugin.mDeviceName, "")
if (randomPairingKey.isNotEmpty() && pairingKey.isNotEmpty()) {
val tPairingKey = Base64.decode(pairingKey, Base64.DEFAULT)
val tRandomPairingKey = Base64.decode(randomPairingKey, Base64.DEFAULT)
val tRandomSyncKey: Byte = 0
bleEncryption.setPairingKeys(tPairingKey, tRandomPairingKey, tRandomSyncKey)
sendV3PairingInformation(0)
}
}
// 0x00 Start encryption, 0x01 Request pairing
private fun sendV3PairingInformation(requestNewPairing: Int) {
val params = byteArrayOf(requestNewPairing.toByte())
val bytes: ByteArray = bleEncryption.getEncryptedPacket(BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION, params, null)
aapsLogger.debug(LTag.PUMPBTCOMM, ">>>>> " + "ENCRYPTION__TIME_INFORMATION" + " " + DanaRS_Packet.toHexString(bytes))
writeCharacteristicNoResponse(uartWriteBTGattChar, bytes)
}
private fun sendPairingRequest() { private fun sendPairingRequest() {
// Start activity which is waiting 20sec // Start activity which is waiting 20sec
// On pump pairing request is displayed and is waiting for conformation // On pump pairing request is displayed and is waiting for conformation
val i = Intent() context.startActivity(Intent(context, PairingHelperActivity::class.java).also { it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) })
i.setClass(context, PairingHelperActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(i)
val bytes = bleEncryption.getEncryptedPacket(BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST, null, null) val bytes = bleEncryption.getEncryptedPacket(BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__PASSKEY_REQUEST, null, null)
aapsLogger.debug(LTag.PUMPBTCOMM, ">>>>> " + "ENCRYPTION__PASSKEY_REQUEST" + " " + DanaRS_Packet.toHexString(bytes)) aapsLogger.debug(LTag.PUMPBTCOMM, ">>>>> " + "ENCRYPTION__PASSKEY_REQUEST" + " " + DanaRS_Packet.toHexString(bytes))
writeCharacteristicNoResponse(uartWriteBTGattChar, bytes) writeCharacteristicNoResponse(uartWriteBTGattChar, bytes)

View file

@ -0,0 +1,32 @@
package info.nightscout.androidaps.utils.extensions
private val HEX_CHARS = "0123456789ABCDEF".toCharArray()
fun ByteArray.toHex() : String{
val result = StringBuffer()
forEach {
val octet = it.toInt()
val firstIndex = (octet and 0xF0).ushr(4)
val secondIndex = octet and 0x0F
result.append(HEX_CHARS[firstIndex])
result.append(HEX_CHARS[secondIndex])
}
return result.toString()
}
fun String.hexStringToByteArray() : ByteArray {
val result = ByteArray(length / 2)
for (i in 0 until length step 2) {
val firstIndex = HEX_CHARS.indexOf(this[i]);
val secondIndex = HEX_CHARS.indexOf(this[i + 1]);
val octet = firstIndex.shl(4).or(secondIndex)
result.set(i.shr(1), octet.toByte())
}
return result
}

View file

@ -15,6 +15,9 @@ class DefaultEditTextValidator : EditTextValidator {
protected var testErrorString: String? = null protected var testErrorString: String? = null
protected var emptyAllowed = false protected var emptyAllowed = false
protected lateinit var editTextView: EditText protected lateinit var editTextView: EditText
private var tw: TextWatcher? = null
private var defaultEmptyErrorString: String? = null
protected var testType: Int protected var testType: Int
protected var classType: String? = null protected var classType: String? = null
protected var customRegexp: String? = null protected var customRegexp: String? = null
@ -26,8 +29,6 @@ class DefaultEditTextValidator : EditTextValidator {
protected var maxNumber = 0 protected var maxNumber = 0
protected var floatminNumber = 0f protected var floatminNumber = 0f
protected var floatmaxNumber = 0f protected var floatmaxNumber = 0f
private var tw: TextWatcher? = null
private var defaultEmptyErrorString: String? = null
@Suppress("unused") @Suppress("unused")
constructor(editTextView: EditText, context: Context) { constructor(editTextView: EditText, context: Context) {
@ -167,44 +168,50 @@ class DefaultEditTextValidator : EditTextValidator {
} }
@Suppress("unused") @Suppress("unused")
fun setClassType(classType: String?, testErrorString: String?, context: Context) { fun setClassType(classType: String?, testErrorString: String?, context: Context) : DefaultEditTextValidator{
testType = EditTextValidator.TEST_CUSTOM testType = EditTextValidator.TEST_CUSTOM
this.classType = classType this.classType = classType
this.testErrorString = testErrorString this.testErrorString = testErrorString
resetValidators(context) resetValidators(context)
return this
} }
@Suppress("unused") @Suppress("unused")
fun setCustomRegexp(customRegexp: String?, context: Context) { fun setCustomRegexp(customRegexp: String?, context: Context) : DefaultEditTextValidator {
testType = EditTextValidator.TEST_REGEXP testType = EditTextValidator.TEST_REGEXP
this.customRegexp = customRegexp this.customRegexp = customRegexp
resetValidators(context) resetValidators(context)
return this
} }
@Suppress("unused") @Suppress("unused")
fun setEmptyAllowed(emptyAllowed: Boolean, context: Context) { fun setEmptyAllowed(emptyAllowed: Boolean, context: Context) : DefaultEditTextValidator {
this.emptyAllowed = emptyAllowed this.emptyAllowed = emptyAllowed
resetValidators(context) resetValidators(context)
return this
} }
fun setEmptyErrorString(emptyErrorString: String?) { fun setEmptyErrorString(emptyErrorString: String?) : DefaultEditTextValidator {
emptyErrorStringActual = if (!TextUtils.isEmpty(emptyErrorString)) { emptyErrorStringActual = if (!TextUtils.isEmpty(emptyErrorString)) {
emptyErrorString emptyErrorString
} else { } else {
defaultEmptyErrorString defaultEmptyErrorString
} }
return this
} }
@Suppress("unused") @Suppress("unused")
fun setTestErrorString(testErrorString: String?, context: Context) { fun setTestErrorString(testErrorString: String?, context: Context) : DefaultEditTextValidator {
this.testErrorString = testErrorString this.testErrorString = testErrorString
resetValidators(context) resetValidators(context)
return this
} }
@Suppress("unused") @Suppress("unused")
fun setTestType(testType: Int, context: Context) { fun setTestType(testType: Int, context: Context) : DefaultEditTextValidator {
this.testType = testType this.testType = testType
resetValidators(context) resetValidators(context)
return this
} }
override fun testValidity(): Boolean { override fun testValidity(): Boolean {

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.utils.textValidator package info.nightscout.androidaps.utils.textValidator
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import androidx.core.content.res.TypedArrayUtils import androidx.core.content.res.TypedArrayUtils
@ -22,7 +23,7 @@ class ValidatingEditTextPreference(ctx: Context, attrs: AttributeSet, defStyleAt
constructor(ctx: Context, attrs: AttributeSet, defStyle: Int) constructor(ctx: Context, attrs: AttributeSet, defStyle: Int)
: this(ctx, attrs, defStyle, 0) : this(ctx, attrs, defStyle, 0)
constructor(ctx: Context, attrs: AttributeSet) @SuppressLint("RestrictedApi") constructor(ctx: Context, attrs: AttributeSet)
: this(ctx, attrs, TypedArrayUtils.getAttr(ctx, R.attr.editTextPreferenceStyle, : this(ctx, attrs, TypedArrayUtils.getAttr(ctx, R.attr.editTextPreferenceStyle,
R.attr.editTextPreferenceStyle)) R.attr.editTextPreferenceStyle))

View file

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".plugins.pump.danaRS.activities.EnterPinActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@color/activity_title_background"
android:orientation="horizontal"
android:padding="5dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/pairing"
android:src="@drawable/ic_bluetooth_white_48dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:text="@string/pairing"
android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:gravity="center_horizontal"
android:labelFor="@+id/rs_v3_pin1"
android:text="@string/num1pin"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<EditText
android:id="@+id/rs_v3_pin1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autofillHints="@string/pin1"
android:digits="0123456789ABCDEF"
android:gravity="center_horizontal"
android:inputType="textCapCharacters"
android:text=""
android:textSize="25sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:gravity="center_horizontal"
android:labelFor="@+id/rs_v3_pin2"
android:text="@string/num2pin"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<EditText
android:id="@+id/rs_v3_pin2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autofillHints="@string/pin2"
android:digits="0123456789ABCDEF"
android:gravity="center_horizontal"
android:inputType="textCapCharacters"
android:text=""
android:textSize="25sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="20dp"
android:gravity="center_horizontal"
android:text="@string/press_ok_on_the_pump"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<include layout="@layout/okcancel" />
</LinearLayout>

View file

@ -417,6 +417,7 @@
<string name="open_settings_on_wear">Open Settings on Wear</string> <string name="open_settings_on_wear">Open Settings on Wear</string>
<string name="pumperror">Pump Error</string> <string name="pumperror">Pump Error</string>
<string name="lowbattery">Low Battery</string> <string name="lowbattery">Low Battery</string>
<string name="basalcompare">Delivering less than preset basal rate</string>
<string name="pumpshutdown">Pump Shutdown</string> <string name="pumpshutdown">Pump Shutdown</string>
<string name="batterydischarged">Pump Battery Discharged</string> <string name="batterydischarged">Pump Battery Discharged</string>
<string name="danarkoreanpump">DanaR Korean</string> <string name="danarkoreanpump">DanaR Korean</string>
@ -731,12 +732,16 @@
<string name="pairingtimedout">Pairing timed out</string> <string name="pairingtimedout">Pairing timed out</string>
<string name="pairing">Pairing</string> <string name="pairing">Pairing</string>
<string name="key_danars_pairingkey" translatable="false">danars_pairing_key_</string> <string name="key_danars_pairingkey" translatable="false">danars_pairing_key_</string>
<string name="key_danars_v3_randompairingkey" translatable="false">danars_v3_randompairing_key_</string>
<string name="key_danars_v3_pairingkey" translatable="false">danars_v3_pairing_key_</string>
<string name="key_danars_v3_randomsynckey" translatable="false">danars_v3_randomsync_key_</string>
<string name="key_danars_address" translatable="false">danars_address</string> <string name="key_danars_address" translatable="false">danars_address</string>
<string name="key_danars_name" translatable="false">danars_name</string> <string name="key_danars_name" translatable="false">danars_name</string>
<string name="danars_nodeviceavailable">No device found so far</string> <string name="danars_nodeviceavailable">No device found so far</string>
<string name="emptyreservoir">Empty reservoir</string> <string name="emptyreservoir">Empty reservoir</string>
<string name="bloodsugarmeasurementalert">Blood sugar measurement alert</string> <string name="bloodsugarmeasurementalert">Blood sugar measurement alert</string>
<string name="remaininsulinalert">Remaining insulin level</string> <string name="remaininsulinalert">Remaining insulin level</string>
<string name="missedbolus">Missed bolus</string>
<string name="danarspump">DanaRS</string> <string name="danarspump">DanaRS</string>
<string name="danarspump_shortname">Dana</string> <string name="danarspump_shortname">Dana</string>
<string name="selectedpump">Selected pump</string> <string name="selectedpump">Selected pump</string>
@ -1736,4 +1741,11 @@
<string name="smscommunicator_otp_install_info">On each follower phone install Authenticator app that support RFC 6238 TOTP tokens. Popular free apps are:\n • Authy\n • Google Authenticator\n • LastPass Authenticator\n • FreeOTP Authenticator</string> <string name="smscommunicator_otp_install_info">On each follower phone install Authenticator app that support RFC 6238 TOTP tokens. Popular free apps are:\n • Authy\n • Google Authenticator\n • LastPass Authenticator\n • FreeOTP Authenticator</string>
<string name="smscommunicator_otp_provisioning_warning">DO NOT SHARE this code online!\nUse it only to setup Authenticator App on follower phones.</string> <string name="smscommunicator_otp_provisioning_warning">DO NOT SHARE this code online!\nUse it only to setup Authenticator App on follower phones.</string>
<string name="smscommunicator_otp_reset_warning">By reseting authenticator you make all already provisioned authenticators invalid. You will need to set up them again!</string> <string name="smscommunicator_otp_reset_warning">By reseting authenticator you make all already provisioned authenticators invalid. You will need to set up them again!</string>
<string name="pin1">PIN1</string>
<string name="pin2">PIN2</string>
<string name="press_ok_on_the_pump">Press OK on the pump\nand enter 2 displayed numbers\nKeep display on pump ON by pressing minus button until you finish entering code.</string>
<string name="num1pin">1: (12 digits)</string>
<string name="num2pin">2: (8 digits)</string>
<string name="resetpairing">Reset pairing information?</string>
<string name="invalidpairing">Invalid pairing information. Requesting new pairing</string>
</resources> </resources>

View file

@ -16,9 +16,13 @@
<string name="error_date_not_valid">Format not valid</string> <string name="error_date_not_valid">Format not valid</string>
<string name="error_mustbe4digitnumber">Must be 4 digit number</string> <string name="error_mustbe4digitnumber">Must be 4 digit number</string>
<string name="error_mustbe6digitnumber">Must be 6 digit number</string> <string name="error_mustbe6digitnumber">Must be 6 digit number</string>
<string name="error_mustbe12hexadidits">Must be 12 characters of ABCDEF0123456789</string>
<string name="error_mustbe8hexadidits">Must be 8 characters of ABCDEF0123456789</string>
<string name="error_not_a_minimum_length">Not a minimum length</string> <string name="error_not_a_minimum_length">Not a minimum length</string>
<string name="error_pin_not_valid">Pin should be 3 to 6 digits, not same or in series</string> <string name="error_pin_not_valid">Pin should be 3 to 6 digits, not same or in series</string>
<string name="fourdigitnumber" translatable="false">^\\d{4}</string> <string name="fourdigitnumber" translatable="false">^\\d{4}</string>
<string name="twelvehexanumber" translatable="false">^[A-F0-9]{12}$</string>
<string name="eighthexanumber" translatable="false">^[A-F0-9]{8}$</string>
</resources> </resources>

View file

@ -14,7 +14,7 @@ import org.powermock.modules.junit4.PowerMockRunner
class DanaRS_Packet_Notify_AlarmTest : DanaRSTestBase() { class DanaRS_Packet_Notify_AlarmTest : DanaRSTestBase() {
@Test fun runTest() { @Test fun runTest() {
val packet = DanaRS_Packet_Notify_Alarm(aapsLogger, resourceHelper) val packet = DanaRS_Packet_Notify_Alarm(aapsLogger, resourceHelper, rxBus)
// test params // test params
Assert.assertEquals(null, packet.requestParams) Assert.assertEquals(null, packet.requestParams)
// test message decoding // test message decoding