feat: Brain-Tools fuer Overlay/Sprache + Rechte automatisch per ADB erteilen

1) ARIA hatte KEIN Werkzeug fuer die Einblendung — der Agent kann notify/speak/
   volume laengst, aber im Brain fehlten die Tools. Sie hat deshalb improvisiert
   ('cmd notification post', gibt es auf Android 9 nicht) und ist gescheitert.
   Neu: host_notify (Overlay/Sprache, mode ueberschreibbar), host_speak,
   host_volume — mit dem ausdruecklichen Hinweis, es NICHT ueber adb zu versuchen.
2) _adb_grant_permissions: appops PROJECT_MEDIA (Screenshots ohne Dialog) +
   SYSTEM_ALERT_WINDOW (Overlay) + pm grant RECORD_AUDIO (Mikro) werden beim
   Einrichten automatisch gesetzt — kein Zusammenklicken mit der Fernbedienung
   mehr. Best effort; was scheitert, wird als 'manuell noetig' gemeldet. Neue
   Aktion adb.permissions setzt sie einzeln nach.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-30 02:37:20 +02:00
co-authored by Claude Opus 4.8
parent ac40afc856
commit 586a2bf134
3 changed files with 118 additions and 4 deletions
+49 -2
View File
@@ -637,6 +637,8 @@ async def _control(action: str, params: dict, devices: list[dict]) -> dict:
return await loop.run_in_executor(None, _do_adb_provision, params)
if action == "adb.status":
return await loop.run_in_executor(None, _do_adb_status, params)
if action == "adb.permissions":
return await loop.run_in_executor(None, _do_adb_permissions, params)
return {"ok": False, "error": f"Aktion '{action}' nicht implementiert."}
except Exception as exc:
return {"ok": False, "error": f"{action} fehlgeschlagen: {exc}"}
@@ -1267,6 +1269,42 @@ def _do_adb_status(params: dict) -> dict:
"nicht installiert")}}
def _adb_grant_permissions(target: str, package: str) -> dict:
"""Erteilt dem Agenten die Sonderrechte per ADB — das spart das Antippen mit
der Fernbedienung (auf einem Fire TV besonders muehsam):
PROJECT_MEDIA Bildschirmaufnahme ohne Nachfrage-Dialog (Screenshots)
SYSTEM_ALERT_WINDOW Einblendung ueber dem laufenden Bild (Overlay)
RECORD_AUDIO Mikrofon (Sprachdialog)
Best effort: was nicht geht, wird gemeldet, bricht aber nichts ab."""
granted, failed = [], []
ops = [("appops", ["appops", "set", package, "PROJECT_MEDIA", "allow"], "Bildschirmaufnahme"),
("appops", ["appops", "set", package, "SYSTEM_ALERT_WINDOW", "allow"], "Einblendung")]
perms = [("android.permission.RECORD_AUDIO", "Mikrofon")]
for _, cmd, label in ops:
rc, out, err = _adb_run(["-s", target, "shell"] + cmd, timeout=30)
(granted if rc == 0 and "error" not in f"{out}{err}".lower() else failed).append(label)
for perm, label in perms:
rc, out, err = _adb_run(["-s", target, "shell", "pm", "grant", package, perm], timeout=30)
(granted if rc == 0 and "error" not in f"{out}{err}".lower() else failed).append(label)
return {"granted": granted, "failed": failed}
def _do_adb_permissions(params: dict) -> dict:
"""Nur die Berechtigungen setzen (ohne Neuinstallation/Einrichtung)."""
target = _adb_target(params)
if not target:
return {"ok": False, "error": "ip/host erforderlich."}
package = (params.get("package") or AGENT_PACKAGE).strip()
ok, msg = _adb_connect(target)
if not ok:
return {"ok": False, "error": msg}
r = _adb_grant_permissions(target, package)
msg = "erteilt: " + (", ".join(r["granted"]) or "—")
if r["failed"]:
msg += " · nicht moeglich: " + ", ".join(r["failed"])
return {"ok": True, "result": {"target": target, "message": msg, **r}}
def _do_adb_provision(params: dict) -> dict:
"""Richtet den ARIA-Agent auf dem Geraet FERN ein (RVS-Zugang uebergeben).
@@ -1301,6 +1339,11 @@ def _do_adb_provision(params: dict) -> dict:
return {"ok": False, "error": f"'{package}' ist nicht installiert — "
"erst adb.install ausfuehren."}
# Sonderrechte gleich mit erteilen (Bildschirm/Overlay/Mikro) — sonst muesste
# man sie am Fernseher mit der Fernbedienung zusammenklicken.
perms = ({"granted": [], "failed": []} if params.get("skip_permissions")
else _adb_grant_permissions(target, package))
# WICHTIG: 'adb shell' haengt die Argumente zu EINER Kommandozeile zusammen,
# die auf dem Geraet nochmal von der Shell zerlegt wird. Werte mit Leerzeichen
# ("Wohnzimmer Fire TV") wuerden dabei abgeschnitten -> jeden Wert quoten.
@@ -1336,9 +1379,13 @@ def _do_adb_provision(params: dict) -> dict:
"Android hat den Einrichtungs-Intent nicht ausgeliefert (Activity "
"war schon offen). Auf dem Geraet die Agent-App einmal schliessen "
"und erneut 'Nur einrichten' druecken. Details: " + txt[:200]}
msg = f"Agent eingerichtet (RVS {rvs_host}) und gestartet"
if perms.get("granted"):
msg += " · Rechte erteilt: " + ", ".join(perms["granted"])
if perms.get("failed"):
msg += " · manuell noetig: " + ", ".join(perms["failed"])
return {"ok": True, "result": {
"target": target, "package": package,
"message": f"Agent eingerichtet (RVS {rvs_host}) und gestartet"}}
"target": target, "package": package, "permissions": perms, "message": msg}}
def _as_bool(v, default: bool) -> bool: