App 2.17.2: Wi-Fi auto-connect removed
Upstream kept the head unit on the driver's phone hotspot by re-joining a saved network every few minutes. This car's head unit has its own connection and the Wi-Fi is not used, so the feature goes entirely: WifiAutoConnect, its call on the service tick, the preference key, and the ACCESS_WIFI_STATE / CHANGE_WIFI_STATE permissions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
This commit is contained in:
@@ -112,9 +112,11 @@ tyre pressures, GPS (lat, lon, elevation, heading) and the is_* flags. It does *
|
||||
INGEST_TOKEN) into git and pushed it. Commit amended and force-pushed; both values regenerated in
|
||||
the now gitignored `k8s/secrets.yaml` (since moved to `k8s/secrets/secrets.yaml`, outside
|
||||
`kubectl apply -f k8s/`). Filled-in secrets live only there from now on.
|
||||
- 2026-10-18 — App 2.17.2: Wi-Fi auto-connect removed with its two permissions (ACCESS_ and
|
||||
CHANGE_WIFI_STATE). Upstream used it to keep the head unit on a phone hotspot; this car's head
|
||||
unit has its own connection.
|
||||
- 2026-10-18 — App 2.17.1: Settings without the Wi-Fi and saved-networks cards, in three columns
|
||||
(display and steering wheel | battery | charging price and app). The background Wi-Fi
|
||||
auto-connect stays on, now without a switch. Logs keeps only the upload log: the steering-key
|
||||
(display and steering wheel | battery | charging price and app). Logs keeps only the upload log: the steering-key
|
||||
reports, the car-colour probe and the screen line are gone, their questions answered.
|
||||
- 2026-10-18 — App 2.17.0: "Speed, 5-min average" card under Speed. The top bar is one compact line
|
||||
(title and subtitle); version, update check and About moved to an App card on the Settings tab.
|
||||
|
||||
+2
-2
@@ -34,8 +34,8 @@ android {
|
||||
applicationId "com.stinnesbeck.nilsmg4"
|
||||
minSdk 27
|
||||
targetSdk 36
|
||||
versionCode 90
|
||||
versionName "2.17.1"
|
||||
versionCode 91
|
||||
versionName "2.17.2"
|
||||
|
||||
// Nothing about the server ships in the APK: its URL and ingest token are entered in
|
||||
// the app's Server tab (ServerConfig), and the database is only reachable by the server.
|
||||
|
||||
@@ -14,12 +14,6 @@
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
|
||||
<!-- Wi-Fi auto-join. CHANGE_WIFI_STATE covers enabling the radio and asking the
|
||||
supplicant to join a network the unit already has saved; ACCESS_WIFI_STATE covers
|
||||
reading which one it landed on. No password is ever handled here — see
|
||||
WifiAutoConnect for why this works on this Android 9 unit and not on a newer one. -->
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
|
||||
<!-- Android Automotive / CarPropertyManager permissions -->
|
||||
<uses-permission android:name="android.car.permission.CAR_SPEED" />
|
||||
|
||||
@@ -68,7 +68,6 @@ public class AbrpUploadService extends Service {
|
||||
private ChargeSessionTracker chargeSessionTracker;
|
||||
private TelemetryUploader telemetryUploader;
|
||||
private SteeringKeyListener steeringKeys;
|
||||
private WifiAutoConnect wifi;
|
||||
private ScheduledExecutorService scheduler;
|
||||
private SharedPreferences prefs;
|
||||
|
||||
@@ -97,7 +96,6 @@ public class AbrpUploadService extends Service {
|
||||
consumptionTracker = ConsumptionTracker.get(this);
|
||||
consumptionCloudClient = new ConsumptionCloudClient(getApplicationContext());
|
||||
chargeSessionTracker = ChargeSessionTracker.get(getApplicationContext());
|
||||
wifi = new WifiAutoConnect(getApplicationContext());
|
||||
telemetryUploader = new TelemetryUploader(getApplicationContext());
|
||||
steeringKeys = new SteeringKeyListener(this, prefs);
|
||||
steeringKeys.register();
|
||||
@@ -169,12 +167,6 @@ public class AbrpUploadService extends Service {
|
||||
chargeSessionTracker.sample(vehicle);
|
||||
consumptionCloudClient.syncIfDue(consumptionTracker, chargeSessionTracker, sampleMs);
|
||||
|
||||
// Keep the head unit on the phone hotspot — the sync needs a network, and this is
|
||||
// exactly when being offline costs something. Rate-limited internally.
|
||||
if (wifi != null && prefs.getBoolean(MainActivity.WIFI_AUTO_KEY, true)) {
|
||||
wifi.ensureConnected(false);
|
||||
}
|
||||
|
||||
UploadSettings settings = UploadSettings.from(prefs);
|
||||
// Live data goes to the server, to ABRP directly, or both; with neither set up there is
|
||||
// nowhere to send it.
|
||||
|
||||
@@ -35,12 +35,6 @@ import com.evsuite.hardware.telemetry.EnergyTelemetryReader;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
/**
|
||||
* Rejoin a saved network (the phone hotspot) in the background; read by
|
||||
* {@link AbrpUploadService} on every tick. On by default; the Wi-Fi card with its switch was
|
||||
* removed from Settings (2.17.1), so it now stays on.
|
||||
*/
|
||||
static final String WIFI_AUTO_KEY = "wifi_auto_connect";
|
||||
private static final String REPOSITORY_URL = "https://git.stinnesbeck.com/nils/mg4";
|
||||
|
||||
/** Navigation destinations in page order. Parallel to {@link #panes}. */
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
package com.evsuite.abrp;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.wifi.WifiConfiguration;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Brings Wi-Fi up and joins a network the head unit already knows.
|
||||
*
|
||||
* The point is the phone hotspot: the car has the credentials saved, but the driver still
|
||||
* has to walk into Settings and tap the network every time. This does that tap.
|
||||
*
|
||||
* <b>Why this works here and would not on a newer phone.</b> {@code getConfiguredNetworks}
|
||||
* and {@code enableNetwork} were deprecated in API 29 and, from that release, return an
|
||||
* empty list and refuse the call for ordinary apps — Android handed network choice to the
|
||||
* user through a system picker. This head unit runs Android 9 (API 28), where both are
|
||||
* still honoured, and the build is platform-signed, so it is the same UID family the
|
||||
* Settings app itself uses. Neither fact is portable: on a Pixel this class would find
|
||||
* nothing and change nothing. It is deliberately written to fail quietly if so.
|
||||
*
|
||||
* Nothing here creates or edits a network. It only enables one the unit was already
|
||||
* configured with, which is why no password ever passes through this app.
|
||||
*/
|
||||
final class WifiAutoConnect {
|
||||
|
||||
private static final String TAG = "EVABRP.Wifi";
|
||||
|
||||
/** Re-checking faster than this achieves nothing: a join takes seconds to settle. */
|
||||
private static final long MIN_ATTEMPT_INTERVAL_MS = 30_000L;
|
||||
|
||||
private final WifiManager wifi;
|
||||
private long lastAttemptMs;
|
||||
|
||||
WifiAutoConnect(Context context) {
|
||||
this.wifi = (WifiManager) context.getApplicationContext()
|
||||
.getSystemService(Context.WIFI_SERVICE);
|
||||
}
|
||||
|
||||
/** The SSID the unit is associated with, or null when it is not on Wi-Fi. */
|
||||
String currentSsid() {
|
||||
try {
|
||||
if (wifi == null || !wifi.isWifiEnabled()) return null;
|
||||
WifiInfo info = wifi.getConnectionInfo();
|
||||
if (info == null) return null;
|
||||
String ssid = info.getSSID();
|
||||
if (ssid == null) return null;
|
||||
// The framework quotes the SSID and reports this placeholder when it will not
|
||||
// say — usually because location is off. Neither is a network name.
|
||||
ssid = ssid.replace("\"", "").trim();
|
||||
if (ssid.isEmpty() || "<unknown ssid>".equals(ssid) || "0x".equals(ssid)) return null;
|
||||
return ssid;
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* One attempt to get onto a saved network, rate-limited.
|
||||
*
|
||||
* @param force ignore the rate limit — for the button, which a driver only presses
|
||||
* because the automatic attempt has not worked yet.
|
||||
* @return what happened, for the log and the UI. Never null.
|
||||
*/
|
||||
Result ensureConnected(boolean force) {
|
||||
if (wifi == null) return Result.UNSUPPORTED;
|
||||
|
||||
String ssid = currentSsid();
|
||||
if (ssid != null) return Result.alreadyOn(ssid);
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
if (!force && now - lastAttemptMs < MIN_ATTEMPT_INTERVAL_MS) return Result.WAITING;
|
||||
lastAttemptMs = now;
|
||||
|
||||
try {
|
||||
if (!wifi.isWifiEnabled()) {
|
||||
// Also gone for ordinary apps from API 29, and also still honoured here.
|
||||
if (!wifi.setWifiEnabled(true)) return Result.CANNOT_ENABLE;
|
||||
// The radio needs a moment before it will associate; the next tick retries.
|
||||
return Result.ENABLING;
|
||||
}
|
||||
|
||||
List<WifiConfiguration> saved = wifi.getConfiguredNetworks();
|
||||
if (saved == null || saved.isEmpty()) return Result.NO_SAVED_NETWORKS;
|
||||
|
||||
// A scan would let us prefer a network that is actually in range, but the results
|
||||
// are themselves throttled and location-gated. Asking the supplicant to pick from
|
||||
// what it knows is both simpler and what the Settings screen effectively does.
|
||||
wifi.reconnect();
|
||||
for (WifiConfiguration c : saved) {
|
||||
if (c == null) continue;
|
||||
// Second argument true: disable the others for this attempt, so the
|
||||
// supplicant commits to this one rather than drifting between them.
|
||||
if (wifi.enableNetwork(c.networkId, true)) {
|
||||
String name = c.SSID == null ? "?" : c.SSID.replace("\"", "").trim();
|
||||
Log.i(TAG, "enableNetwork accepted for " + name);
|
||||
return Result.attempting(name);
|
||||
}
|
||||
}
|
||||
return Result.REFUSED;
|
||||
} catch (SecurityException e) {
|
||||
Log.w(TAG, "Wi-Fi connection refused: permission denied");
|
||||
return Result.REFUSED;
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, "connect attempt failed: " + t.getMessage());
|
||||
return Result.REFUSED;
|
||||
}
|
||||
}
|
||||
|
||||
/** Outcome of one attempt. {@link #connected} is the only one that means we are online. */
|
||||
static final class Result {
|
||||
final boolean connected;
|
||||
final String ssid;
|
||||
final String message;
|
||||
|
||||
private Result(boolean connected, String ssid, String message) {
|
||||
this.connected = connected;
|
||||
this.ssid = ssid;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
static Result alreadyOn(String ssid) { return new Result(true, ssid, "Connected: " + ssid); }
|
||||
static Result attempting(String ssid) { return new Result(false, ssid, "Connecting: " + ssid); }
|
||||
|
||||
static final Result UNSUPPORTED = new Result(false, null, "Wi-Fi cannot be managed");
|
||||
static final Result WAITING = new Result(false, null, "Waiting…");
|
||||
static final Result ENABLING = new Result(false, null, "Turning Wi-Fi on…");
|
||||
static final Result CANNOT_ENABLE = new Result(false, null, "Could not turn Wi-Fi on");
|
||||
static final Result NO_SAVED_NETWORKS = new Result(false, null, "No saved networks");
|
||||
static final Result REFUSED = new Result(false, null, "The system refused");
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ run = './gradlew -Dorg.gradle.java.home=$JAVA_HOME installStableDebug'
|
||||
|
||||
[tasks.logs]
|
||||
description = "Follows the app's logs on the car"
|
||||
run = "adb logcat -s AbrpUploadService:V CarPropertyAdapter:V VhalProbe:V SecurePrefs:V OtaUpdater:V UpdateHook:V NilsMG4.Telemetry:V NilsMG4.Mongo:V NilsMG4.SteeringKey:V EVABRP.Boot:V EVABRP.Wifi:V"
|
||||
run = "adb logcat -s AbrpUploadService:V CarPropertyAdapter:V VhalProbe:V SecurePrefs:V OtaUpdater:V UpdateHook:V NilsMG4.Telemetry:V NilsMG4.Mongo:V NilsMG4.SteeringKey:V EVABRP.Boot:V"
|
||||
|
||||
[tasks.release]
|
||||
description = "Release APKs of both channels (stable without OTA, unstable with OTA)"
|
||||
|
||||
Reference in New Issue
Block a user