App 2.34.0: set the chosen drive mode again when the car starts

The car falls back to its own default mode at every start. With "Set at
start" (on by default), the service sets the mode last chosen on
Settings and confirmed by the car again, once, while the car is in P at
0 km/h, and gives up as soon as the car leaves P or after two minutes
(DriveModeWrite.atStart, unit-tested). The buttons and the start share
the write and read-back (DriveModeSetter). Not tried on the car.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
This commit is contained in:
2026-10-25 20:12:11 +01:00
co-authored by Claude Opus 5.5
parent ce2ebd7f58
commit ed65c96319
13 changed files with 241 additions and 38 deletions
@@ -69,6 +69,8 @@ public class AbrpUploadService extends Service {
private TelemetryUploader telemetryUploader;
private SteeringKeyListener steeringKeys;
private ScheduledExecutorService scheduler;
/** Sets the chosen drive mode at start, then ends (DriveModeWrite.atStart). */
private Thread driveModeAtStart;
private SharedPreferences prefs;
/** What the foreground notification currently says, so re-asserting it does not blank it. */
@@ -100,6 +102,7 @@ public class AbrpUploadService extends Service {
telemetryUploader = new TelemetryUploader(getApplicationContext());
steeringKeys = new SteeringKeyListener(this, prefs);
steeringKeys.register();
startDriveModeAtStart();
scheduler.scheduleWithFixedDelay(
this::safeUploadCycle,
@@ -145,9 +148,62 @@ public class AbrpUploadService extends Service {
telemetryUploader.stop();
}
if (steeringKeys != null) steeringKeys.unregister();
if (driveModeAtStart != null) driveModeAtStart.interrupt();
Log.i(TAG, "Service stopped");
}
// ---------- The drive mode at start ----------
/**
* The service starts with the head unit, i.e. when the car is switched on: the mode the
* driver chose on Settings (and the car confirmed) is set again, once, while the car is in
* P at 0 km/h — readings checked every second, for at most two minutes, and never after
* the car has left P. Its own thread: the upload scheduler may be busy with the network.
*/
private void startDriveModeAtStart() {
driveModeAtStart = new Thread(() -> {
long started = android.os.SystemClock.elapsedRealtime();
try {
while (true) {
Integer chosen = prefs.contains(DriveModeWrite.PREF_CHOSEN)
? prefs.getInt(DriveModeWrite.PREF_CHOSEN, 0) : null;
boolean enabled = prefs.getBoolean(DriveModeWrite.PREF_AT_START, true);
Integer gear = com.evsuite.hardware.EVHardware.INSTANCE.getGearRaw();
Float speed = com.evsuite.hardware.EVHardware.INSTANCE.getVehicleSpeedKmh();
Integer current = com.evsuite.hardware.EVHardware.INSTANCE.getDriveModeRaw();
DriveModeWrite.StartStep step = DriveModeWrite.atStart(chosen, enabled, gear, speed,
current, android.os.SystemClock.elapsedRealtime() - started);
if (step == DriveModeWrite.StartStep.WAIT) {
Thread.sleep(1000);
continue;
}
if (step == DriveModeWrite.StartStep.WRITE) {
DriveModeSetter.Result r = DriveModeSetter.set(chosen);
noteDriveModeAtStart(chosen, !r.written ? "REFUSED"
: DriveModeWrite.outcome(chosen, r.reported).name(), r.reported);
} else if (step == DriveModeWrite.StartStep.ALREADY) {
noteDriveModeAtStart(chosen, "ALREADY", current);
}
// GIVE_UP: nothing chosen, switched off, driven off or too late — nothing to say.
return;
}
} catch (InterruptedException ignored) {
// The service is stopping.
} catch (Throwable t) {
Log.w(TAG, "Drive mode at start failed: " + t);
}
}, "drive-mode-at-start");
driveModeAtStart.start();
}
/** Kept for the status line on Settings and logged: "25.10. 20:31|4|APPLIED|4". */
private void noteDriveModeAtStart(int code, String outcome, Integer reported) {
String line = new java.text.SimpleDateFormat("dd.MM. HH:mm", java.util.Locale.US).format(new java.util.Date())
+ "|" + code + "|" + outcome + "|" + (reported == null ? "" : reported);
prefs.edit().putString(DriveModeWrite.PREF_START_RESULT, line).apply();
Log.i(TAG, "Drive mode at start: " + line);
}
// ---------- Sampling cycle (runs on scheduler thread) ----------
private void safeUploadCycle() {