Erkenntnis aus dem Live-Test: bei gesperrtem Bildschirm greifen Gesten/Tasten nicht (Android-Sicherheit), der Agent meldete aber still 'ok' -> Verwirrung (home,home,home, Screenshot zeigt trotzdem alte App). Fix: KeyguardManager. isKeyguardLocked() gate fuer Steuer-Aktionen (ui_tap/text/swipe/key/app_launch) -> klare Meldung 'Geraet ist gesperrt - bitte entsperren'. Sehen (screenshot/ ui_dump/info) bleibt auch gesperrt erlaubt. README-Hinweis ergaenzt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 KiB
ARIA Android-Agent
Ein nativer Android-Agent (eigene APK), der ARIA erlaubt, ein Smartphone
fernzusteuern — inkl. Bedienen fremder App-UIs (z.B. „ARIA, richte auf dem
Handy ein E-Mail-Konto ein"). Gegenstück zum Desktop-host-agent (Linux/macOS/
Windows), aber Android ist kein Unix-Shell-System — deshalb ein anderes
Action-Set (UI-Automation statt beliebiger Shell-Kommandos).
Verbindet sich wie die ARIA-App ausgehend zum RVS (gleicher Token/Raum),
Verbindungs-Setup per QR-Scan oder manueller Eingabe. Taucht in der
Diagnostic unter Satelliten → Host-Agenten 💻 auf (host_hello mit
os="Android …" + Android-Caps).
Warum nativ (Kotlin), nicht Termux/RN
- UI-Automation (fremde Apps bedienen) geht auf Android nur über einen AccessibilityService — den kann nur eine native App bereitstellen.
- Screenshots einer laufenden Session: MediaProjection (native).
- Dauerbetrieb: Foreground-Service mit Notification (native).
- Termux gäbe nur Shell +
termux-api(SMS/Anruf/Standort …), kein Bedienen anderer App-UIs. Für „E-Mail-Konto durchklicken" reicht das nicht.
Tech: Kotlin, OkHttp-WebSocket (RVS-Client), CameraX/ML-Kit (QR), AccessibilityService (Input), MediaProjection (Screenshot). Build via Docker (Android-SDK + Gradle) → APK. Nur Linux baut APKs (Docker), Deploy manuell.
Action-Set (host_command → host_result)
Android-spezifisch (statt exec/read/write des Desktop-Agents):
| Action | Was |
|---|---|
screenshot |
Bildschirmfoto (MediaProjection) — ARIA sieht den Schirm |
ui_dump |
Sichtbare UI als Baum (Texte, Buttons, Felder + Koordinaten) — ARIAs „Augen" für gezieltes Tippen |
ui_tap |
Tippen (x,y ODER auf ein Element aus ui_dump) |
ui_text |
Text in das fokussierte/angegebene Feld schreiben |
ui_swipe |
Wischen/Scrollen |
ui_key |
Systemtasten (BACK, HOME, ENTER …) |
app_launch |
App per Paketname starten (z.B. E-Mail-App) |
app_list |
installierte Apps auflisten |
info |
Gerät: Modell, Android-Version, Akku, Netz, IP |
notify |
Benachrichtigung anzeigen |
| (später) | sms_send, call, location, clipboard (je nach Bedarf + Berechtigung) |
ARIA-Flow „E-Mail einrichten": app_launch (Mail-App) → screenshot/ui_dump
(sehen, was da ist) → ui_tap/ui_text (durchklicken) → wieder ui_dump prüfen,
bis fertig. Genau das agentische Muster wie beim Endian-Fix, nur mit Handy-UI.
⚠️ Gerät muss ENTSPERRT sein, damit ARIA es steuern kann. Gesten und Systemtasten (
ui_tap/ui_text/ui_swipe/ui_key/app_launch) greifen bei gesperrtem Bildschirm nicht — Android-Sicherheit. Der Agent erkennt das und meldet „Gerät ist gesperrt — bitte erst entsperren", statt still „ok" zu sagen. Sehen (screenshot,ui_dump,info) funktioniert dagegen auch bei gesperrtem Gerät.
Dauerbetrieb — Foreground-Service vs. Push (FCM)
Der Agent muss immer erreichbar sein, obwohl Android Hintergrundprozesse aggressiv killt (Doze, App-Standby, OEM-Batterie-Manager wie Xiaomi/Huawei). Zwei Wege, deine WhatsApp-Intuition trifft ins Schwarze:
A) Foreground-Service (persistente WebSocket) — der einfache Start:
- Dauerhafte RVS-Verbindung + Foreground-Notification („Agent aktiv").
- Braucht:
FOREGROUND_SERVICE, Akku-Optimierung ausnehmen (REQUEST_IGNORE_BATTERY_OPTIMIZATIONS— User whitelistet die App),RECEIVE_BOOT_COMPLETED+ BootReceiver (Neustart nach Reboot), Auto-Reconnect (haben wir im Protokoll schon). - Reutzt unser bestehendes
host_hello/host_command/host_result1:1. - Nachteil: etwas Akku; manche OEMs killen trotzdem → „Autostart" manuell erlauben.
B) Self-hosted Push (KEIN Google!) — genau wie WhatsApp, aber auf eigenem Server:
- UnifiedPush + self-hosted ntfy: Auf dem ARIA-Server läuft ntfy (freier, self-hostbarer Push-Server). Der Agent nutzt UnifiedPush (offener Standard, de-Google-Welt/F-Droid) mit dem ntfy-Distributor auf dem Handy. Will ARIA etwas, POSTet die Bridge/RVS an ntfy → weckt die App → sie holt den Befehl vom RVS, arbeitet, antwortet. Läuft auch auf Custom-ROMs OHNE Google Play Services.
- Akkuschonend wie FCM, aber ohne jede Google-Abhängigkeit. Nur ein Dienst mehr (ntfy) im Stack + der Push-Auslöser serverseitig.
C) FCM (Google) — optional: Wer ein Stock-Android mit Play Services hat und Googles Push-Kanal will, kann FCM statt ntfy nehmen (bester Akku auf GMS-Geräten). Braucht Firebase-Projekt + Play Services. Nur eine Option, keine Pflicht.
Custom-ROM ohne Google: → Weg A (eigener Socket) oder B (self-hosted ntfy). Beide brauchen KEIN Google. Für Stefans dediziertes Ziel-Handy ist A sogar oft die einfachste Dauerlösung (unser eigener „Push" über den RVS-Socket).
Hybrid (ideal, End-Ausbau): Im Leerlauf nur Push (max. Akku). Ein Push weckt die App → sie öffnet die RVS-Verbindung, hält sich per Wakelock für die Interaktion wach (mehrere Befehle flüssig, z.B. E-Mail-Setup durchklicken) → schläft nach ein paar Sekunden Ruhe wieder ein. So WhatsApp-Akku UND schnelle Multi-Befehl-Sessions. Der Push kommt dabei von B (self-hosted ntfy) oder C (FCM) — freie Wahl. Erste Push-Latenz aus tiefem Doze ~1–3 s; danach bleibt der Socket die Session offen.
Empfehlung: Meilenstein 1–3 mit A (Foreground-Service) — läuft sofort und nutzt alles Vorhandene, damit wir schnell einen funktionierenden Agenten haben, ganz ohne externe Dienste. Dann Push-Hybrid mit self-hosted ntfy (B) als Akku-Ausbau — KEIN Google. FCM (C) nur optional für Stock-Android. Action-Set/Protokoll bleiben identisch, nur der Wecker ändert sich.
Sicherheit
- Reagiert nur auf den eigenen RVS-Raum (Token); Setup per QR/manuell.
- CONTROL_ENABLED-Schalter in der App (Default AUS) — erst wenn Stefan es bewusst aktiviert, führt der Agent Aktionen aus.
- AccessibilityService + MediaProjection müssen vom User explizit in den Android-Einstellungen freigegeben werden (kein stiller Zugriff möglich).
- Alle Aktionen werden protokolliert (In-App-Log + optional an ARIA).
- Voller Gerätezugriff — nur auf eigenen/anvertrauten Geräten nutzen.
Meilensteine
- ✅ Verbinden + sichtbar — Gradle-Projekt, AndroidManifest, RVS-WS-Client,
Foreground-Service, Connect-UI (QR-Scan + manuell),
host_hello/host_ping. → Agent erscheint in der Diagnostic.infofunktioniert. - ✅ Sehen — MediaProjection-Screenshot (
ScreenCapturer, gleicher{format,bytes,base64}-Vertrag wie der Desktop-Agent →host_screenshot) +ui_dump(AriaAccessibilityService, nur lesend → Brain-Toolhost_ui_dump). Freigabe einmalig in der App: „Bildschirm-Zugriff erlauben" + „Bedienungshilfe öffnen". → ARIA sieht den Schirm und liest die UI-Elemente mit Koordinaten. - ✅ Steuern —
ui_tap/ui_text/ui_swipe/ui_key/app_launchüber den AccessibilityService (canPerformGestures,dispatchGesture,ACTION_SET_TEXT,performGlobalAction) → Brain-Toolshost_ui_tap/_text/_swipe/_key/host_app_launch. Tap-Koordinaten = die x/y ausui_dump(echte Pixel), NICHT aus dem (skalierten) Screenshot. → ARIA bedient Apps (E-Mail-Setup). - Feinschliff —
app_list/notify, Build-Härtung,release_agent.sh.
Bauen
APK-Builds laufen nur unter Linux — deshalb im Docker-Container. Du brauchst
nichts Android-spezifisches installiert, nur Docker. Android-SDK, Gradle und
Build-Tools zieht der Container selbst (Dockerfile.build).
cd host-agent/android
./build.sh
build.sh baut das Image aria-android-agent-build und lässt darin
gradle assembleDebug laufen. Ergebnis:
host-agent/android/dist/aria-android-agent.apk
Das ist ein Debug-APK: auto-signiert mit dem Android-Debug-Key, also direkt installierbar — ohne eigenen Keystore, ohne Play Store.
Auf dem Handy installieren
dist/aria-android-agent.apkaufs Zielgerät kopieren (USB, Cloud,adb install dist/aria-android-agent.apk, …).- Antippen → Android fragt nach „Unbekannte Quellen / Aus dieser Quelle installieren erlauben" → erlauben.
- App öffnen → verbinden (QR/manuell), „Steuerung erlauben" an, für M2 zusätzlich „Bildschirm-Zugriff erlauben" + „Bedienungshilfe öffnen".
Was der erste Build kostet
Der erste Lauf lädt viel (JDK-Image, Gradle, Android-SDK, Dependencies) und dauert entsprechend — mehrere Minuten. Folge-Builds sind schnell (Docker-Layer + Gradle-Cache im Image). Häufige Stolperer:
- Docker fehlt / kein Zugriff →
docker-Rechte prüfen (docker ps). - Overlay-on-Overlay auf einem Live-ISO („invalid argument" beim Image-Bau) — gleiches Problem wie beim Desktop-Agent auf dem Mint-Live-System; von einer installierten Linux-Kiste bauen.
- Kotlin-/Manifest-Fehler beim allerersten Bau eines neuen Meilensteins: den Gradle-Fehler posten, das glätten wir schnell.
Version setzen
Bis es release.sh gibt, wird die Version in app/build.gradle gepflegt
(versionCode / versionName). Aktuell 1 / 0.2.0 (M1+M2).
Release
Das APK ist kein Teil des Git-Trees (blaeht sonst die History dauerhaft auf) —
es wird als Release-Asset an einen Tag gehaengt. Das macht release_agent.sh
(liegt eine Ebene hoeher, in host-agent/):
cd host-agent
./release_agent.sh 0.2.0
Das Skript (wie die release.sh der Haupt-App, Version als Parameter):
- setzt die Version (
host_agent.py→AGENT_VERSION,app/build.gradle→versionName/versionCode), - baut Linux-Binary + Android-APK per Docker,
- committet den Version-Bump, taggt
agent-v<version>(eigener Namespace, kollidiert nicht mit den App-Tagsv<version>) und pusht, - legt ein Gitea-Release an und laedt die Assets hoch:
aria-host-agent-linux-x64,aria-host-agent-android-agent-v<version>.apk, optional-macos/-windows.exe(falls inhost-agent/dist/vorgebaut).
Gitea-Zugang (GITEA_URL, GITEA_REPO, GITEA_USER) kommt aus der Umgebung oder
einer .env; das Kennwort wird interaktiv abgefragt. Binaries landen unter
„Releases", nie im Tree.
Status: M1–M3 fertig — verbinden,
info,screenshot,ui_dump(sehen) undui_tap/ui_text/ui_swipe/ui_key/app_launch(steuern);release_agent.shvorhanden. Damit läuft der volle Ablauf sehen→steuern (z.B. E-Mail-Konto einrichten). Nächstes: Feinschliff (M4).