t66: app names on a device that exposes none
The reported Shield names no app: `pm dump com.netflix.ninja` is ~2241 lines of
dumpsys and grep for `application-label` over the whole capture finds nothing.
The probe shipped `| head -300`, which cut the dump BEFORE the package section,
so "no label" was a truncation artefact; the sweep then named every app after
its own package id, and the apps editor pre-filled the `pkg=pkg` row the user
read as a bug.
* the probe greps ON THE DEVICE for the three shapes Android prints
(`application-label`, `nonLocalizedLabel`, `labelRes`); a quoted value is a
name, `null` and a resource id are not. The dump is never truncated.
* after APP_LABEL_PROBE_LIMIT consecutive "ran, no label" answers the device
is taken at its word: the sweep stops spending a shell per package
(APP_LABEL_TTL expiry re-checks, and a package whose probe FAILED still owes
its retry — t57 UNIT 0 survives, driven by the app on screen).
* ONE precedence chain in `ha_adb_common/app_label.resolve_package_name`, used
by the media player, the Current App sensor, the discovery table, the source
list and the apps editor: user map > device label > curated map > package id
(the id shown ONCE, `name` empty when nothing names the app).
* the curated table covers the user's 21 apps (community-maintained, not
device truth) and the Configure row says when the device named nothing.
* the real device shape is a test fixture (trimmed, and marked as such in the
file: the raw capture is not in the repo) pinning that no label is
extracted, that the probe count is bounded, and that `Netflix` surfaces.
Also drops the dead `ATTR_MEDIA_DAG_NOW_PLAYING` re-exports left by the t65
flattening and updates the RESEARCH_SHIELD now-playing note to the flat names.
No real Shield was available: the fetch/burst behaviour is exercised against the
documented Android output and the trimmed reproduction of the reported capture.
This commit is contained in:
1 parent
f29f62ddae
commit
7b32de789a
16 files changed
+973
-176
No files matched your search
@@ -244,7 +244,7 @@ What each retired piece used to provide, and what covers it now:
|
||||
| Retired piece | Covered by |
|
||||
|---------------|------------|
|
||||
| `androidtv` playback state | the ADB media-session state ladder (`playing` / `paused` / `buffering` / `idle` / `off`, plus `on` only when no source reported a power or playback fact) |
|
||||
| `androidtv` app name / `app_id` lookups | the manager's store + the app-name map + the manifest label of the app that is currently on screen (fetched on demand; a failed fetch is retried on the next poll instead of being cached as a permanent miss) |
|
||||
| `androidtv` app name / `app_id` lookups | the manager's store + the one app-name chain: your apps map, then the device's own label for the app on screen (fetched on demand; a failed fetch is retried on the next poll instead of being cached as a permanent miss), then the built-in app names, then the package id |
|
||||
| `androidtv_remote` power, volume and key commands | the protocol transport: the `remote` entity, `media_player` volume, `KEYCODE_*` commands |
|
||||
| `androidtv_remote` availability | the availability contract (protocol `is_on`, ADB awake, power switch) |
|
||||
| `universal` media player templates | the media player entity's own attributes plus the `media_dag_*` attributes |
|
||||
@@ -391,11 +391,16 @@ The **Apps** row opens a small editor rather than one long form. Home Assistant
|
||||
*Edit* and *Remove* are offered only while there is something to act on: a table with
|
||||
no remembered rows and no discovery yet shows just the text field and *Add an app*. The
|
||||
editor's description lists every app it knows — the rows you saved, and the ones
|
||||
discovery found, marked `[detected]`. **Edit** acts on both (that is the point: the
|
||||
Shield exposes no app labels of its own, so a detected app reads as `name (package id)`
|
||||
or as `package id (no name yet)`, and picking it is how you give it a name — saving
|
||||
stores it as one of your own rows). **Remove** acts only on rows you saved, because a
|
||||
detected app has no line to delete (it would simply come back with the next sweep).
|
||||
discovery found, marked `[detected]`. **Edit** acts on both (that is the point: an app
|
||||
the device does not name reads as `Your Name (package id)` when the map or the built-in
|
||||
app names know it, and as `package id (no name yet)` when nothing does — picking it is how
|
||||
you give it a name, and saving stores it as one of your own rows). **Remove** acts only on
|
||||
rows you saved, because a detected app has no line to delete (it would simply come back
|
||||
with the next sweep).
|
||||
|
||||
A discovered app that nothing can name is **never pre-filled into the text field** as
|
||||
`package=package`: a row like that would store the package id as if it were a name. It is
|
||||
offered in the row editor instead, where "no name yet" says what it is.
|
||||
Removing a row never uninstalls anything from the Shield — the app simply stops being
|
||||
offered. Rows you do not touch are left exactly as they were.
|
||||
|
||||
@@ -508,10 +513,10 @@ outcome that leaves you guessing.
|
||||
### App discovery (what is actually installed)
|
||||
|
||||
The Configure page can ask the Shield which apps it has, so the **Apps** table does not
|
||||
start from a four-entry guess. Pick **Discover installed apps** on the Configure menu and
|
||||
the table is rebuilt from two ADB queries — and the row then **says what happened** (see
|
||||
*What the Discover row tells you* below), because an action that appears to do nothing is
|
||||
indistinguishable from a broken integration:
|
||||
start from the built-in app names alone. Pick **Discover installed apps** on the Configure
|
||||
menu and the table is rebuilt from two ADB queries — and the row then **says what happened**
|
||||
(see *What the Discover row tells you* below), because an action that appears to do nothing
|
||||
is indistinguishable from a broken integration:
|
||||
|
||||
| Command | What it returns | How it is treated |
|
||||
| --- | --- | --- |
|
||||
@@ -529,15 +534,44 @@ capture: no real Shield was available when this was written, so
|
||||
fixtures — the same "inferred, not observed" mark the state=6 buffering value
|
||||
carries in the source ladder above.)*
|
||||
|
||||
Names come from the same cached path the Current App sensor uses: a `pm dump` per
|
||||
package, parsed for `application-label`, cached for 24 h. A full sweep runs the dumps
|
||||
in a small burst (4 at a time, 50 ms apart), which finishes a typical Shield in
|
||||
seconds instead of the minutes a strictly serialised sweep would take; the commands
|
||||
share the watchdog's own ADB connection, and `adb-shell` serialises the I/O on it, so
|
||||
the burst queues politely instead of interleaving on the socket. A label fetch
|
||||
that **fails is not cached**, so that app shows its package id in this table and the
|
||||
next sweep tries again — this is the same rule as the entity path, and it is why a
|
||||
transient ADB hiccup no longer sticks.
|
||||
Names come from the same cached path the Current App sensor uses, and there is ONE
|
||||
chain behind every surface — this table, `media_player.app_name`/`source`, the Current
|
||||
App sensor and the Apps editor. In order, the first answer wins:
|
||||
|
||||
1. **your Apps map** — your own edit always wins;
|
||||
2. **the device's own label** for that app, read from `pm dump <package>`: the probe
|
||||
greps **on the device** for the label shapes Android prints — `application-label:`,
|
||||
`nonLocalizedLabel=` and `labelRes=` — so only those lines cross ADB (a real dump on
|
||||
one reported Shield is ~2241 lines of dumpsys, and the old `head -300` cut it before
|
||||
the package section even began, which is why it looked like "no labels");
|
||||
a quoted value counts, `null` and a resource id like `0x7f0f0d31` do not;
|
||||
3. **the built-in app names** (see *Sources* below) — a curated, community-maintained
|
||||
`package → name` table;
|
||||
4. **the package id**, shown **once** (never as `pkg=pkg` in a row).
|
||||
|
||||
A label probe is cached for 24 h, and its outcome is remembered either way: a probe that
|
||||
**ran** and found nothing is a definitive negative (zero further round trips for that
|
||||
app), while a probe that **could not run** is not cached at all and is retried on the next
|
||||
poll — this is the same rule as the entity path, and it is why a transient ADB hiccup no
|
||||
longer sticks.
|
||||
|
||||
A full sweep runs a handful of probes at a time (4 at a time, 50 ms apart), which
|
||||
finishes a typical Shield in seconds instead of the minutes a strictly serialised sweep
|
||||
would take; the commands share the watchdog's own ADB connection, and `adb-shell`
|
||||
serialises the I/O on it, so the burst queues politely instead of interleaving on the
|
||||
socket.
|
||||
|
||||
**Some devices expose no labels at all.** The reported Shield is one: nothing in that
|
||||
2241-line dump names any app. So after 5 probes in a row come back "ran, no label" the
|
||||
integration stops spending a shell per package for the rest of the 24 h TTL: the sweep
|
||||
answers the remaining packages from the map instead of probing them, names come from
|
||||
tiers 1, 3 and 4, and a later device change or update is re-checked when the TTL
|
||||
expires. The Configure menu says so in its verdict when it happens, and the table's
|
||||
`name` is then your map's name, the built-in name, or **empty** — an app nothing can name
|
||||
keeps the id in `package` alone rather than inventing a name out of it.
|
||||
|
||||
An app that nothing names is still offered by the **Apps** row editor (it reads
|
||||
"`com.example.app` (no name yet)"), which is where you give it one.
|
||||
|
||||
Icons are **best effort and explicitly heuristic**: `pm dump` does not expose an app's
|
||||
icon resource (an extracted APK's icon is a bitmap only its own code can resolve), so
|
||||
@@ -563,6 +597,7 @@ one-line verdict, which replaces the usual "not discovered yet" note:
|
||||
| --- | --- |
|
||||
| `N apps found on the Shield — the table is updated.` | the sweep ran, and the editor's text field now carries the discovered rows |
|
||||
| `the Shield reported no installed apps — the table keeps your saved rows.` | the sweep ran and found nothing to add; your rows are untouched |
|
||||
| `N apps found — this Shield exposes no app labels over ADB, so names come from the built-in app names and your own rows.` | the sweep ran, and the device itself named nothing: the per-package probes have stopped (see *App discovery* above) |
|
||||
| `the discovery sweep could not reach the Shield — check the ADB connection; your saved rows are unchanged.` | the sweep failed; nothing was cached as a result and nothing you wrote was lost |
|
||||
| `ADB is not set up for this entry — nothing could be discovered.` | this entry has no ADB manager (a remote-protocol-only setup), so there is nothing to sweep |
|
||||
|
||||
@@ -588,11 +623,20 @@ sweep could not run), while the Configure row forces one.
|
||||
### Sources (the app list in a dropdown)
|
||||
|
||||
The media player's source list is your **Apps** map — the same map app-name resolution
|
||||
uses. With no map configured it falls back to a small built-in list
|
||||
(TiviMate, Wholphin, Netflix, YouTube), so a universal-media-player card always has
|
||||
something to show. Discovery fills the **Configure** table, not this fallback: the
|
||||
dropdown stays exactly the map you saved, so what a card offers is always what you
|
||||
chose. An app that discovery found and you want in the dropdown is one row to add.
|
||||
uses. With no map configured it falls back to the built-in app names, which on a device
|
||||
that exposes no labels *is* the naming: the table covers the apps the reported Shield has
|
||||
(Projectivy Launcher, Samba (SMB), X-plore, Plex Media Server, TiviMate, Hyperion Grabber,
|
||||
Prime Video, MediaSession2MQTT, Speech Services by Google, Button Mapper, Wholphin,
|
||||
Findroid, Pluto TV, YouTube, Downloader, TV Quick Actions, Argosy, Netflix, RetroArch and
|
||||
the Google TV launcher, plus Google's speech services), so a card shows
|
||||
"Netflix" instead of `com.netflix.ninja` with nothing configured at all.
|
||||
|
||||
That table is a **community-maintained convenience, not device truth**: package ids are
|
||||
stable, but display names change with vendors and app updates, and an app it has never
|
||||
heard of falls through to its package id. Your own map overrides any row in it — one line
|
||||
in **Apps** — and discovery fills the **Configure** table, not this fallback, so the
|
||||
dropdown stays exactly what you saved. An app that discovery found and you want in the
|
||||
dropdown is one row to add.
|
||||
|
||||
**Apps are sources, not browser media.** This integration no longer publishes the app
|
||||
directory through HA's media **Browser**: the Browse dialog shows an **empty tree** for
|
||||
@@ -610,9 +654,11 @@ entity: media_player.living_room_shield
|
||||
```
|
||||
|
||||
- **`source_list`** — the app names. They come from the map (in the order it lists
|
||||
them), so editing the map renames the dropdown entries.
|
||||
- **`source`** — the app currently on screen, resolved exactly like the Current App
|
||||
sensor: your map first, then the cached device label, then the raw package.
|
||||
them), so editing the map renames the dropdown entries; with no map, the built-in
|
||||
app names above.
|
||||
- **`source`** — the app currently on screen, resolved by the same chain the Current App
|
||||
sensor uses: your map first, then the cached device label, then the built-in app name,
|
||||
then the raw package id.
|
||||
- **`media_player.select_source`** — picks one **by name** and launches it over the
|
||||
protocol, the same path `play_media` uses for apps. A raw package id works too (that
|
||||
holds the package id), names match case-insensitively, and a name that
|
||||
@@ -640,7 +686,8 @@ The map is your own override, so two shapes are accepted rather than papered ove
|
||||
- A **padded value** typed by hand (`com.x= Netflix `) is shown cleaned in the
|
||||
dropdown, but `app_name`, `source` and the Current App sensor
|
||||
display the map value **verbatim** — so `source` can be a string that is not in
|
||||
`source_list`. A blank value falls through to the raw package instead.
|
||||
`source_list`. A blank value falls through the rest of the chain instead: the device
|
||||
label, then the built-in app name, then the raw package id.
|
||||
|
||||
### Remote command script
|
||||
|
||||
|
||||
Reference in new issue
Block a user