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.
65 lines
3.3 KiB
Plaintext
65 lines
3.3 KiB
Plaintext
/* ── Android Auto's front door, as the PackageManager resolved it ─────
|
|
* Two of the three fields FAIL the walk, and they are the only automated
|
|
* proof this project has that Auto can see the app at all.
|
|
*
|
|
* What they catch is the `TAURI_ANDROID_PROJECT_PATH` trap. The service,
|
|
* its intent filter and the `com.google.android.gms.car.application`
|
|
* meta-data all live in the media-session plugin's own library manifest;
|
|
* tauri-build only rewrites `gen/android/tauri.settings.gradle` when the
|
|
* CLI's variable names the generated project, and without it the whole
|
|
* module compiles fine and never reaches the APK. That has already cost
|
|
* this project a start-up `ClassNotFoundException` once. Read out of
|
|
* `pm dump` the filters are simply absent, which is a red line rather than
|
|
* a crash in a car.
|
|
*
|
|
* `pm dump <pkg>` is `dumpsys package <pkg>`: resolver tables, permissions
|
|
* and package state. It prints NO application meta-data, so the
|
|
* `automotive_app_desc` half cannot be asserted from here — `desc` is the
|
|
* prober's own `{"ev":"desc"}` line echoed back, advisory, and the
|
|
* meta-data's real proofs are aapt2 at link time (`@xml/automotive_app_desc`
|
|
* is a compile-time reference; a missing resource fails the build) and
|
|
* `tools/auto-prober/`, which resolves the int through
|
|
* `PackageManager.GET_META_DATA` and parses the XML out of the app's own
|
|
* Resources.
|
|
*
|
|
* `table` is in the JSON on purpose: a zero there says the Service Resolver
|
|
* Table was not where this looked — a format surprise, not a missing
|
|
* filter — which is the one thing that could turn these two into a false
|
|
* red. */
|
|
function autoManifest() {
|
|
const out = { browse: "", library: "", table: 0, desc: "no prober line" };
|
|
try {
|
|
const dump = adb("shell", "pm", "dump", "com.soulstream.app").toString();
|
|
const at = dump.indexOf("Service Resolver Table:");
|
|
/* the section, and no further: the tables are printed one after
|
|
another and an action name must not be found in the wrong one */
|
|
let table = "";
|
|
if (at >= 0) {
|
|
const rest = dump.slice(at, at + 200000);
|
|
const next = rest.indexOf("Resolver Table:", 23);
|
|
table = next < 0 ? rest : rest.slice(0, next);
|
|
}
|
|
out.table = table.length;
|
|
/* each action heads a block of the components that declare it; the
|
|
window keeps a match from wandering into the next action's */
|
|
const resolves = (action) => {
|
|
const i = table.indexOf(action + ":");
|
|
if (i < 0) return "no filter";
|
|
return /com\.soulstream\.mediasession\.MediaService/.test(table.slice(i, i + 600))
|
|
? "MediaService" : "elsewhere";
|
|
};
|
|
out.browse = resolves("android.media.browse.MediaBrowserService");
|
|
out.library = resolves("androidx.media3.session.MediaLibraryService");
|
|
} catch (e) {
|
|
out.browse = "ERR " + e.message;
|
|
out.library = "ERR " + e.message;
|
|
}
|
|
try {
|
|
const log = adb("logcat", "-d", "-s", "SoulAutoProbe:I").toString();
|
|
const lines = log.split(/\r?\n/).filter((l) => l.includes('"ev":"desc"'));
|
|
const m = (lines[lines.length - 1] || "").match(/\{.*\}$/);
|
|
if (m) out.desc = m[0];
|
|
} catch (e) { out.desc = "ERR " + e.message; }
|
|
return out;
|
|
}
|