Skip to content

About

Shared, safety-gated vehicle-access layer for EVSuite; compatible with selected SAIC MG4 firmware generations.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

2 watching

Forks

Repository files navigation

EVHardware

EVHardware

Tests Security Publish Release License: PolyForm Noncommercial Part of EVSuite

⚠️ This library reads and writes a car's settings. It runs inside apps installed on an MG4 head unit. Read DISCLAIMER.md before depending on it. MG and MG4 are third-party marks used only to identify compatibility; this independent project is not affiliated with or approved by SAIC Motor or MG Motor.

Shared vehicle-access layer for the SAIC MG4 (Android Automotive OS 9, MT2712). One implementation of the reflection-based hardware layer, the 0 km/h safety gate, the driving models, and the condition/action catalogue — consumed by the EVSuite apps so they do not each re-derive it and drift apart.

The design line: the apps are thin. EVHardware provides the vehicle. EVTasker is a rule engine between shared conditions and actions; EVChargePilot and EVABRPUploader consume the same energy snapshot instead of carrying their own property ids, units and fallback order.

Consumers: EVProfile (drive-profile manager; owns the signature-protected TaskerBridge), EVTasker, EVABRPUploader and EVChargePilot. Each app consumes EVHardware as a git submodule (EVHardware/lib as the :evhardware subproject). Common toolchain: AGP 9.1.1 / Gradle 9.3.1 / compileSdk 36 / JDK 17.

Part of EVSuite

EVHardware is the shared vehicle library of EVSuite, a family of independent, offline-first apps for the MG4 head unit (Android Automotive OS 9). Each app installs on its own — pick only what you need. User guides and install instructions: https://malys.github.io/EVSuite/.

Discover the rest of the suite:

EVProfile EVTasker EVABRPUploader EVChargePilot EVLauncher EVSwipe


Contents

Overview

Area Types
Vehicle access EVHardware — Katman1/4/5 reflection over android.car + SAIC SDK, per-firmware routing
Safety VehicleWriteGate (0 km/h, fail-closed) + @RequiresStandstill on every gated setter
Vendor services saic.* — the AIDL the head unit's own HVAC, charging, radio, hands-free and TTS apps use
Radio SaicRadio — play, silence, fail-closed play/pause toggle, FM/AM tune, station stepping (DAB included), screen
Energy telemetry EnergyTelemetryReader, EnergySnapshot — coherent nullable SOC, range, power, temperature, charging, tyre and climate reads
Energy accounting TripDetector, EnergyTripAccumulator, ChargingSessionMeter, EnergyTripHistoryStore — tested automatic boundaries, integration and atomic local persistence
Firmware FirmwareInfo (detection + capability helpers), FirmwareGen
Models DrivingProfile, DriveMode, RegenLevel, ProfileBackup
Catalogue ConditionType, ActionType, ValueSpec, VehicleEnums, SnapshotKeys
Compatibility @SupportedOn, FirmwareSupport, FirmwareMatrix → docs/firmware-matrix.md
Diagnostics AppLogger (ring buffer, no ADB needed), diag.CrashLogger, diag.PrivateBin

The safety gate and @RequiresStandstill

VehicleWriteGate refuses any road-behaviour write above 0 km/h, and fails closed when the speed is unreadable. Every gated setter in EVHardware carries @RequiresStandstill, and a unit test asserts that exact set — a setter cannot silently gain or lose gating. Comfort writes (seat/steering heating, volume, brightness, audio) are not gated and not annotated.

The threshold is fixed at exactly 0 km/h. It is not configurable, and no secondary signal (including park) can rescue an unreadable speed. Every decision reads the speed again so a sequence is cancelled as soon as the vehicle moves or the signal disappears.

Firmware compatibility is generated, not written

Each vehicle catalogue entry carries @SupportedOn(...), derived from FirmwareInfo and EVHardware's per-generation routing. docs/firmware-matrix.md is rendered from those annotations by FirmwareMatrix and checked by a test — never edited by hand. Consumers use the same annotations to hide entries unsupported on the connected car.

The matrix is derived from code, not on-vehicle testing. Consumers should surface a diagnostic that reads each signal live; unreadable ≠ unsupported.

Climate, glass and charging go through the vendor services

The AOSP property ids for HVAC and window position match the R69 runtime names but are not confirmed on every MG4 generation. They are still used for reads, where a wrong id returns null and nothing else happens — but nothing is written through them, because writing an unverified id to a vehicle is a different kind of mistake.

Writes go to saic.* instead: the binder interfaces the car's own HVAC, charging and vehicle apps call. The integration contract is known; whether a given firmware answers is a bind, which consumers report live rather than tabulate. The electric tailgate stays out — the launcher defines OPEN and CLOSE as the same value, so it is a pulse whose direction depends on state this cannot read.


How it works

EVHardware is consumed as a git submodule exposed as a Gradle subproject. In a consumer app:

git submodule add https://github.com/malys/EVHardware EVHardware

settings.gradle.kts:

include(":evhardware")
project(":evhardware").projectDir = file("EVHardware/lib")

app/build.gradle.kts:

dependencies { implementation(project(":evhardware")) }

Vehicle writes require the app to be signed with the ROM platform key (the car permissions are signature|privileged); reads of standard AAOS properties work with less. The library itself declares no permissions and no sharedUserId — that is the consuming app's decision.

Install

EVHardware is a library, not an APK: it ships inside the consumer apps (EVProfile, EVTasker, EVABRPUploader, EVChargePilot). Those apps are sideloaded on the head unit via the keyboard route: open a text field, long-press , on the on-screen keyboard → Language settings → search backup then press back to reach Android Settings → enable Developer options + Install unknown apps → search storage and open the APK. See each app's README for the full steps.

Consume the AAR

Projects outside this org can depend on EVHardware as a binary instead of a submodule. A version tag publishes the AAR to GitHub Packages (com.evsuite:evhardware:<version>), and ./gradlew :lib:publishToMavenLocal installs it to ~/.m2 for local experimentation:

repositories {
    mavenLocal()
    maven { url = uri("https://maven.pkg.github.com/malys/EVHardware") } // GitHub Packages
}
dependencies { implementation("com.evsuite:evhardware:0.1.0-SNAPSHOT") }

The AAR carries consumer-rules.pro, so a consumer's R8 keeps the reflected names. Vehicle writes still require the consuming app to be signed with the ROM platform key.

Building

./gradlew :lib:testDebugUnitTest   # unit tests + regenerates docs/firmware-matrix.md
./gradlew :lib:assembleDebug       # the AAR

JDK 17 (AGP 9.1.1 / Gradle 9.3.1 / compileSdk 36). android.car is not on the compile SDK, so all vehicle access is reflection — there is nothing to test on an emulator; the JVM tests cover the decision logic (gate, firmware annotations, catalogue consistency, telemetry validation and energy integration).

Adding a condition or action

One enum entry in catalog/ConditionType or catalog/ActionType, one string in lib/src/main/res/values/strings.xml (+ values-fr/), and — for a vehicle entry — one @SupportedOn(...). A test fails if a vehicle entry lacks it; the matrix regenerates on the next test run.


Project documents

Document What it covers
DESIGN.md The EVSuite design system — colour, type, touch targets, icons
AGENTS.md Context for AI agents working in this repository
CONTRIBUTING.md How to build, test and submit a change
SECURITY.md Threat model and vulnerability disclosure
DISCLAIMER.md Vehicle-safety disclaimer — read before installing
docs/radio-action-gap.md Which radio behaviours the catalogue carries, and which were rejected
CHANGELOG.md Release history
LICENSE.md Licence text

Security

See SECURITY.md. The library is the one place vehicle writes happen for its consumers, so the gate, the closed action vocabulary, and the per-firmware routing all live here rather than being reimplemented per app.

Contributing

Read CONTRIBUTING.md before opening a pull request. In short: this code runs in a moving vehicle, so changes stay small, carry tests, and say in the diff what would break without them. Anything touching the interface follows DESIGN.md.

Legal

Source-available under the PolyForm Noncommercial License 1.0.0. Commercial use is not permitted. See LICENSE and LICENSE.md. Runs on a vehicle; see DISCLAIMER.md.

About

Shared, safety-gated vehicle-access layer for EVSuite; compatible with selected SAIC MG4 firmware generations.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages