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_ROLE,
|
||||||
ATTR_MEDIA_DAG_RUNNING_APPS,
|
ATTR_MEDIA_DAG_RUNNING_APPS,
|
||||||
)
|
)
|
||||||
|
from .foreground import parse_foreground
|
||||||
from .intent import PlaybackIntent
|
from .intent import PlaybackIntent
|
||||||
from .keys import ensure_adb_key
|
from .keys import ensure_adb_key
|
||||||
from .media_session import (
|
from .media_session import (
|
||||||
@@ -32,6 +33,7 @@ __all__ = [
|
|||||||
"ensure_adb_key",
|
"ensure_adb_key",
|
||||||
"parse_all_media_sessions",
|
"parse_all_media_sessions",
|
||||||
"parse_audio_playback",
|
"parse_audio_playback",
|
||||||
|
"parse_foreground",
|
||||||
"parse_wakefulness",
|
"parse_wakefulness",
|
||||||
"parse_media_session",
|
"parse_media_session",
|
||||||
"running_apps_from_sessions",
|
"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