Everything written this session that has not been through the device: rounds 43 to 48 (each patch adversarially verified, 43's transient-notice expectation fixed), a partial round 49, the pipeline scripts, the Android Auto design and the icon comparison mock. Also the two harness changes that were never A/B'd — adaptive waits, which may only shorten a declared wait and never lengthen it, and a ship that runs the mock walk and the deploy at the same time. The A/B and its 255-step baseline are in handoff/tools; main stays at what the phone has actually walked.
73 lines
3.9 KiB
Plaintext
73 lines
3.9 KiB
Plaintext
### 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 `<queries>`, 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 `<uses name="media"/>` 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.
|