android auto: the tree under the root — four tabs read cold out of library.db
Round forty-nine put SoulStream in the car with an empty root. This fills it.
`onGetChildren` answers the four tabs Kyle picked — Recent · Albums · Artists ·
Saved — and everything under them: albums with their artist and track count,
artists grouped the way the app groups them, an album's tracks in disc and
track order, and every local file under Saved. `src-tauri/src/auto_browse.rs`
is one JNI export over `library.db`, and `AutoNative` in the plugin is the
Kotlin half.
**It reads read-only and it never calls `Library::open`.** That constructor
runs `PRAGMA journal_mode = WAL` and a `CREATE TABLE IF NOT EXISTS` batch —
writes, both of them — and a service that takes a write lock on the library to
draw a list is a bug waiting for a long drive. The path is derived the way
`config::download_dir` derives it, but without it: that function takes a
`tauri::AppHandle` and there is no Tauri in this process, so `config.json` is
parsed read-only for `download_dir` and the default is `<dataDir>/Library`,
with the database under `.soulstream/` as the portability rule requires.
**`AutoNative` calls `System.loadLibrary("app_lib")` itself, and that is the
round's other half.** When Auto binds a cold app there is no Activity, so
nothing has loaded the Rust side and none of it exists. Loading it here also
runs `JNI_OnLoad`, which publishes the JavaVM and the app Context to
`ndk-context` — the same hand-off the audio backend needs, arriving before
there is any audio, which is what round fifty-one stands on. `loadLibrary` is
idempotent, so it costs nothing when the app is already up.
Two things a head unit taught that no unit test would have. **The root must not
depend on the database.** The four tabs are a constant, and gating the whole
dispatch on a connection meant one unreadable library cost the car its entire
structure — it read as "Media isn't available for this list", which names
neither the tabs nor the reason. A library that cannot be read should cost the
ROWS, never the shape. And **`log::warn!` goes nowhere in a cold service
process**: nothing has run Tauri, so no `android_logger` is installed, and the
first failure here was completely silent. It writes to the platform log
directly now, which is the only reason the second failure had a cause attached.
`open_read_only` falls back to a read-write handle it immediately sets
`query_only` on. A read-only connection to a WAL-mode database needs the `-shm`
beside it and cannot create one, and the mode lives in the header, so it does
not go away when the sidecars do. This has NOT been observed to bite — the one
failure that looked like it was SELinux, below — but the fallback is cheap and
the connection still cannot change a row. `CREATE` is in neither flag set, so a
missing database stays an error rather than becoming an empty one.
Verified twice. The prober walks it cold on the phone AVD: 12 nodes, 139
leaves, 2.26 s, with the album tracklists in the right order and both artists
carrying the right albums. And it is on an Android Automotive head unit —
`ss_car`, API 34 — showing the four tabs, eleven Recent rows and five albums
with their counts, which is the first time any of this work has been seen
rendered by a car rather than described.
**Not done, and deliberately not guessed at.** There is no artwork: the plan
called for cover art through the existing FileProvider via a `files-path`
entry, but the library lives under the app's DATA dir and `files-path` maps to
`getFilesDir()`, which does not reach it — so the mechanism needs deciding, not
patching. And the rows are all lists; the grid Kyle chose for albums is a
content-style hint that is not set yet. Both are presentation over a tree that
is now correct, and both want the car in front of them.
This commit is contained in:
1 parent
e1bf456743
commit
515b3da71f
6 files changed
+603
No files matched your search
@@ -0,0 +1,59 @@
|
||||
android auto: the tree under the root — four tabs read cold out of library.db
|
||||
|
||||
Round forty-nine put SoulStream in the car with an empty root. This fills it.
|
||||
`onGetChildren` answers the four tabs Kyle picked — Recent · Albums · Artists ·
|
||||
Saved — and everything under them: albums with their artist and track count,
|
||||
artists grouped the way the app groups them, an album's tracks in disc and
|
||||
track order, and every local file under Saved. `src-tauri/src/auto_browse.rs`
|
||||
is one JNI export over `library.db`, and `AutoNative` in the plugin is the
|
||||
Kotlin half.
|
||||
|
||||
**It reads read-only and it never calls `Library::open`.** That constructor
|
||||
runs `PRAGMA journal_mode = WAL` and a `CREATE TABLE IF NOT EXISTS` batch —
|
||||
writes, both of them — and a service that takes a write lock on the library to
|
||||
draw a list is a bug waiting for a long drive. The path is derived the way
|
||||
`config::download_dir` derives it, but without it: that function takes a
|
||||
`tauri::AppHandle` and there is no Tauri in this process, so `config.json` is
|
||||
parsed read-only for `download_dir` and the default is `<dataDir>/Library`,
|
||||
with the database under `.soulstream/` as the portability rule requires.
|
||||
|
||||
**`AutoNative` calls `System.loadLibrary("app_lib")` itself, and that is the
|
||||
round's other half.** When Auto binds a cold app there is no Activity, so
|
||||
nothing has loaded the Rust side and none of it exists. Loading it here also
|
||||
runs `JNI_OnLoad`, which publishes the JavaVM and the app Context to
|
||||
`ndk-context` — the same hand-off the audio backend needs, arriving before
|
||||
there is any audio, which is what round fifty-one stands on. `loadLibrary` is
|
||||
idempotent, so it costs nothing when the app is already up.
|
||||
|
||||
Two things a head unit taught that no unit test would have. **The root must not
|
||||
depend on the database.** The four tabs are a constant, and gating the whole
|
||||
dispatch on a connection meant one unreadable library cost the car its entire
|
||||
structure — it read as "Media isn't available for this list", which names
|
||||
neither the tabs nor the reason. A library that cannot be read should cost the
|
||||
ROWS, never the shape. And **`log::warn!` goes nowhere in a cold service
|
||||
process**: nothing has run Tauri, so no `android_logger` is installed, and the
|
||||
first failure here was completely silent. It writes to the platform log
|
||||
directly now, which is the only reason the second failure had a cause attached.
|
||||
|
||||
`open_read_only` falls back to a read-write handle it immediately sets
|
||||
`query_only` on. A read-only connection to a WAL-mode database needs the `-shm`
|
||||
beside it and cannot create one, and the mode lives in the header, so it does
|
||||
not go away when the sidecars do. This has NOT been observed to bite — the one
|
||||
failure that looked like it was SELinux, below — but the fallback is cheap and
|
||||
the connection still cannot change a row. `CREATE` is in neither flag set, so a
|
||||
missing database stays an error rather than becoming an empty one.
|
||||
|
||||
Verified twice. The prober walks it cold on the phone AVD: 12 nodes, 139
|
||||
leaves, 2.26 s, with the album tracklists in the right order and both artists
|
||||
carrying the right albums. And it is on an Android Automotive head unit —
|
||||
`ss_car`, API 34 — showing the four tabs, eleven Recent rows and five albums
|
||||
with their counts, which is the first time any of this work has been seen
|
||||
rendered by a car rather than described.
|
||||
|
||||
**Not done, and deliberately not guessed at.** There is no artwork: the plan
|
||||
called for cover art through the existing FileProvider via a `files-path`
|
||||
entry, but the library lives under the app's DATA dir and `files-path` maps to
|
||||
`getFilesDir()`, which does not reach it — so the mechanism needs deciding, not
|
||||
patching. And the rows are all lists; the grid Kyle chose for albums is a
|
||||
content-style hint that is not set yet. Both are presentation over a tree that
|
||||
is now correct, and both want the car in front of them.
|
||||
Reference in new issue
Block a user