fix(discovery): a cached negative is an answer, not a miss (t57)
The sweep asked `app_label()` whether a package still needed a label, and that accessor answers "what should be displayed" — so a definitive negative (a package whose pm dump ran and carried no label) came back as None, indistinguishable from "never asked". Every sweep therefore re-dumped every label-less package, throwing away the 24 h cache the label path exists for, and a second sweep cost a full dump round for the whole app list. `_known_label(package) -> (known, label)` is the accessor that keeps the two apart: known-and-empty means the device already answered, so the row falls back to its package id with ZERO further round trips. A forced re-sweep now issues only the two list commands. Pinned by tests: a second sweep dumps nothing at all (negatives included), and a package whose fetch FAILED (no output — the UNIT 0 rule) stays uncached and is retried by the next sweep, recovering its label.
This commit is contained in:
1 parent
364d4b7da3
commit
ff8dd1c454
2 files changed
+102
-5
No files matched your search
@@ -612,6 +612,25 @@ class ShieldConnectionManager:
|
||||
self._app_label_requests.add(package)
|
||||
return None
|
||||
|
||||
def _known_label(self, package: str) -> tuple[bool, str]:
|
||||
"""``(known, label)`` from the label cache — "known" = a fetch RAN (t57).
|
||||
|
||||
The accessor the DISCOVERY sweep needs and ``app_label`` deliberately is
|
||||
not: ``app_label`` answers "what should be displayed", so a definitive
|
||||
negative (a package with no manifest label) comes back as ``None``,
|
||||
exactly like a miss. A sweep that used it would re-dump every label-less
|
||||
package on every run and throw away the 24 h cache it is supposed to
|
||||
benefit from. Here the two are distinguishable.
|
||||
"""
|
||||
cached = self._app_labels.get(package)
|
||||
if cached is None:
|
||||
return False, ""
|
||||
label, stamp = cached
|
||||
if self._clock() - stamp > APP_LABEL_TTL:
|
||||
self._app_labels.pop(package, None) # stale — refetch on demand
|
||||
return False, ""
|
||||
return True, label
|
||||
|
||||
def cache_app_label(self, package: str, label: str | None) -> None:
|
||||
"""Record a label fetch that RAN, and what it found (t55, t57).
|
||||
|
||||
@@ -956,7 +975,7 @@ class ShieldConnectionManager:
|
||||
rows = [
|
||||
{
|
||||
"package": package,
|
||||
"name": labels.get(package) or self.app_label(package) or package,
|
||||
"name": labels.get(package) or package,
|
||||
"icon": icon_for_package(package, APP_ICON_FALLBACKS, APP_ICON_GENERIC),
|
||||
}
|
||||
for package in packages
|
||||
@@ -979,15 +998,21 @@ class ShieldConnectionManager:
|
||||
Cached labels are reused, so a second sweep costs almost nothing; a
|
||||
failed fetch is NOT cached (t57 UNIT 0) and the app falls back to its
|
||||
package id for this table while the next sweep retries it.
|
||||
|
||||
The check goes through :meth:`_known_label` rather than
|
||||
:meth:`app_label`, so a package the device has definitively answered
|
||||
"no label" for is not re-dumped on every sweep.
|
||||
"""
|
||||
cached: dict[str, str] = {}
|
||||
missing: list[str] = []
|
||||
for package in packages:
|
||||
label = self.app_label(package)
|
||||
if label:
|
||||
cached[package] = label
|
||||
else:
|
||||
known, label = self._known_label(package)
|
||||
if not known:
|
||||
missing.append(package)
|
||||
elif label:
|
||||
cached[package] = label
|
||||
# a known NEGATIVE needs no entry: the row falls back to its
|
||||
# package id, and the cache already holds the definitive answer
|
||||
if not missing:
|
||||
return cached
|
||||
|
||||
|
||||
Reference in new issue
Block a user