feat(ha_adb_common): foreground parser — mResumedActivity primary with block-join for ActivityRecord wraps, topResumedActivity fallback (R4); no window-focus parsing
This commit is contained in:
1 parent
34d09e9f70
commit
f1e49a661b
2 files changed
+93
No files matched your search
@@ -9,6 +9,7 @@ from .const import (
|
||||
ATTR_MEDIA_DAG_ROLE,
|
||||
ATTR_MEDIA_DAG_RUNNING_APPS,
|
||||
)
|
||||
from .foreground import parse_foreground
|
||||
from .intent import PlaybackIntent
|
||||
from .keys import ensure_adb_key
|
||||
from .media_session import (
|
||||
@@ -32,6 +33,7 @@ __all__ = [
|
||||
"ensure_adb_key",
|
||||
"parse_all_media_sessions",
|
||||
"parse_audio_playback",
|
||||
"parse_foreground",
|
||||
"parse_wakefulness",
|
||||
"parse_media_session",
|
||||
"running_apps_from_sessions",
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
"""Foreground-app parsing from `dumpsys activity activities` (RESEARCH_SHIELD R4).
|
||||
|
||||
Primary pattern: ``mResumedActivity: ActivityRecord{... u0 <pkg>/<activity> ...}``
|
||||
(Shield Android 11). ``topResumedActivity`` (newer firmware) is the fallback,
|
||||
checked only after the primary — per R4 finding 1. ``dumpsys window`` focus is
|
||||
NOT parsed (R4 finding 2: the first ``mCurrentFocus`` can be literally null).
|
||||
|
||||
Multi-line ActivityRecord wraps are handled by joining the block until the
|
||||
record closes or a state line arrives (R4 finding 3: match on the joined
|
||||
block, not per-line). ``ResumedActivity``/``Resumed`` duplicates are tolerated.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
_RESUMED_MARKERS = (
|
||||
("mResumedActivity", "mResumedActivity:"),
|
||||
("topResumedActivity", "topResumedActivity:"),
|
||||
("ResumedActivity", "ResumedActivity:"),
|
||||
("Resumed", "Resumed:"),
|
||||
)
|
||||
|
||||
# `u0 <pkg>/<activity>` — the userId prefix tolerates u0..uN; the package is
|
||||
# anything up to the first `/` (or closing brace when the activity is bare).
|
||||
_PACKAGE_RE = re.compile(r"u\d+\s+(\S+?)\s*/|u\d+\s+(\S+?)\s*}")
|
||||
|
||||
|
||||
def _marker_kind(stripped: str) -> str | None:
|
||||
for kind, marker in _RESUMED_MARKERS:
|
||||
if stripped.startswith(marker):
|
||||
return kind
|
||||
return None
|
||||
|
||||
|
||||
def _resumed_blocks(dump: str) -> list[tuple[str, str]]:
|
||||
"""(kind, joined-text) blocks for each resumed-activity line, in order."""
|
||||
blocks: list[tuple[str, str]] = []
|
||||
kind: str | None = None
|
||||
buf = ""
|
||||
for raw in dump.split("\n"):
|
||||
stripped = raw.strip()
|
||||
marker = _marker_kind(stripped)
|
||||
if marker is not None:
|
||||
if buf:
|
||||
blocks.append((kind, buf))
|
||||
kind, buf = marker, stripped
|
||||
continue
|
||||
if kind is None:
|
||||
continue
|
||||
# Continuation of a wrapped ActivityRecord: join until the record
|
||||
# closes (brace) or the state line completes the block (R4 finding 3).
|
||||
buf += " " + stripped
|
||||
if "}" in stripped or "state=" in stripped:
|
||||
blocks.append((kind, buf))
|
||||
kind, buf = None, ""
|
||||
if buf:
|
||||
blocks.append((kind, buf))
|
||||
return blocks
|
||||
|
||||
|
||||
def _extract_package(text: str) -> str | None:
|
||||
match = _PACKAGE_RE.search(text)
|
||||
if match is None:
|
||||
return None
|
||||
package = match.group(1) or match.group(2)
|
||||
if not package or package in ("null", "Window{"):
|
||||
return None
|
||||
return package
|
||||
|
||||
|
||||
def parse_foreground(dump: str) -> str | None:
|
||||
"""Foreground package: mResumedActivity primary, topResumed fallback.
|
||||
|
||||
Returns None when neither pattern matches (e.g. pre-parse wrappers) —
|
||||
callers must not fall back to window-focus parsing (R4 finding 2).
|
||||
"""
|
||||
if not dump:
|
||||
return None
|
||||
blocks = _resumed_blocks(dump)
|
||||
for kind, text in blocks:
|
||||
if kind == "topResumedActivity":
|
||||
continue
|
||||
package = _extract_package(text)
|
||||
if package:
|
||||
return package
|
||||
for kind, text in blocks:
|
||||
if kind != "topResumedActivity":
|
||||
continue
|
||||
package = _extract_package(text)
|
||||
if package:
|
||||
return package
|
||||
return None
|
||||
Reference in new issue
Block a user