## The tree in the car Android Auto browses through a `MediaLibraryService`, and the system starts that service in a process the app has never been in: no Activity, no Tauri, no WebView, and therefore no frontend, no `#[tauri::command]` and no resolver. Everything on a car screen has to come from somewhere else. It comes from `src-tauri/src/auto.rs`, which opens `library.db` **read-only** and answers with its own SQL, and from `crates/tauri-plugin-media-session/…/LibraryBridge.kt`, which loads the native library, hands the JSON across and turns it into `MediaItem`s. Cold start works because of three things that were already true. `JNI_OnLoad` (`android_glue.rs`) runs at `System.loadLibrary("app_lib")`, before any component exists, and publishes the JavaVM and the app Context; `crates/library` is plain rusqlite with no Tauri in it; `crates/player` is plain rodio with no Tauri in it. So a service-started process can load the `.so`, open the database and — in a later round — drive the engine. `LibraryBridge` is what calls `System.loadLibrary` there, because the app's only other loader (`com.soulstream.app.Rust`) needs an Activity to exist. **The connection is read-only and that is not a detail.** `Library::open` runs `PRAGMA journal_mode = WAL` and eleven `CREATE TABLE IF NOT EXISTS` before it returns, which are writes into a database whose real owner may be running in another process at that moment. `auto.rs` uses `Connection::open_with_flags(…, SQLITE_OPEN_READ_ONLY)` with a 1.5-second busy timeout, and its statements are copied from `crates/library` rather than invented — including `album_tracks`'s disc ordering and `recent_albums`'s `link` CTE. **Artists are grouped in Rust the way the phone groups them in JS.** There is no artists table; `src/mobile/lib.svelte.js` derives artists by folding `list_albums()` in its own order, keeping the FIRST casing it sees. A SQL `GROUP BY artist` would pick a different display name and the car would disagree with the phone in the same hand. The node grammar, which is the contract the rounds after this one read: | node | | | --- | --- | | `ss:root[?limit=N]` | the tabs; `N` is the browser's own root-children hint | | `ss:tab:recent` / `albums` / `artists` / `playlists` | browsable | | `ss:album:` | browsable → tracks | | `ss:artist:` | browsable → albums | | `ss:playlist:` | browsable → rows | | `ss:track:` | playable; `tracks.path` | | `ss:plitem:` | playable; `playlist_items.path`, **which may be NULL** | A playlist row of kind `stream` or `unresolved` has no file and is listed and marked playable anyway, because the phone lists it: the round that plays these owes them `STATE_ERROR` and "open SoulStream on your phone" rather than a hole in the list. Catalog and Soulseek content cannot play from cold at all — the resolver is 872 lines of shared JS and Soulseek needs a live session — and that is the same answer. **Every failure is an empty but valid tree.** No config, no database, a storage grant revoked since the last launch, a corrupt page: each of them answers an empty list with the reason in the envelope's `note`. `ss:root` is answered without opening anything at all, so the four tabs are there on a phone that has never had a library. A head unit that spins forever is worse than one that says the library is empty, and a driver cannot dismiss a dialog. **Covers go out as `content://`, never as bytes.** Auto supports that scheme and `android.resource://` and no other, and `MediaLibraryServiceLegacyStub` will decode `MediaMetadata.artworkData` into a Bitmap for every item it converts — a few hundred of those is over Binder's 1 MB limit and the whole browse fails. So `auto.rs` picks the file (the warmed 360 px thumbnail from `library_art_prewarm`'s cache when it is there, the master otherwise) and `LibraryBridge` mints the URI through the app's FileProvider. That provider needed new roots: see `src-tauri/android/res/xml/file_paths.xml`, and note that `files-path` is the wrong tag — it maps `getFilesDir()`, and the library lives one level up under `getDataDir()`. **And media3 grants nothing.** There is no `grantUriPermission` anywhere in `MediaSessionImpl`, `MediaSessionLegacyStub`, `MediaLibraryServiceLegacyStub` or `LegacyConversions` at 1.10.1, and the app's provider is `android:exported="false"` — so `LibraryBridge` grants each URI to the browser's own package by hand. Forget that line and the car browses perfectly and shows a grid of grey squares, with nothing in any log. The phone walk's six `auto-*` checks run **with the app force-stopped**, which is the entire claim, and they read `LibraryBridge`'s own `auto node=… n=… … note=…` line out of logcat — the only place on a device with no screen where any of this can be read back.