### The Auto prober ```bash bash tools/auto-prober/probe.sh # depth 2, the ordinary root bash tools/auto-prober/probe.sh 3 true # depth 3, the RECENT root adb uninstall com.soulstream.autoprober # and it is gone ``` Android Auto reaches a media app through `MediaBrowserService`, with the app's process **cold** — no Activity, no WebView, and therefore no DevTools socket. `smoke.mjs` drives the phone over CDP, so with SoulStream force-stopped it has nothing to attach to and is blind to every bit of this. `tools/auto-prober/` is a throwaway app that binds the same way Auto does, with Auto's own root hints, walks whatever tree comes back, and prints one JSON object per event under the logcat tag `SoulAutoProbe`. It is framework-only (`android.media.browse.MediaBrowser`, not androidx, not media3) because that is the path Auto itself takes into a media3 app, and a prober built out of the library it is probing would not prove that path works. It lives outside `src-tauri/gen/android/` deliberately — `npx tauri android init` would delete it — and borrows the generated project's Gradle wrapper rather than shipping one, which pins Gradle 8.14.3, AGP 8.11.0, Kotlin 1.9.25 and JDK 17: what is already in the cache, so a run downloads nothing. `probe.sh` builds, installs, clears logcat, force-stops **SoulStream** (only; the walk already does the same), starts the prober by name, waits, and prints the log. No permissions beyond ``, no network, no storage, no transport command; every exit path disconnects and `onDestroy` disconnects again. The events are `start`, `desc`, `filter`, `root`, `children`, `fail`, `done`. The number it exists for is `root`: **milliseconds from `connect()` to `onConnected()` with the process cold**, next to the root media id and whether a session token came with it. Auto gives a media app a few seconds and then drops it, which is why `onGetLibraryRoot` answers with `Futures.immediateFuture` and nothing else — `MediaLibraryServiceLegacyStub` blocks the binder thread on that future with no timeout. `desc` is the half the walk cannot do. `adb shell pm dump` is `dumpsys package`: resolver tables, permissions, package state, and **no application meta-data at all** — so `auto-browse` and `auto-library` can prove the intent filters and nothing else. The prober resolves `com.google.android.gms.car.application` through `PackageManager.GET_META_DATA` and then opens that resource out of the app's own `Resources` and reads the `` inside it, which is the only way to tell a real declaration from a dangling id short of unpacking the APK. The walk echoes the last such line back as the advisory `auto-desc`. **androidx/media #3158.** Open since 2026-04-03, still open, reproduced on media3 1.11.0: a host that binds a `MediaLibraryService` while nothing is playing can put the service into an `onCreate → onBind → onGetSession → onConnect → onDestroy` loop at about thirty cycles a second. It is 100% reproducible after a force-stop with no existing binding, which is exactly what this prober does — so it shows up here before it ever shows up in a car. `MediaHub` holds the session, the player, the notification provider, the audio-focus request and the becoming-noisy receiver for the life of the **process**, so a cycle allocates and registers nothing and the loop is survivable; the loop itself is Android Auto's to fix and media3 has said it will not act first. The counter is the instrument: ```bash adb logcat -d -s SoulStream:I | grep "auto service created" | tail -1 ``` One or two is normal. A number climbing while a probe or a car is connected is the bug, and it is worth watching across a LONG bind rather than a short one — the failure mode this guards against is an OOM hours in, not a crash at connect. **None of this proves the car screen.** The Desktop Head Unit or a real head unit is the only thing that shows what Auto draws, how it groups a tree, or whether a tap plays. This proves the app answers.