Android apps, remote tooling, and field research for Denza / BYD head units.
A working lab for Denza / BYD head-unit apps, remote access, and the reverse-engineering notes behind them. Product code, infrastructure, and experiments live in separate parts of the repository.
Important
Compatibility and responsibility. All research, development, and live validation in this repository has been performed exclusively on the author's Denza Z9GT, using the particular firmware builds installed at the time of each test. Compatibility with other vehicles, model years, regional configurations, head-unit revisions, or firmware versions has not been established and is not guaranteed. A vehicle or firmware update may change or disable previously verified behavior.
Denza Lab is an independent hobby and research project, not an official Denza or BYD product. The software interacts with undocumented vehicle services and is provided without warranties of functionality, compatibility, reliability, or safety. You are solely responsible for evaluating the risks, keeping a recovery path available, and using the software only when it is safe to do so. Use this project and its applications entirely at your own risk.
| Lifecycle | Component | Purpose |
|---|---|---|
| Active | apps/car-adb-gateway/ |
Generic, relay-only remote ADB gateway with one trusted computer and a self-healing Android service. |
| Active | apps/denza-apps/ |
Simulcast, side-camera mirrors, navigation and HUD guidance, Shortcuts default-app roles, explicit stock split-screen sessions, trip/spectrum display, native weather adaptation, and passenger-screen app installation. |
| Legacy | legacy/denza-mirrors/ |
Frozen hardware-verified camera reference. Its working behavior has moved into Denza Apps and it is no longer in the root Gradle build. |
| Legacy | legacy/denza-gateway/ |
Original LAN-only SSH-to-ADB gateway. Kept for maintenance and reference; superseded for new remote-access work. |
| Library | libraries/dishare-bridge/ |
Shared raw DiShare binder integration used by Denza Apps. |
| Platform | platform/cli/, platform/relay/, ops/ansible/ |
Developer CLI, restricted relay control plane, and reproducible server provisioning. |
The rest of the tree is straightforward:
docs/— durable architecture, decisions, and verified findings;experiments/— isolated on-device probes included in the root build;tools/— host-side probes and live-car utilities;research/— parked or non-built experiments;reverse/— ignored local workspace for extracted artifacts and captures.
flowchart LR
Dev["Developer computer"] --> CLI["cag CLI"]
CLI --> Relay["Restricted SSH relay"]
Relay --> CAG["Car ADB Gateway"]
CAG --> ADB["Head-unit ADB"]
Apps["Denza Apps"] --> Bridge["DiShare bridge"]
Bridge --> Car["Denza / BYD services"]
Apps --> Scene["Shared instrument scene"]
Scene --> Cluster["Map base + camera overlay"]
Apps --> Guidance["HUD guidance"]
Guidance --> HUD["Windshield projection"]
Apps --> FSE["Passenger-screen installer"]
FSE --> Passenger["Passenger FSE"]
Car ADB Gateway never exposes ADB or SSH on the vehicle network. The vehicle opens the outbound relay connection, while the developer's SSH connection stays end-to-end encrypted to the Android app. See the architecture and decision log for the security model.
The repository is called Denza Lab. Older checkouts may still use the
historical directory name denza-gateway.
Active apps live under apps/, shared code under libraries/, and frozen
products under legacy/. The migrated Mirrors and navigation paths now live in
Denza Apps behind one instrument-display resolver and scene. The standalone
Denza Mirrors source is retained only as reference and is not a root Gradle
module.
The repository shape is:
apps/
car-adb-gateway/
denza-apps/
libraries/
dishare-bridge/
platform/
cli/
relay/
ops/
experiments/
legacy/
denza-gateway/
denza-mirrors/
docs/ research/ tools/
Requirements:
- JDK 17;
- Android SDK platform 37;
- Android Platform Tools;
- Go for the
cagCLI.
On a Homebrew-based macOS setup:
export JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home
export ANDROID_HOME=/opt/homebrew/share/android-commandlinetools
./gradlew :car-adb-gateway:testDebugUnitTest :car-adb-gateway:assembleDebug
./gradlew :denza-apps:testDebugUnitTest :denza-apps:assembleDebugBuild the legacy gateway only when working on it:
./gradlew :denza-gateway:testDebugUnitTest :denza-gateway:assembleDebugBuild and test the developer CLI:
cd platform/cli
go test ./...
go build -o cag ./cmd/cagGit ignores generated APKs and reverse-engineered binaries. Keep them out of the repository.
After an administrator enrolls the vehicle, a person at the head unit can issue a ten-minute pairing code for one trusted computer:
cag pair XXXX-XXXX
cag connect -- adb devices
cag connect -- adb shell
cag status
cag disconnectThe relay must be deployed through ops/ansible/, not by
copying scripts manually. This keeps SSH, PAM, account, filesystem, and
verification rules consistent.
Car ADB Gateway uses two different short-lived codes:
| Code | Created by | Used for | Lifetime |
|---|---|---|---|
| Enrollment code | Relay administrator | Register one fresh APK installation with the relay. | 60 minutes |
| Pairing code | A person at the enrolled head unit | Connect or replace the one trusted computer. | 10 minutes |
Share either code only with the person who is setting up or operating the vehicle. A pairing code grants full remote ADB access after the vehicle confirms the computer.
Build from the repository root, then use the serial shown by adb devices -l:
./gradlew :car-adb-gateway:assembleDebug
adb devices -l
adb -s <serial> install -r apps/car-adb-gateway/build/outputs/apk/debug/car-adb-gateway.apk
adb -s <serial> shell am start -n ru.adbgw.gateway/.MainActivityKeep -r when updating an enrolled installation so its private keys and relay
identity remain intact. Uninstalling the APK removes that state and requires a
new enrollment code.
- Open Car ADB Gateway on the head unit.
- If Android shows an ADB authorization dialog, approve the app key and press «Проверить снова».
- Ask the relay administrator for a current enrollment code. Administrators
create it using the procedure in
ops/ansible/README.md. - Enter the code, press «Подключить автомобиль», read the full-access warning, and confirm.
- Wait until the main screen reports that the vehicle is connected to the relay. The «Поддержка» dialog shows the relay, device ID, ADB endpoint, connection state, and recent events when diagnosis is needed.
The computer needs the cag CLI, OpenSSH, and Android Platform Tools on PATH.
Build/install instructions are in platform/cli/README.md.
-
On the head unit, press «Подключить компьютер» or «Заменить компьютер».
-
Read the warning and press «Показать код».
-
On the trusted computer, run the displayed command before the code expires:
cag pair XXXX-XXXX
-
Wait for
Paired with .... A successful replacement revokes the previous computer. If replacement fails or expires, the previous computer keeps its access.
cag opens the protected tunnel and selects the correct smart-socket or raw-ADB
mode automatically:
cag connect -- adb devices
cag connect -- adb shell
cag statusUse the same prefix for other ADB commands, for example:
cag connect -- adb shell getprop ro.build.version.release
cag connect -- adb logcatSome raw-ADB head units show a second Android authorization dialog for the computer's normal ADB key. Approve it at the vehicle before retrying the command.
cag disconnectcloses only this computer's local tunnel. It does not disable remote access on the vehicle.- «Отключить удалённый доступ» in the vehicle app closes current sessions, disables the relay grant, and keeps access disabled after reboot.
- «Включить удалённый доступ» in the vehicle app re-enables it.
no car is paired— create a fresh pairing code at the vehicle and runcag pair.- Pairing code rejected — create another code; pairing codes expire after ten minutes.
- Enrollment code rejected — ask the relay administrator for another code; enrollment codes expire after 60 minutes and are single-use.
- Vehicle is reconnecting — check network access and the «Поддержка» events; transient network, relay, and ADB failures are retried automatically.
- App was force-stopped — open Car ADB Gateway manually. Android does not allow an ordinary APK to restart itself after Force Stop.
- Identity or host-key error — stop and inspect the support details. Resolve the mismatch instead of bypassing the check.
- Project map — component boundaries, lifecycle, and build outputs.
- Repository governance — active, migration, legacy, and research rules.
- Docs index — ownership of durable knowledge.
- Car ADB Gateway architecture — normative relay-only design.
- Car ADB Gateway decisions — ADR-lite rationale and evidence.
- Instrument-display findings — display selection, Mirrors, navigation, evidence, and limitations.
- ADB authorization recovery — passive startup gate, one-shot authorization, and stuck-queue boundary.
- DiShare API notes — Simulcast and HUD reverse-engineering notes.
- FSE app installation — verified SMB and cross-device path for passenger-screen APKs.
- Split-screen findings — explicit one-package picker flow, live acceptance, and retired routes.
- Vehicle-data findings — usable GNSS/IMU inputs and blocked BYD/CAN surfaces.
- Audio-capture findings — output-mix spectrum source and product boundaries.
- Weather-adapter findings — native provider contract and MET Norway adapter.
- Shortcuts automation findings — stock automation limits and the default navigation/music/video role bridge.
- CarPlay findings — hardware/software evidence and unsupported routes.
When a page disagrees with the code, manifest, or Gradle configuration, follow the implementation and update that page.
The original work in this repository is released under the MIT License — use, modify, and redistribute it freely, including commercially, as long as the copyright notice and the license text travel with it.
The license covers this project's own source, documentation, and tooling. It does not grant rights to third-party material this repository merely describes or analyzes: Denza and BYD firmware, their applications, and their trademarks remain the property of their owners. Nothing here is redistributed from them.