From cdd5f6dd0dc376032d3ea126274d9ff0cbb37b7f Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt <git@jotomo.de> Date: Tue, 27 Jun 2017 16:07:42 +0200 Subject: [PATCH] Ensure wake event is finished in all cases. --- .../service/MedtronicCnlIntentService.java | 357 +++++++++--------- 1 file changed, 177 insertions(+), 180 deletions(-) diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java index 9b46b76..e897ef8 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java +++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java @@ -104,152 +104,148 @@ public class MedtronicCnlIntentService extends IntentService { protected void onHandleIntent(Intent intent) { Log.d(TAG, "onHandleIntent called"); + try { - long timePollStarted = System.currentTimeMillis(), - timePollExpected = timePollStarted, - timeLastGoodSGV = dataStore.getLastPumpStatus().getEventDate().getTime(); + long timePollStarted = System.currentTimeMillis(), + timePollExpected = timePollStarted, + timeLastGoodSGV = dataStore.getLastPumpStatus().getEventDate().getTime(); - short pumpBatteryLevel = dataStore.getLastPumpStatus().getBatteryPercentage(); + short pumpBatteryLevel = dataStore.getLastPumpStatus().getBatteryPercentage(); - if (timeLastGoodSGV != 0) { - timePollExpected = timeLastGoodSGV + POLL_PERIOD_MS + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((timePollStarted - 1000L - (timeLastGoodSGV + POLL_GRACE_PERIOD_MS)) / POLL_PERIOD_MS)); - } + if (timeLastGoodSGV != 0) { + timePollExpected = timeLastGoodSGV + POLL_PERIOD_MS + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((timePollStarted - 1000L - (timeLastGoodSGV + POLL_GRACE_PERIOD_MS)) / POLL_PERIOD_MS)); + } - // avoid polling when too close to sensor-pump comms - if (((timePollExpected - timePollStarted) > 5000L) && ((timePollExpected - timePollStarted) < (POLL_GRACE_PERIOD_MS + 45000L))) { - sendStatus("Please wait: Poll due in " + ((timePollExpected - timePollStarted) / 1000L) + " seconds"); - MedtronicCnlAlarmManager.setAlarm(timePollExpected); - MedtronicCnlAlarmReceiver.completeWakefulIntent(intent); - return; - } + // avoid polling when too close to sensor-pump comms + if (((timePollExpected - timePollStarted) > 5000L) && ((timePollExpected - timePollStarted) < (POLL_GRACE_PERIOD_MS + 45000L))) { + sendStatus("Please wait: Poll due in " + ((timePollExpected - timePollStarted) / 1000L) + " seconds"); + MedtronicCnlAlarmManager.setAlarm(timePollExpected); + return; + } - long pollInterval = configurationStore.getPollInterval(); - if ((pumpBatteryLevel > 0) && (pumpBatteryLevel <= 25)) { - pollInterval = configurationStore.getLowBatteryPollInterval(); - } + long pollInterval = configurationStore.getPollInterval(); + if ((pumpBatteryLevel > 0) && (pumpBatteryLevel <= 25)) { + pollInterval = configurationStore.getLowBatteryPollInterval(); + } - if (!hasUsbHostFeature()) { - sendStatus("It appears that this device doesn't support USB OTG."); - Log.e(TAG, "Device does not support USB OTG"); - MedtronicCnlAlarmReceiver.completeWakefulIntent(intent); - // TODO - throw, don't return - return; - } + if (!hasUsbHostFeature()) { + sendStatus("It appears that this device doesn't support USB OTG."); + Log.e(TAG, "Device does not support USB OTG"); + // TODO - throw, don't return + return; + } - UsbDevice cnlStick = UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID); - if (cnlStick == null) { - sendStatus("USB connection error. Is the Contour Next Link plugged in?"); - Log.w(TAG, "USB connection error. Is the CNL plugged in?"); + UsbDevice cnlStick = UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID); + if (cnlStick == null) { + sendStatus("USB connection error. Is the Contour Next Link plugged in?"); + Log.w(TAG, "USB connection error. Is the CNL plugged in?"); - // TODO - set status if offline or Nightscout not reachable - uploadToNightscout(); - MedtronicCnlAlarmReceiver.completeWakefulIntent(intent); - // TODO - throw, don't return - return; - } + // TODO - set status if offline or Nightscout not reachable + uploadToNightscout(); + // TODO - throw, don't return + return; + } - if (!mUsbManager.hasPermission(UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID))) { - sendMessage(Constants.ACTION_NO_USB_PERMISSION); - MedtronicCnlAlarmReceiver.completeWakefulIntent(intent); - // TODO - throw, don't return - return; - } - mHidDevice = UsbHidDriver.acquire(mUsbManager, cnlStick); + if (!mUsbManager.hasPermission(UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID))) { + sendMessage(Constants.ACTION_NO_USB_PERMISSION); + // TODO - throw, don't return + return; + } + mHidDevice = UsbHidDriver.acquire(mUsbManager, cnlStick); - try { - mHidDevice.open(); - } catch (Exception e) { - Log.e(TAG, "Unable to open serial device", e); - MedtronicCnlAlarmReceiver.completeWakefulIntent(intent); - // TODO - throw, don't return - return; - } + try { + mHidDevice.open(); + } catch (Exception e) { + Log.e(TAG, "Unable to open serial device", e); + // TODO - throw, don't return + return; + } - DateFormat df = new SimpleDateFormat("HH:mm:ss", Locale.US); + DateFormat df = new SimpleDateFormat("HH:mm:ss", Locale.US); - MedtronicCnlReader cnlReader = new MedtronicCnlReader(mHidDevice); + MedtronicCnlReader cnlReader = new MedtronicCnlReader(mHidDevice); - Realm realm = Realm.getDefaultInstance(); - realm.beginTransaction(); + Realm realm = Realm.getDefaultInstance(); + realm.beginTransaction(); - try { - sendStatus("Connecting to Contour Next Link"); - Log.d(TAG, "Connecting to Contour Next Link"); - cnlReader.requestDeviceInfo(); - - // Is the device already configured? - ContourNextLinkInfo info = realm - .where(ContourNextLinkInfo.class) - .equalTo("serialNumber", cnlReader.getStickSerial()) - .findFirst(); - - if (info == null) { - info = realm.createObject(ContourNextLinkInfo.class, cnlReader.getStickSerial()); - } + try { + sendStatus("Connecting to Contour Next Link"); + Log.d(TAG, "Connecting to Contour Next Link"); + cnlReader.requestDeviceInfo(); + + // Is the device already configured? + ContourNextLinkInfo info = realm + .where(ContourNextLinkInfo.class) + .equalTo("serialNumber", cnlReader.getStickSerial()) + .findFirst(); - cnlReader.getPumpSession().setStickSerial(info.getSerialNumber()); + if (info == null) { + info = realm.createObject(ContourNextLinkInfo.class, cnlReader.getStickSerial()); + } - cnlReader.enterControlMode(); + cnlReader.getPumpSession().setStickSerial(info.getSerialNumber()); - try { - cnlReader.enterPassthroughMode(); - cnlReader.openConnection(); + cnlReader.enterControlMode(); - cnlReader.requestReadInfo(); + try { + cnlReader.enterPassthroughMode(); + cnlReader.openConnection(); - String key = info.getKey(); + cnlReader.requestReadInfo(); - if (key == null) { - cnlReader.requestLinkKey(); + String key = info.getKey(); - info.setKey(MessageUtils.byteArrayToHexString(cnlReader.getPumpSession().getKey())); - key = info.getKey(); - } + if (key == null) { + cnlReader.requestLinkKey(); - cnlReader.getPumpSession().setKey(MessageUtils.hexStringToByteArray(key)); + info.setKey(MessageUtils.byteArrayToHexString(cnlReader.getPumpSession().getKey())); + key = info.getKey(); + } - long pumpMAC = cnlReader.getPumpSession().getPumpMAC(); - Log.i(TAG, "PumpInfo MAC: " + (pumpMAC & 0xffffff)); - PumpInfo activePump = realm - .where(PumpInfo.class) - .equalTo("pumpMac", pumpMAC) - .findFirst(); + cnlReader.getPumpSession().setKey(MessageUtils.hexStringToByteArray(key)); - if (activePump == null) { - activePump = realm.createObject(PumpInfo.class, pumpMAC); - } + long pumpMAC = cnlReader.getPumpSession().getPumpMAC(); + Log.i(TAG, "PumpInfo MAC: " + (pumpMAC & 0xffffff)); + PumpInfo activePump = realm + .where(PumpInfo.class) + .equalTo("pumpMac", pumpMAC) + .findFirst(); - activePump.updateLastQueryTS(); + if (activePump == null) { + activePump = realm.createObject(PumpInfo.class, pumpMAC); + } + + activePump.updateLastQueryTS(); - byte radioChannel = cnlReader.negotiateChannel(activePump.getLastRadioChannel()); - if (radioChannel == 0) { - sendStatus("Could not communicate with the pump. Is it nearby?"); - Log.i(TAG, "Could not communicate with the pump. Is it nearby?"); - pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available - } else { - dataStore.setActivePumpMac(pumpMAC); + byte radioChannel = cnlReader.negotiateChannel(activePump.getLastRadioChannel()); + if (radioChannel == 0) { + sendStatus("Could not communicate with the pump. Is it nearby?"); + Log.i(TAG, "Could not communicate with the pump. Is it nearby?"); + pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available + } else { + dataStore.setActivePumpMac(pumpMAC); - activePump.setLastRadioChannel(radioChannel); - sendStatus(String.format(Locale.getDefault(), "Connected on channel %d RSSI: %d%%", (int) radioChannel, cnlReader.getPumpSession().getRadioRSSIpercentage())); - Log.d(TAG, String.format("Connected to Contour Next Link on channel %d.", (int) radioChannel)); + activePump.setLastRadioChannel(radioChannel); + sendStatus(String.format(Locale.getDefault(), "Connected on channel %d RSSI: %d%%", (int) radioChannel, cnlReader.getPumpSession().getRadioRSSIpercentage())); + Log.d(TAG, String.format("Connected to Contour Next Link on channel %d.", (int) radioChannel)); - // read pump status - PumpStatusEvent pumpRecord = realm.createObject(PumpStatusEvent.class); + // read pump status + PumpStatusEvent pumpRecord = realm.createObject(PumpStatusEvent.class); - String deviceName = String.format("medtronic-600://%s", cnlReader.getStickSerial()); - activePump.setDeviceName(deviceName); + String deviceName = String.format("medtronic-600://%s", cnlReader.getStickSerial()); + activePump.setDeviceName(deviceName); - // TODO - this should not be necessary. We should reverse lookup the device name from PumpInfo - pumpRecord.setDeviceName(deviceName); + // TODO - this should not be necessary. We should reverse lookup the device name from PumpInfo + pumpRecord.setDeviceName(deviceName); - long pumpTime = cnlReader.getPumpTime().getTime(); - long pumpOffset = pumpTime - System.currentTimeMillis(); - Log.d(TAG, "Time offset between pump and device: " + pumpOffset + " millis."); + long pumpTime = cnlReader.getPumpTime().getTime(); + long pumpOffset = pumpTime - System.currentTimeMillis(); + Log.d(TAG, "Time offset between pump and device: " + pumpOffset + " millis."); - // TODO - send ACTION to MainActivity to show offset between pump and uploader. - pumpRecord.setPumpTimeOffset(pumpOffset); - pumpRecord.setPumpDate(new Date(pumpTime - pumpOffset)); - cnlReader.updatePumpStatus(pumpRecord); + // TODO - send ACTION to MainActivity to show offset between pump and uploader. + pumpRecord.setPumpTimeOffset(pumpOffset); + pumpRecord.setPumpDate(new Date(pumpTime - pumpOffset)); + cnlReader.updatePumpStatus(pumpRecord); if (pumpRecord.getSgv() != 0) { String offsetSign = ""; @@ -290,81 +286,82 @@ public class MedtronicCnlIntentService extends IntentService { dataStore.incUnavailableSGVCount(); // poll clash detection } - realm.commitTransaction(); - // Tell the Main Activity we have new data - sendMessage(Constants.ACTION_UPDATE_PUMP); - } + realm.commitTransaction(); + // Tell the Main Activity we have new data + sendMessage(Constants.ACTION_UPDATE_PUMP); + } - } catch (UnexpectedMessageException e) { - Log.e(TAG, "Unexpected Message", e); - sendStatus("Communication Error: " + e.getMessage()); - pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); + } catch (UnexpectedMessageException e) { + Log.e(TAG, "Unexpected Message", e); + sendStatus("Communication Error: " + e.getMessage()); + pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); + } catch (TimeoutException e) { + Log.e(TAG, "Timeout communicating with the Contour Next Link.", e); + sendStatus("Timeout communicating with the Contour Next Link."); + pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); + } catch (NoSuchAlgorithmException e) { + Log.e(TAG, "Could not determine CNL HMAC", e); + sendStatus("Error connecting to Contour Next Link: Hashing error."); + } finally { + try { + cnlReader.closeConnection(); + cnlReader.endPassthroughMode(); + cnlReader.endControlMode(); + } catch (NoSuchAlgorithmException e) { + } + + } + } catch (IOException e) { + Log.e(TAG, "Error connecting to Contour Next Link.", e); + sendStatus("Error connecting to Contour Next Link."); + } catch (ChecksumException e) { + Log.e(TAG, "Checksum error getting message from the Contour Next Link.", e); + sendStatus("Checksum error getting message from the Contour Next Link."); + } catch (EncryptionException e) { + Log.e(TAG, "Error decrypting messages from Contour Next Link.", e); + sendStatus("Error decrypting messages from Contour Next Link."); } catch (TimeoutException e) { Log.e(TAG, "Timeout communicating with the Contour Next Link.", e); sendStatus("Timeout communicating with the Contour Next Link."); - pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); - } catch (NoSuchAlgorithmException e) { - Log.e(TAG, "Could not determine CNL HMAC", e); - sendStatus("Error connecting to Contour Next Link: Hashing error."); + } catch (UnexpectedMessageException e) { + Log.e(TAG, "Could not close connection.", e); + sendStatus("Could not close connection: " + e.getMessage()); } finally { - try { - cnlReader.closeConnection(); - cnlReader.endPassthroughMode(); - cnlReader.endControlMode(); - } catch (NoSuchAlgorithmException e) { + if (!realm.isClosed()) { + if (realm.isInTransaction()) { + // If we didn't commit the transaction, we've run into an error. Let's roll it back + realm.cancelTransaction(); + } + realm.close(); } - - } - } catch (IOException e) { - Log.e(TAG, "Error connecting to Contour Next Link.", e); - sendStatus("Error connecting to Contour Next Link."); - } catch (ChecksumException e) { - Log.e(TAG, "Checksum error getting message from the Contour Next Link.", e); - sendStatus("Checksum error getting message from the Contour Next Link."); - } catch (EncryptionException e) { - Log.e(TAG, "Error decrypting messages from Contour Next Link.", e); - sendStatus("Error decrypting messages from Contour Next Link."); - } catch (TimeoutException e) { - Log.e(TAG, "Timeout communicating with the Contour Next Link.", e); - sendStatus("Timeout communicating with the Contour Next Link."); - } catch (UnexpectedMessageException e) { - Log.e(TAG, "Could not close connection.", e); - sendStatus("Could not close connection: " + e.getMessage()); - } finally { - if (!realm.isClosed()) { - if (realm.isInTransaction()) { - // If we didn't commit the transaction, we've run into an error. Let's roll it back - realm.cancelTransaction(); + // TODO - set status if offline or Nightscout not reachable + sendToXDrip(); + uploadToNightscout(); + + // smart polling and pump-sensor poll clash detection + long lastActualPollTime = timePollStarted; + if (timeLastGoodSGV > 0) { + lastActualPollTime = timeLastGoodSGV + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((System.currentTimeMillis() - (timeLastGoodSGV + POLL_GRACE_PERIOD_MS)) / POLL_PERIOD_MS)); } - realm.close(); - } - // TODO - set status if offline or Nightscout not reachable - sendToXDrip(); - uploadToNightscout(); - - // smart polling and pump-sensor poll clash detection - long lastActualPollTime = timePollStarted; - if (timeLastGoodSGV > 0) { - lastActualPollTime = timeLastGoodSGV + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((System.currentTimeMillis() - (timeLastGoodSGV + POLL_GRACE_PERIOD_MS)) / POLL_PERIOD_MS)); - } - long nextActualPollTime = lastActualPollTime + POLL_PERIOD_MS; - long nextRequestedPollTime = lastActualPollTime + pollInterval; - if ((nextRequestedPollTime - System.currentTimeMillis()) < 10000L) { - nextRequestedPollTime = nextActualPollTime; - } - // extended unavailable SGV may be due to clash with the current polling time - // while we wait for a good SGV event, polling is auto adjusted by offsetting the next poll based on miss count - if (dataStore.getUnavailableSGVCount() > 0) { - if (timeLastGoodSGV == 0) { - nextRequestedPollTime += POLL_PERIOD_MS / 5L; // if there is a uploader/sensor poll clash on startup then this will push the next attempt out by 60 seconds - } else if (dataStore.getUnavailableSGVCount() > 2) { - sendStatus("Warning: No SGV available from pump for " + dataStore.getUnavailableSGVCount() + " attempts"); - nextRequestedPollTime += ((long) ((dataStore.getUnavailableSGVCount() - 2) % 5)) * (POLL_PERIOD_MS / 10L); // adjust poll time in 1/10 steps to avoid potential poll clash (max adjustment at 5/10) + long nextActualPollTime = lastActualPollTime + POLL_PERIOD_MS; + long nextRequestedPollTime = lastActualPollTime + pollInterval; + if ((nextRequestedPollTime - System.currentTimeMillis()) < 10000L) { + nextRequestedPollTime = nextActualPollTime; } + // extended unavailable SGV may be due to clash with the current polling time + // while we wait for a good SGV event, polling is auto adjusted by offsetting the next poll based on miss count + if (dataStore.getUnavailableSGVCount() > 0) { + if (timeLastGoodSGV == 0) { + nextRequestedPollTime += POLL_PERIOD_MS / 5L; // if there is a uploader/sensor poll clash on startup then this will push the next attempt out by 60 seconds + } else if (dataStore.getUnavailableSGVCount() > 2) { + sendStatus("Warning: No SGV available from pump for " + dataStore.getUnavailableSGVCount() + " attempts"); + nextRequestedPollTime += ((long) ((dataStore.getUnavailableSGVCount() - 2) % 5)) * (POLL_PERIOD_MS / 10L); // adjust poll time in 1/10 steps to avoid potential poll clash (max adjustment at 5/10) + } + } + MedtronicCnlAlarmManager.setAlarm(nextRequestedPollTime); + sendStatus("Next poll due at: " + df.format(nextRequestedPollTime)); } - MedtronicCnlAlarmManager.setAlarm(nextRequestedPollTime); - sendStatus("Next poll due at: " + df.format(nextRequestedPollTime)); - + } finally { MedtronicCnlAlarmReceiver.completeWakefulIntent(intent); } } -- GitLab