Tweaking SMS protocol

This commit is contained in:
2026-05-08 17:44:36 +00:00
parent fba1bbef80
commit 668bfad802
3 changed files with 219 additions and 14 deletions
@@ -610,8 +610,8 @@ public abstract class SdDataSource {
mSdData.alarmCause = ""; mSdData.alarmCause = "";
boolean flapDetected = flapCheck(); boolean flapDetected = flapCheck();
alarmCheck(flapDetected);
hrCheck(); hrCheck();
alarmCheck(flapDetected);
o2SatCheck(); o2SatCheck();
fallCheck(); fallCheck();
muteCheck(); muteCheck();
@@ -739,7 +739,10 @@ public abstract class SdDataSource {
mAlarmCount += mSamplePeriod; mAlarmCount += mSamplePeriod;
if (mAlarmCount > mAlarmTime) { if (mAlarmCount > mAlarmTime) {
if (mRequireHrForSeizureAlarm) { if (mSdData.alarmState == 2) {
// Already in full alarm. Do not require HR confirmation again.
mSdData.alarmState = 2;
} else if (mRequireHrForSeizureAlarm) {
if (hasRecentHrConfirmation()) { if (hasRecentHrConfirmation()) {
mSdData.alarmState = 2; mSdData.alarmState = 2;
mSdData.alarmCause = mSdData.alarmCause + "HR_CONFIRM "; mSdData.alarmCause = mSdData.alarmCause + "HR_CONFIRM ";
@@ -753,9 +756,6 @@ public abstract class SdDataSource {
// Existing behavior // Existing behavior
mSdData.alarmState = 2; mSdData.alarmState = 2;
} }
} else if (mAlarmCount > mWarnTime) {
// warning
mSdData.alarmState = 1;
} }
} else { } else {
// If we are not in an ALARM state, revert back to WARNING, otherwise // If we are not in an ALARM state, revert back to WARNING, otherwise
@@ -125,8 +125,19 @@ public class SdServer extends Service implements SdDataReceiver {
private boolean mMp3Alarm = false; private boolean mMp3Alarm = false;
private boolean mPhoneAlarm = false; private boolean mPhoneAlarm = false;
private boolean mSMSAlarm = false; private boolean mSMSAlarm = false;
private boolean mFlogaEmergencyEscalation = true; // Emergency-services escalation SMS.
// This is separate from the normal carer/contact SMS.
private boolean mEmergencySmsEnabled = false;
private boolean mEmergencySmsNotifyContacts = true;
private String mEmergencySmsNumber = "";
private String mEmergencySmsMessage =
"Possible seizure emergency. The wearer may be having a tonic-clonic seizure and the alarm has remained active."; private int mEmergencySmsDelaySecs = 300;
private EmergencySmsTimer mEmergencySmsTimer = null;
private boolean mEmergencySmsSent = false;
private long mAlarmStartTime = 0; private long mAlarmStartTime = 0;
private String mLastLocationUrl = "";
private String mLastAlarmDateStr = "";
private String[] mSMSNumbers; private String[] mSMSNumbers;
private String mSMSMsgStr = "default SMS Message"; private String mSMSMsgStr = "default SMS Message";
private String mSMSFalseAlarmMsgStr = "default SMS False Alarm Message"; private String mSMSFalseAlarmMsgStr = "default SMS False Alarm Message";
@@ -657,7 +668,16 @@ public class SdServer extends Service implements SdDataReceiver {
if ((sdData.alarmState == 2) || (sdData.alarmState == 5)) { if ((sdData.alarmState == 2) || (sdData.alarmState == 5)) {
sdData.alarmPhrase = "ALARM"; sdData.alarmPhrase = "ALARM";
sdData.alarmStanding = true; sdData.alarmStanding = true;
if (mAlarmStartTime == 0) mAlarmStartTime = System.currentTimeMillis();
if (mAlarmStartTime == 0) {
mAlarmStartTime = System.currentTimeMillis();
Time tnow = new Time(Time.getCurrentTimezone());
tnow.setToNow();
mLastAlarmDateStr = tnow.format("%H:%M:%S %d/%m/%Y");
startEmergencySmsTimer();
}
if (mLogAlarms) { if (mLogAlarms) {
Log.v(TAG, "***ALARM*** - Logging to SD Card"); Log.v(TAG, "***ALARM*** - Logging to SD Card");
//writeAlarmToSD(); //writeAlarmToSD();
@@ -678,12 +698,6 @@ public class SdServer extends Service implements SdDataReceiver {
- mSMSTime.toMillis(false)) - mSMSTime.toMillis(false))
> 60000) { > 60000) {
sendSMSAlarm(); sendSMSAlarm();
// Floga: escalate to emergency call if seizure longer than 5 minutes
long alarmDurationSecs = (System.currentTimeMillis() - mAlarmStartTime) / 1000;
if (alarmDurationSecs >= 300 && mFlogaEmergencyEscalation) {
sendPhoneAlarm();
Log.v(TAG, "Floga: 5 minute threshold reached - calling emergency contacts");
}
mSMSTime = tnow; mSMSTime = tnow;
} else { } else {
mUtil.showToast(getString(R.string.SMSAlarmAlreadySentMsg)); mUtil.showToast(getString(R.string.SMSAlarmAlreadySentMsg));
@@ -1067,6 +1081,46 @@ public class SdServer extends Service implements SdDataReceiver {
} }
} }
private void startEmergencySmsTimer() {
if (!mEmergencySmsEnabled) {
Log.v(TAG, "startEmergencySmsTimer() - emergency SMS disabled");
return;
}
if (mEmergencySmsSent) {
Log.v(TAG, "startEmergencySmsTimer() - emergency SMS already sent");
return;
}
if (mEmergencySmsTimer != null) {
Log.v(TAG, "startEmergencySmsTimer() - timer already running");
return;
}
if (mEmergencySmsNumber == null || mEmergencySmsNumber.trim().length() == 0) {
Log.w(TAG, "startEmergencySmsTimer() - no emergency SMS number set");
return;
}
Log.i(TAG, "startEmergencySmsTimer() - starting emergency SMS timer for "
+ mEmergencySmsDelaySecs + " seconds");
runOnUiThread(new Runnable() {
public void run() {
mEmergencySmsTimer =
new EmergencySmsTimer(mEmergencySmsDelaySecs * 1000L, 1000);
mEmergencySmsTimer.start();
}
});
}
private void stopEmergencySmsTimer() {
if (mEmergencySmsTimer != null) {
Log.i(TAG, "stopEmergencySmsTimer() - cancelling emergency SMS timer");
mEmergencySmsTimer.cancel();
mEmergencySmsTimer = null;
}
}
/* /*
* Start the timer that will automatically re-set a latched alarm after a given period. * Start the timer that will automatically re-set a latched alarm after a given period.
@@ -1111,6 +1165,12 @@ public class SdServer extends Service implements SdDataReceiver {
Log.i(TAG, "acceptAlarm()"); Log.i(TAG, "acceptAlarm()");
mSdData.alarmStanding = false; mSdData.alarmStanding = false;
mSdData.fallAlarmStanding = false; mSdData.fallAlarmStanding = false;
mAlarmStartTime = 0;
mLastAlarmDateStr = "";
mEmergencySmsSent = false;
stopEmergencySmsTimer();
mSdDataSource.acceptAlarm(); mSdDataSource.acceptAlarm();
stopLatchTimer(); stopLatchTimer();
} }
@@ -1315,7 +1375,24 @@ public class SdServer extends Service implements SdDataReceiver {
mSMSAlarm = SP.getBoolean("SMSAlarm", false); mSMSAlarm = SP.getBoolean("SMSAlarm", false);
Log.v(TAG, "updatePrefs() - mSMSAlarm = " + mSMSAlarm); Log.v(TAG, "updatePrefs() - mSMSAlarm = " + mSMSAlarm);
mUtil.writeToSysLogFile("updatePrefs() - mSMSAlarm = " + mSMSAlarm); mUtil.writeToSysLogFile("updatePrefs() - mSMSAlarm = " + mSMSAlarm);
mFlogaEmergencyEscalation = SP.getBoolean("FlogaEmergencyEscalation", true); mEmergencySmsEnabled = SP.getBoolean("EmergencySmsEnabled", false);
mEmergencySmsNotifyContacts = SP.getBoolean("EmergencySmsNotifyContacts", true);
mEmergencySmsNumber = SP.getString("EmergencySmsNumber", "");
mEmergencySmsMessage = SP.getString(
"EmergencySmsMessage",
"Possible seizure emergency. The wearer may be having a tonic-clonic seizure and the alarm has remained active."
);
try {
String emergencyDelayStr = SP.getString("EmergencySmsDelaySecs", "300");
mEmergencySmsDelaySecs = Integer.parseInt(emergencyDelayStr);
} catch (Exception ex) {
mEmergencySmsDelaySecs = 300;
}
Log.v(TAG, "updatePrefs() - mEmergencySmsEnabled = " + mEmergencySmsEnabled);
Log.v(TAG, "updatePrefs() - mEmergencySmsNumber = " + mEmergencySmsNumber);
Log.v(TAG, "updatePrefs() - mEmergencySmsDelaySecs = " + mEmergencySmsDelaySecs);
mPhoneAlarm = SP.getBoolean("PhoneCallAlarm", false); mPhoneAlarm = SP.getBoolean("PhoneCallAlarm", false);
Log.v(TAG, "updatePrefs() - mSMSAlarm = " + mSMSAlarm); Log.v(TAG, "updatePrefs() - mSMSAlarm = " + mSMSAlarm);
mUtil.writeToSysLogFile("updatePrefs() - mSMSAlarm = " + mSMSAlarm); mUtil.writeToSysLogFile("updatePrefs() - mSMSAlarm = " + mSMSAlarm);
@@ -1509,6 +1586,7 @@ public class SdServer extends Service implements SdDataReceiver {
+ ";u=" + df.format(ll.getAccuracy()) + "'>here</a>"; + ";u=" + df.format(ll.getAccuracy()) + "'>here</a>";
String googleUrl = "https://www.google.com/maps/place?q=" String googleUrl = "https://www.google.com/maps/place?q="
+ ll.getLatitude() + "%2C" + ll.getLongitude(); + ll.getLatitude() + "%2C" + ll.getLongitude();
mLastLocationUrl = googleUrl;
String shortUuidStr = mUuidStr.substring(mUuidStr.length() - 6); String shortUuidStr = mUuidStr.substring(mUuidStr.length() - 6);
String messageStr = "Location update for seizure detected at " String messageStr = "Location update for seizure detected at "
@@ -1532,7 +1610,69 @@ public class SdServer extends Service implements SdDataReceiver {
} }
private void sendSMSDirect(String phoneNo, String msgStr) {
String number = phoneNo == null ? "" : phoneNo.trim();
if (number.length() == 0) {
Log.w(TAG, "sendSMSDirect() - empty phone number, skipping");
return;
}
Log.i(TAG, "sendSMSDirect() - Sending to " + number);
try {
SmsManager sm = SmsManager.getDefault();
sm.sendTextMessage(number, null, msgStr, null, null);
} catch (Exception e) {
Log.e(TAG, "sendSMSDirect - Failed to send SMS Message");
mUtil.writeToSysLogFile("sendSMSDirect - Failed to send SMS Message");
Log.e(TAG, e.toString());
mUtil.showToast(getString(R.string.failed_to_send_sms));
}
}
private void sendEmergencySms() {
String shortUuidStr = mUuidStr.substring(mUuidStr.length() - 6);
String locationPart;
if (mLastLocationUrl != null && mLastLocationUrl.length() > 0) {
locationPart = " Location: " + mLastLocationUrl;
} else {
locationPart = " Location unavailable.";
}
String alarmTimePart;
if (mLastAlarmDateStr != null && mLastAlarmDateStr.length() > 0) {
alarmTimePart = " Alarm time: " + mLastAlarmDateStr + ".";
} else {
alarmTimePart = "";
}
String emergencyMessage = mEmergencySmsMessage
+ alarmTimePart
+ " Alarm duration: "
+ mEmergencySmsDelaySecs
+ " seconds."
+ locationPart
+ " Device ID: "
+ shortUuidStr;
Log.i(TAG, "sendEmergencySms() - sending emergency SMS to " + mEmergencySmsNumber);
Log.i(TAG, "sendEmergencySms() - message: " + emergencyMessage);
sendSMSDirect(mEmergencySmsNumber, emergencyMessage);
if (mEmergencySmsNotifyContacts) {
String contactMessage = "Emergency services have been contacted for seizure alert"
+ alarmTimePart
+ locationPart
+ " Device: "
+ shortUuidStr;
for (int i = 0; i < mSMSNumbers.length; i++) {
sendSMSDirect(mSMSNumbers[i], contactMessage);
}
}
}
/* /*
* Latch alarm in alarm state for a given period (mLatchAlarmPeriod seconds) after the alarm is raised. * Latch alarm in alarm state for a given period (mLatchAlarmPeriod seconds) after the alarm is raised.
@@ -1619,7 +1759,37 @@ public class SdServer extends Service implements SdDataReceiver {
} }
} }
private class EmergencySmsTimer extends CountDownTimer {
public EmergencySmsTimer(long startTime, long interval) {
super(startTime, interval);
}
@Override
public void onFinish() {
Log.i(TAG, "EmergencySmsTimer.onFinish()");
mEmergencySmsTimer = null;
if (mEmergencySmsSent) {
Log.i(TAG, "EmergencySmsTimer.onFinish() - already sent");
return;
}
if (mSdData == null || !mSdData.alarmStanding) {
Log.i(TAG, "EmergencySmsTimer.onFinish() - alarm no longer standing, not sending");
return;
}
sendEmergencySms();
mEmergencySmsSent = true;
}
@Override
public void onTick(long msRemaining) {
Log.v(TAG, "EmergencySmsTimer.onTick() - time remaining = "
+ msRemaining / 1000 + " sec");
}
}
/** /**
* Inhibit fault alarm initiation for a period to avoid spurious warning * Inhibit fault alarm initiation for a period to avoid spurious warning
* beeps caused by short term network interruptions. * beeps caused by short term network interruptions.
+35
View File
@@ -87,6 +87,41 @@
android:summary="@string/sms_false_alarm_message_summary" android:summary="@string/sms_false_alarm_message_summary"
android:title="@string/sms_false_alarm_message_title" /> android:title="@string/sms_false_alarm_message_title" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="Emergency Services SMS Settings">
<CheckBoxPreference
android:defaultValue="false"
android:key="EmergencySmsEnabled"
android:summary="Send a separate SMS to the emergency services test number if the seizure alarm remains active for the configured delay."
android:title="Enable emergency services SMS" />
<EditTextPreference
android:defaultValue=""
android:key="EmergencySmsNumber"
android:summary="Test number for emergency services escalation. Use a project/test number, not a real emergency number."
android:title="Emergency services test number" />
<EditTextPreference
android:defaultValue="300"
android:key="EmergencySmsDelaySecs"
android:inputType="number"
android:summary="Delay before emergency services SMS is sent. Use 300 for 5 minutes; use a shorter value for testing."
android:title="Emergency SMS delay seconds" />
<EditTextPreference
android:defaultValue="Possible seizure emergency. The wearer may be having a tonic-clonic seizure and the alarm has remained active."
android:key="EmergencySmsMessage"
android:summary="Message prefix for the emergency services SMS."
android:title="Emergency services SMS message" />
<CheckBoxPreference
android:defaultValue="true"
android:key="EmergencySmsNotifyContacts"
android:summary="Also send a follow-up SMS to normal contacts when the emergency services SMS is sent."
android:title="Notify contacts after emergency SMS" />
</PreferenceCategory>
<!-- <!--
<PreferenceCategory android:title="Phone Call Alarm Settings"> <PreferenceCategory android:title="Phone Call Alarm Settings">
<CheckBoxPreference <CheckBoxPreference