How to set a machine up to build, sign, install and debug apps for the Aiways U5 head unit (Adayo i.MX6, Android 4.4.2). See Hardware & OS for the platform and How to get in for getting in.
Four small apps you can install on the car — good starting points that show how the pieces fit together. Each runs as system (sharedUserId=“android.uid.system”), is platform-signed.
| App | What it does | Download |
|---|---|---|
| OpenAiways Connect | Automatically connects to a given Wi-Fi and enables ADB-over-Wi-Fi. | openaiways-connect.apk |
| OpenAiways Telemetry | Reads CAN signals and publishes them to MQTT with Home Assistant auto-discovery. | openaiways-telemetry.apk |
| OpenAiways Terminal | A VT100 terminal with an advanced keyboard; runs as system, or as root if you press the su button . | openaiways-terminal.apk |
| OpenAiways Speedwarning | Shows the current speed limit on the driver panel and warns when you exceed it — fully offline, from an on-board map index. | openaiways-speedwarning.apk |
.apk (copy to a USB stick or download it on the unit) — see How to get in for the file-manager route.Add your Wi-Fi networks (SSID + password, as a priority list) and tick ADB. After boot it reconnects Wi-Fi and re-enables adb-over-Wi-Fi.
Source: https://codeberg.org/noonscoomo/openaiways/src/branch/main/android-connect
Enter your MQTT broker (host, port, user, password) and a base topic. With Home Assistant auto-discovery the car shows up automatically — state of charge, voltage, current, range, charging and more.
A proper terminal (based on jackpal's emulatorview) with its own on-screen US keyboard, so the device's IME is not needed.
su key switches the shell to root and back — when you are root it reads system. Root is a real uid 0 PTY served by the local adbd. warning - you can do everything as root, even kill you system. Use on your own riskadb cannot connect (and vice-versa). Leaving or closing the terminal frees adb again automatically.Shows the current speed limit on the driver panel and blinks + beeps when you drive faster than it — fully offline, no cloud and no OEM data.
How it works: live speed comes from the CAN bus, position from GPS. An on-board HMM map-matcher (Newson–Krumm style) fits the GPS track to the road sequence in an offline OpenStreetMap index — so it stays on the correct road under bridges, in tunnels (it coasts on CAN odometry until GPS re-acquires), and past parallel streets, then reads that road's limit (including time-dependent “30 km/h 07–19h” style zones). The sign renders on the reachable driver panel (see architecture).
The map index (required): the app reads an offline road/speed index at /sdcard/region_route.sqlite. A ready-made one:
For another area you build your own index from OpenStreetMap with the map-matcher pipeline (source to follow). Without an index the app runs but shows no limit.
Install:
openaiways-speedwarning.apk and open it once (arms auto-start; it then runs on every boot)./sdcard/region_route.sqlite (e.g. adb push region_route.sqlite /sdcard/), then tap Restart service. The status screen should read Map DB: loaded.
It also records your drives to /sdcard/aiways_track.csv (size-capped, auto-rotated to ≤32 MB) to help improve the matcher.
minSdkVersion 19 (compileSdk / targetSdk can be higher). The ABI is armeabi-v7a — don't ship arm64/x86-only native libraries.adb (Android platform-tools) on your PATH.
The unit runs adb over TCP and has ro.adb.secure=0 (no key-auth prompt). On the home Wi-Fi it
is at <HEAD-UNIT-IP>:5555.
adb connect <HEAD-UNIT-IP>:5555 adb devices # -> <HEAD-UNIT-IP>:5555 device
If adb-over-TCP isn't up, enable it from a root shell on the unit: setprop service.adb.tcp.port
5555 then stop adbd; start adbd.
Several of these apps declare sharedUserId=“android.uid.system” and use signature-level permissions, so they must be signed with the platform key — the key the device's own framework is signed with. Android only grants that system identity to an app whose signing certificate matches the platform's.
The head unit was never re-keyed. (at least for my 1.7.0 firmware) It is a Freescale/NXP i.MX6 Android BSP build, and the OEM shipped it signed with the BSP's public test key instead of a private release key — so signing an app with that same public key is enough to run as system. The key is self-signed:
Subject : C=US, O=Android, CN=Android, emailAddress=android@freescale.com Valid : 2011-07-14 -> 2038-11-29 SHA-256 : 7F:BA:E8:17:B7:DB:24:64:2D:89:59:C3:47:B5:61:C0:03:C6:CF:DE:2C:B1:0F:90:9C:93:2F:21:A4:D6:D8:04
Because that key is public, anyone can sign a system-privileged app for this unit — an OEM security lapse, and exactly what makes this project possible. NXP reused the same key across every i.MX BSP for about a decade, so it is easy to find.
It lives in the i.MX device tree under common/security/: platform.pk8 (private key) and platform.x509.pem (certificate). A public mirror:
https://github.com/Avnet/android-imx-device-fsl/tree/maaxboard_android_p9.0.0_1.0.0/common/security
Check you have the right one — the certificate must match the fingerprint above:
openssl x509 -in platform.x509.pem -noout -fingerprint -sha256
Turn the BSP key pair into a keystore Gradle can use (platform.pk8 is DER PKCS#8):
openssl pkcs8 -inform DER -nocrypt -in platform.pk8 -out platform.key.pem openssl pkcs12 -export -in platform.x509.pem -inkey platform.key.pem -name platform -out platform.p12 -passout pass:android
Point the app's signingConfig at platform.p12 (store & key password android, alias platform), then build and install:
./gradlew :app:assembleDebug adb -s <HEAD-UNIT-IP>:5555 install -r app/build/outputs/apk/debug/app-debug.apk
install -r replaces in place and fails safely on a signature mismatch (the old app stays), so it is safe to try. Alternatively, put the same key at ~/.android/debug.keystore so ordinary debug builds are platform-signed without a signingConfig.
Vehicle data and commands go through two OEM bound services (bind by Intent action + package —
they are not in ServiceManager, so a shell service call can't reach them):
| Service | Bind (action / package) | Interface | Use |
|---|---|---|---|
| Canbus | action.adayo.CANBUSSERVICE / com.adayo.canbus | ICanbusService | getInt/getFloat/getIntArray(key) to read, sendData(action,val) to command — the signal keys |
| CarManager | action.adayo.CARSERVICE / com.adayo.carmanager | ICarService | audio / EQ, radio, mcu_writeDataToMcu (raw MCU frames) |
Carry the AIDL interface, or call via a raw Binder transact(). Sketch (Canbus):
bindService(new Intent("action.adayo.CANBUSSERVICE").setPackage("com.adayo.canbus"),
conn, BIND_AUTO_CREATE);
// in onServiceConnected:
ICanbusService s = ICanbusService.Stub.asInterface(binder);
int soc = s.getInt(3457); // pack-1 SoC %
float v = s.getFloat(3455); // pack-1 voltage V
s.sendData(103, 1); // A/C ON (⚠ actuates the car)
To try keys without writing an app, OpenAiways Telemetry has a built-in
probe (it already binds ICanbusService) — read or command any key straight from adb.
See CAN bus → Probing signals live.
Not a head-unit app — a standalone ESP32-S3 you wire into the car, for people who don't want to (or can't) get onto the head unit at all. One board does four things at once:
A prebuilt image and a browser-based flasher (no toolchain needed) are on its own page:
https://codeberg.org/noonscoomo/openaiways/src/branch/main/esp32-companion
🤖 AI-assisted development — reviewed before trusting it on a real vehicle, same disclaimer as the apps above.