Files

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.

Fire TV / Android TV

Dieselbe APK läuft auf Handy und Stick — die TV-Fähigkeiten schalten sich automatisch frei (caps enthalten dann tv). Zusätzlich auf dem TV:

Action Was
notify Einblendung über dem laufenden Bild (Text/Bild) — ohne Netflix & Co. zu stoppen; oder als Sprache, je nach Einstellung (mode überschreibt)
speak Sprachausgabe über die TV-Lautsprecher (ARIAs echte Stimme, sonst Geräte-TTS)
volume Lautstärke setzen (percent)
listen / listen_stop Mikrofon öffnen/schließen — ARIA-Sprachdialog auf dem Gerät
ui_key zusätzlich D-Pad: up/down/left/right/ok, play_pause/next/prev, lauter/leiser/stumm

Sprachdialog: Die App spricht das gleiche Protokoll wie die ARIA-App (stt_stream_* rein, chat + audio_pcm raus) — serverseitig ist nichts anzupassen. In den App-Einstellungen wählst Du, wie ARIA hier antwortet: Einblendung (Standard) oder Sprache; sie kann es pro Anfrage überschreiben.

Turn-Ownership: Der RVS verteilt jede Antwort an alle Clients im Raum. Damit nicht Handy und Stick gleichzeitig losreden, gibt dieses Gerät nur aus, was aus seiner eigenen Frage entstanden ist.

Mikrofon der Fire-TV-Fernbedienung: Die Mikro-Taste ist systemseitig mit Alexa belegt; ob Fremd-Apps an das Remote-Mikro kommen, ist je nach Stick- Generation unterschiedlich und muss am Gerät ausprobiert werden. Sicher funktioniert der Sprech-Knopf in der App (OK-Taste) — und falls das Remote- Mikro nicht hergibt, tut es ein USB-Mikro am Stick.

Berechtigungen einmalig in der App: „Einblendung erlauben" (Über anderen Apps anzeigen) und beim ersten Sprechen das Mikrofon.

Ausrollen per Satellit (ohne Datei-Kopieren)

Statt die APK von Hand aufs Gerät zu schaufeln, kann ein Satellit sie per ADB über das Netz installieren:

  1. Am Fire TV: Einstellungen › Mein Fire TV › Entwickleroptionen › ADB-Debugging an.
  2. Diagnostic → Satelliten → scannen. ADB-fähige Geräte zeigen ein ADB-Tag und einen 📦-Button.
  3. 📦 → APK-URL (aus dem Gitea-Release) → Jetzt ausrollen. Beim ersten Mal erscheint auf dem TV „USB-Debugging zulassen?" → bestätigen (Haken „Immer erlauben"). Der Rollout wartet darauf und macht dann automatisch weiter.

Einrichtung ohne Tippen: Am Fire TV gibt es keine Kamera (kein QR-Scan) und Host/Token mit der Fernbedienung einzugeben ist eine Qual. Deshalb ist im Rollout- Dialog „Gleich einrichten" vorausgewählt: die Diagnostic kennt den RVS-Zugang selbst und gibt ihn dem Agenten über dieselbe ADB-Verbindung mit (adb.provision → am start mit Intent-Extras). Der Agent speichert die Daten, startet den Dienst und ist sofort online — null Eingaben am Fernseher. Das Token geht dabei serverseitig raus, es steht nie im Browser-Formular.

Mit „Status prüfen" (adb.status) siehst Du jederzeit, ob der Agent auf dem Gerät installiert ist, welche Version läuft und ob der Dienst aktiv ist.

Sicherheitshinweis: Die Einrichtung per Intent geht an die (exportierte) Haupt-Activity. Wer ADB-Zugriff auf das Gerät hat, kann den Agenten damit umkonfigurieren — wer ADB hat, kontrolliert das Gerät ohnehin. Die App zeigt beim Empfang einen Hinweis („Von ARIA eingerichtet"), damit es nicht unbemerkt passiert. Auf einem dedizierten Stick ist das unkritisch; schalte ADB-Debugging nach dem Ausrollen ab, wenn Du ganz sicher gehen willst.

ARIA kann dasselbe: satellite_command mit action='adb.install', params={'ip':'…','url':'…'}. Der Satellit lädt die APK einmal und kann sie auf beliebig viele Sticks installieren.

⚠️ 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_result 1: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

  1. ✅ Verbinden + sichtbar — Gradle-Projekt, AndroidManifest, RVS-WS-Client, Foreground-Service, Connect-UI (QR-Scan + manuell), host_hello/host_ping. → Agent erscheint in der Diagnostic. info funktioniert.
  2. ✅ Sehen — MediaProjection-Screenshot (ScreenCapturer, gleicher {format,bytes,base64}-Vertrag wie der Desktop-Agent → host_screenshot) + ui_dump (AriaAccessibilityService, nur lesend → Brain-Tool host_ui_dump). Freigabe einmalig in der App: „Bildschirm-Zugriff erlauben" + „Bedienungshilfe öffnen". → ARIA sieht den Schirm und liest die UI-Elemente mit Koordinaten.
  3. ✅ Steuern — ui_tap/ui_text/ui_swipe/ui_key/app_launch über den AccessibilityService (canPerformGestures, dispatchGesture, ACTION_SET_TEXT, performGlobalAction) → Brain-Tools host_ui_tap/_text/_swipe/_key/ host_app_launch. Tap-Koordinaten = die x/y aus ui_dump (echte Pixel), NICHT aus dem (skalierten) Screenshot. → ARIA bedient Apps (E-Mail-Setup).
  4. 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

  1. dist/aria-android-agent.apk aufs Zielgerät kopieren (USB, Cloud, adb install dist/aria-android-agent.apk, …).
  2. Antippen → Android fragt nach „Unbekannte Quellen / Aus dieser Quelle installieren erlauben" → erlauben.
  3. 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          # Version automatisch +1 (oder: ./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-Tags v<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 in host-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) und ui_tap/ui_text/ui_swipe/ui_key/app_launch (steuern); release_agent.sh vorhanden. Damit läuft der volle Ablauf sehen→steuern (z.B. E-Mail-Konto einrichten). Nächstes: Feinschliff (M4).

Auto-Update

Der Agent fragt beim Verbinden (und danach alle ~6 h) den RVS nach einer neueren Version (agent_update_check). Liegt auf dem RVS eine neuere APK in rvs/updates/agent/, lädt er sie im Hintergrund per HTTP (/update/agent/latest.apk, Token im Header) und zeigt „ARIA Host-Agent x.y bereit — Tippen zum Installieren" — zusätzlich einen Knopf in der App. Den Install-Dialog zeigt Android selbst; still installieren darf eine normale App nicht. Beim ersten Mal fragt Android, ob die App „unbekannte Apps installieren" darf.

release_agent.sh kopiert die APK automatisch auf den RVS, wenn RVS_UPDATE_HOST gesetzt ist (wie bei release.sh). Manuell: scp aria-android-agent.apk <rvs-host>:~/ARIA-AGENT/rvs/updates/agent/aria-host-agent-v<version>.apk — die Version muss im Dateinamen stehen.