Files
mg4/AGENTS.md
T
nilsandClaude Opus 5.5 e39d9cdcf9 Replace the muted gold accent with MG red
ev_accent is now #E0424C, the red of the launcher icon (about 4.6:1 on
the black background, enough for accent-coloured text; the darker MG
brand red is about 3:1). ev_on_accent and ev_gold_surface follow with a
dark red tint; the gold hardcoded in two drawables and the consumption
period buttons is replaced too. AGENTS.md's visual direction updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
2026-10-17 14:29:01 +02:00

6.7 KiB

AGENTS.md — EVABRPUploader

Context for AI agents and new contributors working in this repository.

EVABRPUploader is part of EVSuite. The workspace AGENTS.md and normative workspace DESIGN.md apply; this file contains only telemetry-specific additions.

App-specific visual direction (2.2.6)

The user explicitly requested a permanent OLED-black theme, charcoal glass-like cards, MG red (#E0424C, replacing the earlier muted gold) and cyan accents. These app resources intentionally override the shared DESIGN.md palette, DayNight preference and flat-surface rules. Do not overwrite them with the shared token generator. Use static gradients rather than continuous effects; keep large touch targets, readable text and null telemetry shown as a dash. Wide layouts start at 1000dp; narrower windows use a bottom navigation bar.

What this is

An Android Automotive app for the MG4 (SAIC eh32) that reads vehicle telemetry and pushes it to A Better Route Planner. It runs on the car's head unit (AAOS 9, MT2712).

Fork of Leon Kernan's ABRP_Uploader. See LICENSE.md — the licence status is unresolved and that is a real constraint, not paperwork.

Non-negotiables

  1. Telemetry is read-only. No vehicle settings may be written. Window automation was removed in 2.2.5 at the user's request, including its service and door listener.
  2. A failed read is not a zero. Getters return Integer/Float/Boolean and null means "could not read". Null fields are omitted from the payload. Sending 0 for SOC tells ABRP the battery is empty and wrecks a live route plan.
  3. Fail closed. Update origin, APK signature, unknown vehicle state — refuse rather than assume.
  4. Never let a credential reach a URL, a log line, or a crash report.
  5. AAOS 9 must stay stable. A third-party app crashing the head unit is unacceptable. Every car call goes through reflection with try/catch; keep it that way.
  6. Power matters. The head unit is awake whenever the car is on. No wake locks, no alarms. The GPS subscription and the upload tick both cost power — keep them in step.

Architecture

AbrpUploadService   foreground service; scheduler thread does all IPC and HTTP
CarPropertyAdapter  reflection bridge to android.car (not on the compile classpath)
TelemetryPayload    pure JSON building — no Android types, unit-tested
UploadCadence       when a tick actually uploads — pure, unit-tested
UploadSettings      user cadence configuration
UploadLog           last 20 attempts + derived service state — pure, unit-tested
SecurePrefs         credentials in EncryptedSharedPreferences, migrates plaintext
AbrpApi             HTTP; credentials in the POST body, never the query string
MainActivity        configuration, service state, upload log

Put logic in the pure classes. Anything Android-free can be unit-tested on the JVM; anything inside the service effectively cannot be tested without a car.

Source sets

Set Contents Ships in
main everything shared all builds
stable UpdateHook no-op stable channel
unstable active GitHub pre-release updater and PackageInstaller result receiver unstable channel
debug VhalProbe — enumerates the whole VHAL debug builds only
testUnstable OTA policy tests test only

The stable channel does not merely disable self-update — the updater is not in the APK.

Firmware generations

MG4 infotainment ships as SWI68 / SWI69 / SWI131 / SWI132 / SWI133 / SWI165, and they differ. Property IDs confirmed on one generation are not universal. Anything vehicle-facing must tolerate a property simply not existing.

Two read paths, split by whether the signal is firmware-agnostic:

Signal Read path Firmwares
Speed, outside temp, park EVHardware getters (getVehicleSpeedKmh, getOutsideTempCelsius, isVehicleInPark) all six — EVHardware branches per generation internally
SOC, range CarPropertyAdapter vendor IDs (0x2160F404, 0x2140F41C) SWI68 only
Charge rate/port, cabin temp CarPropertyAdapter standard-AAOS IDs wherever the VHAL implements them

EVHardware.init() is called in AbrpUploadService.onCreate() (idempotent); it detects the generation and picks the right underlying API. Fields it covers therefore work on every supported firmware.

Reference firmware: SWI68-29958-1300R69. The vendor EV IDs still in CarPropertyAdapter are supported only on SWI68 firmware. EVHardware has no EV-battery abstraction for any generation, so SOC and range cannot go through it; until those vendor IDs are confirmed on the other generations they stay SWI68-only, and on another firmware they may silently return nothing (handled safely — the field is omitted, but SOC/range would be missing). Re-confirm with VhalProbe before trusting the vendor IDs on any other generation.

Working here

mise run check      # what CI runs: permission gate + lint + unit tests
mise run build
mise run logs       # adb logcat, filtered to this app

Gradle needs JDK 17. If a build fails in JdkImageTransform complaining about jlink, a stale Gradle daemon started by VS Code's Java extension is being reused — pkill -f GradleDaemon and retry.

Things that will bite you

  • android.car is not on the compile classpath. Everything car-related is reflection. R8 cannot see those uses, so proguard-rules.pro keeps them by name.
  • Unit tests get a stub org.json from android.jar that returns null. The real implementation is added as a test dependency; do not remove it.
  • service_running in preferences goes stale-true after a force-kill. Use AbrpUploadService.isRunning().
  • Permission changes fail CI until allowlisted with a justification.

Honesty requirements

Most of this cannot be verified off a vehicle. When reporting work, state plainly what was tested and what was not. "Unit tests pass, not tried on the car" is the expected and acceptable answer. Do not imply road-testing that did not happen.

This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ (resolved from this file's directory; in monorepos the next package may not be visible from the repo root) before writing any code. Heed deprecation notices.

This block is written and re-added by next dev — verify at node_modules/next/dist/server/lib/generate-agent-files.js. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.