Skip to content

Android Auto

monikapurpl3 edited this page Aug 21, 2026 · 1 revision

Android Auto

A deliberately tiny car surface, built with the Android for Cars App Library (native Kotlin, in the same APK): one AC per screen, and nothing on it but a huge power button, with the unit's name in the header above it. Big ▲ / ▼ actions step between units, wrapping around.

Everything else — temperature, mode, fan, programs — stays on the phone, on purpose: you should be able to hit this without reading it.

The button is colour-coded (green running, red stopped) and labelled ON / OFF, or Not reachable if the unit is offline. Taps respond immediately: the state flips optimistically while the command flies, and reconciles when the real state lands.

How it authenticates

It does not, itself. The car screen reads the same cached unit list and state the home-screen widgets use, and fires the same headless background callback to control a unit — so it reuses the phone's Ed25519 signing and Keystore-held credentials rather than shipping a second API client and a copy of the crypto into the car process.

Consequence: open the phone app once after installing, so there is something cached to show.

Enabling it

Android Auto only loads template apps in a handful of categories, and anything outside navigation and media needs Google review to be distributed through Play. For a self-hosted APK you enable it yourself:

  1. In Android Auto settings on the phone, tap Version about ten times to unlock Developer settings.
  2. In the ⋮ menu → Developer settings, enable Unknown sources.
  3. Reconnect to the car, or the Desktop Head Unit — Breeze appears in the launcher, and Auto posts a "new app" notification the first time it sees it.

The gotcha that cost a whole evening

The service declares two categories: androidx.car.app.category.IOT — the semantically correct home for "control a device at my house", and what Google's own IoT guide pairs with the GridTemplate used here — and POI.

Two things went wrong in sequence, and neither said anything in any log:

  1. IOT alone is not enough. It is an Android Automotive OS category; Android Auto (phone projection) does not list an app that only declares it. Nothing on the phone side could have helped — enabling Unknown sources was irrelevant. POI is the only Auto-supported templated category that permits these templates without the navigation-template permission, so 2.1.2 added it.
  2. Declaring them as two separate <intent-filter> blocks still failed. Through 2.2.0 the app remained invisible in the car launcher on a phone with developer mode and unknown sources enabled. Two filters resolve perfectly well in PackageManager — both category queries return the service — but the host reads the categories off the first ResolveInfo it gets and sees only one of them. One filter carrying both categories (2.2.1) and Auto picked it up immediately.

If you are writing a car app and it is invisible with no error anywhere, check that first.

POI is a stretch semantically and would be rejected in Play review; for a self-hosted APK it is the pragmatic answer. If this ever goes to Play, drop POI and ship the car surface for Automotive OS only.

Host validation

Release builds accept any host (HostValidator.ALLOW_ALL_HOSTS_VALIDATOR). Validating against the library's sample allowlist is right for a Play app and wrong for a sideloaded one: the Desktop Head Unit is not a signed host, so it makes the car surface untestable, and a mismatch fails silently with nothing in the log. What a rogue host gets here is the ability to toggle an air conditioner.

Notes

Because the host renders the templates, it — not the app — decides exact sizes and where the ▲/▼ actions sit. "Huge" means the largest primitive the library offers, a single-item grid.

Verified against the Desktop Head Unit on Android Auto 17.3: discovered, listed, bound, screen rendered. If it misbehaves, the car classes log under the tag BreezeCaradb logcat -s BreezeCar shows the service being created, the session opening, and each template build with its unit count.

Clone this wiki locally