Remove on-screen diagnostics from the Logs tab
They served their purpose: vehicle values show on the car. This reverts
commit b2a5b32.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,8 +42,6 @@ what has been changed, and what is still to do.
|
|||||||
- 2026-10-17 — Renamed to "Nils' MG4": package ID `com.stinnesbeck.nilsmg4` (`.unstable` for the car
|
- 2026-10-17 — Renamed to "Nils' MG4": package ID `com.stinnesbeck.nilsmg4` (`.unstable` for the car
|
||||||
build), APK names `NilsMG4-*.apk`. It installs next to the old FatihsMG4 app instead of replacing
|
build), APK names `NilsMG4-*.apk`. It installs next to the old FatihsMG4 app instead of replacing
|
||||||
it, and starts with empty settings and history.
|
it, and starts with empty settings and history.
|
||||||
- 2026-10-17 — Diagnostics section on the Logs tab: UID, signer, car permissions, last vehicle
|
|
||||||
read and its error, and the vehicle library's in-app log. For debugging without adb.
|
|
||||||
|
|
||||||
## Installing on the car
|
## Installing on the car
|
||||||
|
|
||||||
|
|||||||
@@ -102,16 +102,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
private ChargeSessionTracker chargeSessionTracker;
|
private ChargeSessionTracker chargeSessionTracker;
|
||||||
private ChargingGraphView chargeGraph;
|
private ChargingGraphView chargeGraph;
|
||||||
|
|
||||||
/**
|
|
||||||
* What the vehicle reads last did, for the diagnostics section of the Logs page. A failed
|
|
||||||
* read leaves the cells showing a dash, which looks the same as a signal the car does not
|
|
||||||
* publish; this is the only on-screen trace of why.
|
|
||||||
*/
|
|
||||||
private TextView diagnosticsText;
|
|
||||||
private volatile EnergySnapshot lastVehicleSnapshot;
|
|
||||||
private volatile long lastVehicleReadOkMs;
|
|
||||||
private volatile String lastVehicleReadError;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inflates a page with no parent, then gives it the MATCH_PARENT/MATCH_PARENT layout
|
* Inflates a page with no parent, then gives it the MATCH_PARENT/MATCH_PARENT layout
|
||||||
* params ViewPager2 requires of its items — inflating detached leaves them null, and
|
* params ViewPager2 requires of its items — inflating detached leaves them null, and
|
||||||
@@ -178,7 +168,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
((TextView) findViewById(R.id.header_title)).setText(PAGE_TITLES[position]);
|
((TextView) findViewById(R.id.header_title)).setText(PAGE_TITLES[position]);
|
||||||
((TextView) findViewById(R.id.header_subtitle)).setText(PAGE_SUBTITLES[position]);
|
((TextView) findViewById(R.id.header_subtitle)).setText(PAGE_SUBTITLES[position]);
|
||||||
if (panes[position] == logPane) refreshDiagnostics();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Refreshes state + call log while the screen is visible. */
|
/** Refreshes state + call log while the screen is visible. */
|
||||||
@@ -227,9 +216,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
statusText = servicePane.findViewById(R.id.status_text);
|
statusText = servicePane.findViewById(R.id.status_text);
|
||||||
callLogText = logPane.findViewById(R.id.call_log_text);
|
callLogText = logPane.findViewById(R.id.call_log_text);
|
||||||
logPane.findViewById(R.id.flush_now_button).setOnClickListener(v -> manualFlush());
|
logPane.findViewById(R.id.flush_now_button).setOnClickListener(v -> manualFlush());
|
||||||
diagnosticsText = logPane.findViewById(R.id.diagnostics_text);
|
|
||||||
logPane.findViewById(R.id.diagnostics_refresh_button)
|
|
||||||
.setOnClickListener(v -> refreshDiagnostics());
|
|
||||||
|
|
||||||
setUpPager();
|
setUpPager();
|
||||||
findViewById(R.id.about_button).setOnClickListener(v -> showAbout());
|
findViewById(R.id.about_button).setOnClickListener(v -> showAbout());
|
||||||
@@ -421,8 +407,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
// The timestamp is passed explicitly: its Kotlin default is not visible from Java.
|
// The timestamp is passed explicitly: its Kotlin default is not visible from Java.
|
||||||
EnergySnapshot s = vehicleReader.read(System.currentTimeMillis());
|
EnergySnapshot s = vehicleReader.read(System.currentTimeMillis());
|
||||||
lastVehicleSnapshot = s;
|
|
||||||
lastVehicleReadOkMs = System.currentTimeMillis();
|
|
||||||
ConsumptionTracker.get(this).sample(s);
|
ConsumptionTracker.get(this).sample(s);
|
||||||
refreshCharging(s);
|
refreshCharging(s);
|
||||||
|
|
||||||
@@ -439,12 +423,8 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
vehTireFr.setText(fmt(s.getTirePressures().getFrontRightKpa(), "%.0f kPa"));
|
vehTireFr.setText(fmt(s.getTirePressures().getFrontRightKpa(), "%.0f kPa"));
|
||||||
vehTireRl.setText(fmt(s.getTirePressures().getRearLeftKpa(), "%.0f kPa"));
|
vehTireRl.setText(fmt(s.getTirePressures().getRearLeftKpa(), "%.0f kPa"));
|
||||||
vehTireRr.setText(fmt(s.getTirePressures().getRearRightKpa(), "%.0f kPa"));
|
vehTireRr.setText(fmt(s.getTirePressures().getRearRightKpa(), "%.0f kPa"));
|
||||||
} catch (Throwable t) {
|
} catch (Throwable ignored) {
|
||||||
// Leave the last good reading on screen, but keep the reason for the diagnostics.
|
// Leave the last good reading on screen.
|
||||||
String trace = android.util.Log.getStackTraceString(t);
|
|
||||||
lastVehicleReadError = new java.text.SimpleDateFormat("HH:mm:ss", java.util.Locale.US)
|
|
||||||
.format(new java.util.Date()) + " "
|
|
||||||
+ (trace.length() > 2000 ? trace.substring(0, 2000) + "…" : trace);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -769,84 +749,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
callLogText.setText(sb.toString());
|
callLogText.setText(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- Diagnostics ----------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fills the diagnostics section: who the app runs as, how it is signed, which car
|
|
||||||
* permissions it holds, what the last vehicle read returned, and the in-app log of the
|
|
||||||
* vehicle library. On the head unit there is no adb, so this is what can be photographed.
|
|
||||||
*/
|
|
||||||
private void refreshDiagnostics() {
|
|
||||||
if (diagnosticsText == null) return;
|
|
||||||
java.text.SimpleDateFormat time = new java.text.SimpleDateFormat("HH:mm:ss", java.util.Locale.US);
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
int uid = android.os.Process.myUid();
|
|
||||||
sb.append("Package ").append(getPackageName()).append('\n');
|
|
||||||
sb.append("Version ").append(BuildConfig.VERSION_NAME).append('\n');
|
|
||||||
sb.append("UID ").append(uid)
|
|
||||||
.append(uid == android.os.Process.SYSTEM_UID ? " (system — OK)" : " (NOT system)").append('\n');
|
|
||||||
sb.append("Firmware ").append(safe(() -> com.evsuite.hardware.FirmwareInfo.INSTANCE.getDetectedString()))
|
|
||||||
.append('\n');
|
|
||||||
PackageManager pm = getPackageManager();
|
|
||||||
try {
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
android.content.pm.PackageInfo info = pm.getPackageInfo(getPackageName(),
|
|
||||||
PackageManager.GET_SIGNATURES | PackageManager.GET_PERMISSIONS);
|
|
||||||
if (info.signatures != null && info.signatures.length > 0) {
|
|
||||||
byte[] digest = java.security.MessageDigest.getInstance("SHA-256")
|
|
||||||
.digest(info.signatures[0].toByteArray());
|
|
||||||
StringBuilder hex = new StringBuilder();
|
|
||||||
for (byte b : digest) hex.append(String.format(java.util.Locale.US, "%02x", b));
|
|
||||||
sb.append("Signer ").append(hex).append('\n');
|
|
||||||
}
|
|
||||||
if (info.requestedPermissions != null) {
|
|
||||||
sb.append("Permissions\n");
|
|
||||||
for (String p : info.requestedPermissions) {
|
|
||||||
if (!p.startsWith("android.car.")) continue;
|
|
||||||
boolean granted = checkSelfPermission(p) == PackageManager.PERMISSION_GRANTED;
|
|
||||||
sb.append(" ").append(granted ? "granted " : "DENIED ")
|
|
||||||
.append(p.substring(p.lastIndexOf('.') + 1)).append('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
sb.append("Package info unavailable: ").append(e).append('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append('\n').append("Last vehicle read ")
|
|
||||||
.append(lastVehicleReadOkMs > 0 ? time.format(new java.util.Date(lastVehicleReadOkMs)) : "never")
|
|
||||||
.append('\n');
|
|
||||||
EnergySnapshot s = lastVehicleSnapshot;
|
|
||||||
if (s != null) {
|
|
||||||
sb.append(" SOC ").append(s.getSocPercent())
|
|
||||||
.append(" range ").append(s.getRangeKm())
|
|
||||||
.append(" speed ").append(s.getSpeedKmh())
|
|
||||||
.append(" power ").append(s.getBatteryPowerKw()).append('\n')
|
|
||||||
.append(" odometer ").append(s.getOdometerKm())
|
|
||||||
.append(" charge status ").append(s.getChargingStatus())
|
|
||||||
.append(" port ").append(s.getChargePortConnected()).append('\n');
|
|
||||||
}
|
|
||||||
String error = lastVehicleReadError;
|
|
||||||
if (error != null) sb.append("Last read error ").append(error).append('\n');
|
|
||||||
|
|
||||||
java.util.List<com.evsuite.hardware.AppLogger.Entry> entries =
|
|
||||||
com.evsuite.hardware.AppLogger.INSTANCE.getEntries();
|
|
||||||
int shown = Math.min(entries.size(), 200);
|
|
||||||
sb.append('\n').append("Vehicle library log (newest first, ").append(shown)
|
|
||||||
.append(" of ").append(entries.size()).append(")\n");
|
|
||||||
for (int i = entries.size() - 1; i >= entries.size() - shown; i--) {
|
|
||||||
com.evsuite.hardware.AppLogger.Entry e = entries.get(i);
|
|
||||||
sb.append(e.getTime()).append(' ').append(e.getLevel().name().charAt(0)).append('/')
|
|
||||||
.append(e.getTag()).append(": ").append(e.getMsg()).append('\n');
|
|
||||||
}
|
|
||||||
diagnosticsText.setText(sb.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface StringSupplier { String get() throws Exception; }
|
|
||||||
|
|
||||||
private static String safe(StringSupplier supplier) {
|
|
||||||
try { return supplier.get(); } catch (Throwable t) { return "unavailable (" + t + ")"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- Service status ----------
|
// ---------- Service status ----------
|
||||||
|
|
||||||
private void refreshStatus() {
|
private void refreshStatus() {
|
||||||
|
|||||||
@@ -4,8 +4,5 @@
|
|||||||
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/label_call_log" android:textAppearance="@style/Text.EV.Eyebrow" />
|
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/label_call_log" android:textAppearance="@style/Text.EV.Eyebrow" />
|
||||||
<com.google.android.material.button.MaterialButton android:id="@+id/flush_now_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="@string/btn_flush_now" app:icon="@drawable/ic_refresh" app:iconGravity="textStart" style="@style/Widget.EV.Button.Outlined" xmlns:app="http://schemas.android.com/apk/res-auto" />
|
<com.google.android.material.button.MaterialButton android:id="@+id/flush_now_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="@string/btn_flush_now" app:icon="@drawable/ic_refresh" app:iconGravity="textStart" style="@style/Widget.EV.Button.Outlined" xmlns:app="http://schemas.android.com/apk/res-auto" />
|
||||||
<TextView android:id="@+id/call_log_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:minHeight="180dp" android:text="@string/call_log_empty" android:textAppearance="@style/Text.EV.Body" android:fontFamily="monospace" android:textSize="17sp" android:lineSpacingExtra="12dp" android:textIsSelectable="true" />
|
<TextView android:id="@+id/call_log_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:minHeight="180dp" android:text="@string/call_log_empty" android:textAppearance="@style/Text.EV.Body" android:fontFamily="monospace" android:textSize="17sp" android:lineSpacingExtra="12dp" android:textIsSelectable="true" />
|
||||||
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="32dp" android:text="@string/label_diagnostics" android:textAppearance="@style/Text.EV.Eyebrow" />
|
|
||||||
<com.google.android.material.button.MaterialButton android:id="@+id/diagnostics_refresh_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="@string/btn_refresh_diagnostics" app:icon="@drawable/ic_refresh" app:iconGravity="textStart" style="@style/Widget.EV.Button.Outlined" xmlns:app="http://schemas.android.com/apk/res-auto" />
|
|
||||||
<TextView android:id="@+id/diagnostics_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:textAppearance="@style/Text.EV.Body" android:fontFamily="monospace" android:textSize="14sp" android:lineSpacingExtra="4dp" android:textIsSelectable="true" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -107,8 +107,6 @@
|
|||||||
<!-- Call log -->
|
<!-- Call log -->
|
||||||
<string name="label_call_log">Recent uploads</string>
|
<string name="label_call_log">Recent uploads</string>
|
||||||
<string name="call_log_empty">No upload attempted yet.</string>
|
<string name="call_log_empty">No upload attempted yet.</string>
|
||||||
<string name="label_diagnostics">Diagnostics</string>
|
|
||||||
<string name="btn_refresh_diagnostics">Refresh diagnostics</string>
|
|
||||||
<string name="btn_flush_now">Upload now</string>
|
<string name="btn_flush_now">Upload now</string>
|
||||||
<string name="status_starting">Starting…</string>
|
<string name="status_starting">Starting…</string>
|
||||||
<string name="status_running_no_upload">Running — waiting for first upload</string>
|
<string name="status_running_no_upload">Running — waiting for first upload</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user