diff --git a/README.md b/README.md index 17b0dbc..4fc8f08 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ ARIA hat zwei Rollen: | Compute-Node(s) (STT/TTS/LLM) | 1..n GPU-Rechner | `cd xtts && cp .env.example .env && docker compose up -d` | | Satellit(en) πŸ›°οΈ (optional) | Fremdes Netz (BΓΌro …) | `cd satellite && cp .env.example .env && docker compose up -d --build` | | Host-Agent(en) πŸ’» (optional) | Direkt auf einem Rechner | `cd host-agent && ./build.sh` β†’ Binary + `.env` auf den Rechner, starten (Details unten & in [`host-agent/README.md`](host-agent/README.md)) | -| Android-Agent πŸ“±πŸ“Ί (optional) | Handy, Tablet, Fire TV | `cd host-agent/android && ./build.sh` β†’ APK, oder per Satellit via ADB ausrollen (Details unten) | +| Android-Agent πŸ“±πŸ“Ί (optional) | Handy, Tablet, Fire TV | `cd host-agent/android && ./build.sh` β†’ APK, oder per Satellit via ADB bzw. per MDM ausrollen (Details unten) | | Android App | Stefans Handy | APK installieren (Auto-Update via RVS) | > Compute-Nodes sind optional: ohne sie faellt STT auf lokales Whisper (CPU, @@ -1267,6 +1267,65 @@ vorgeschlagen; nur wenn es keinen gibt, kommt der automatisch generierte. --- +## Ausrollen per MDM πŸ“² β€” Cockpit-App und Android-Agent + +Beide Android-Apps lassen sich ueber ein MDM verteilen. Die Zugangsdaten gibt +das MDM als **App-Konfiguration** (Android β€žManaged Configuration") mit β€” am +Geraet muss niemand einen QR-Code scannen oder etwas eintippen. + +Im MDM pro App den **Paketnamen** eintragen und darunter die Werte als +Schluessel / Typ / Wert: + +![MDM: App-Konfiguration mit Paketname, Schluessel, Typ und Wert](docs/images/mdm/mdm-app-konfiguration.png) + +Der Typ wird pro Zeile aus *Text*, *Zahl* oder *Ja/Nein* gewaehlt: + +![MDM: Auswahl des Typs (Text, Zahl, Ja/Nein)](docs/images/mdm/mdm-typ-auswahl.png) + +### Cockpit-App β€” Paketname `com.ariacockpit` + +| Schluessel | Typ | Wert | Pflicht | +|------------|-----|------|---------| +| `host` | Text | RVS-Hostname, z.B. `aria-rvs.example.de` (= `RVS_HOST`) | ja | +| `token` | Text | Pairing-Token (= `RVS_TOKEN`, derselbe wie im QR-Code) | ja | +| `port` | Zahl | `443` (= `RVS_PORT`) | nein, Standard 443 | +| `tls` | Ja/Nein | Ja (= `RVS_TLS`) | nein, Standard Ja | + +### Android-Agent β€” Paketname `de.hackersoft.ariaagent` + +| Schluessel | Typ | Wert | Pflicht | +|------------|-----|------|---------| +| `host` | Text | RVS-Hostname (= `RVS_HOST`) | ja | +| `token` | Text | Pairing-Token (= `RVS_TOKEN`) | ja | +| `port` | Zahl | `443` | nein, Standard 443 | +| `tls` | Ja/Nein | Ja | nein, Standard Ja | +| `name` | Text | Geraetename, so erscheint er in der Diagnostic | nein, sonst der Modellname | +| `control` | Ja/Nein | Ja = ARIA darf das Geraet steuern (tippen, wischen, Apps starten) | nein, Standard Nein | +| `reply_mode` | Text | `overlay` (Einblendung) oder `speech` (Sprache) | nein, Standard `overlay` | + +### Was dabei passiert + +- **Nur mit `host` UND `token`** uebernimmt die App die Werte. Fehlt einer der + beiden, bleibt alles wie bisher (QR-Code bzw. Eingabe am Geraet). +- **Das MDM hat Vorrang.** Eine Aenderung im MDM (z.B. neuer Token) ueberschreibt + die Werte am Geraet. Beim Agenten bleiben nicht gesetzte Schluessel auf ihrem + bisherigen Wert. +- **Wann gelesen wird:** Cockpit beim App-Start, der Agent beim App-Start und + nach einem Neustart des Geraets. Eine Aenderung im MDM greift also erst danach. +- **Was das MDM nicht erledigen kann:** Beim Agenten muessen die + Bedienungshilfe (fuer `control`), die Einblendung ueber anderen Apps und die + Bildschirm-Aufnahme weiterhin einmal am Geraet erlaubt werden β€” Android laesst + diese Spezialberechtigungen nicht per App-Konfiguration zu. Cockpit fragt + Mikrofon, Benachrichtigungen usw. wie gewohnt beim ersten Start. +- Der **Token ist ein Zugangsschluessel**: Wer ihn kennt, landet im selben + RVS-Room. Im MDM nur fuer Admins sichtbar ablegen. + +Welche Schluessel es gibt, steht in `res/xml/app_restrictions.xml` der +jeweiligen App (`android/android/app/...` bzw. `host-agent/android/app/...`); +ein MDM, das die Liste selbst ausliest, zeigt sie dort auch mit Beschreibung an. + +--- + ## Docker Volumes | Volume / Bind | Pfad im Container | Zweck | diff --git a/android/android/app/src/main/AndroidManifest.xml b/android/android/app/src/main/AndroidManifest.xml index c52bb4f..cea3178 100644 --- a/android/android/app/src/main/AndroidManifest.xml +++ b/android/android/app/src/main/AndroidManifest.xml @@ -38,6 +38,10 @@ android:allowBackup="false" android:networkSecurityConfig="@xml/network_security_config" android:theme="@style/AppTheme"> + + out.putString(key, v) + is Int -> out.putInt(key, v) + is Boolean -> out.putBoolean(key, v) + } + } + promise.resolve(out) + } catch (e: Exception) { + promise.resolve(Arguments.createMap()) + } + } +} + +class MdmConfigPackage : ReactPackage { + override fun createNativeModules(reactContext: ReactApplicationContext): List = + listOf(MdmConfigModule(reactContext)) + + override fun createViewManagers(reactContext: ReactApplicationContext): List> = + emptyList() +} diff --git a/android/android/app/src/main/res/values/mdm_strings.xml b/android/android/app/src/main/res/values/mdm_strings.xml new file mode 100644 index 0000000..f631d50 --- /dev/null +++ b/android/android/app/src/main/res/values/mdm_strings.xml @@ -0,0 +1,10 @@ + + + + RVS-Host + Hostname des RVS, z.B. aria-rvs.example.de + RVS-Port + TLS verwenden + Token + Derselbe Token wie im QR-Code + diff --git a/android/android/app/src/main/res/xml/app_restrictions.xml b/android/android/app/src/main/res/xml/app_restrictions.xml new file mode 100644 index 0000000..56c7e88 --- /dev/null +++ b/android/android/app/src/main/res/xml/app_restrictions.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/android/src/services/rvs.ts b/android/src/services/rvs.ts index 324a730..91a03d7 100644 --- a/android/src/services/rvs.ts +++ b/android/src/services/rvs.ts @@ -491,7 +491,40 @@ class RVSConnection { } } + /** + * Verbindungsdaten aus dem MDM (Managed Configuration), falls beim Ausrollen + * gesetzt. Nur gueltig mit host UND token. Schluessel: res/xml/app_restrictions.xml + */ + async loadMdmConfig(): Promise { + try { + const Mdm: any = NativeModules.MdmConfig; + if (Platform.OS !== 'android' || !Mdm?.getConfig) return null; + const m = await Mdm.getConfig(); + const host = String(m?.host ?? '').trim(); + const token = String(m?.token ?? '').trim(); + if (!host || !token) return null; + const port = Number(m.port); + return { + host, + token, + port: Number.isFinite(port) && port > 0 ? port : 443, + useTLS: m.tls !== false, + }; + } catch { + return null; + } + } + async loadConfig(): Promise { + // Vom MDM gesetzte Werte haben Vorrang: so kommen geaenderte Zugangsdaten + // beim naechsten App-Start an, ohne dass jemand am Geraet etwas tun muss. + const mdm = await this.loadMdmConfig(); + if (mdm) { + console.log('[RVS] Konfiguration aus dem MDM'); + this.config = mdm; + await this.saveConfig(mdm); + return mdm; + } try { const data = await AsyncStorage.getItem(RVSConnection.STORAGE_KEY); if (data) { diff --git a/docs/images/mdm/mdm-app-konfiguration.png b/docs/images/mdm/mdm-app-konfiguration.png new file mode 100644 index 0000000..b700059 Binary files /dev/null and b/docs/images/mdm/mdm-app-konfiguration.png differ diff --git a/docs/images/mdm/mdm-typ-auswahl.png b/docs/images/mdm/mdm-typ-auswahl.png new file mode 100644 index 0000000..68374ef Binary files /dev/null and b/docs/images/mdm/mdm-typ-auswahl.png differ diff --git a/host-agent/android/app/src/main/AndroidManifest.xml b/host-agent/android/app/src/main/AndroidManifest.xml index 9d45bcc..a30ef16 100644 --- a/host-agent/android/app/src/main/AndroidManifest.xml +++ b/host-agent/android/app/src/main/AndroidManifest.xml @@ -37,6 +37,11 @@ android:supportsRtl="true" android:theme="@style/Theme.AppCompat.DayNight"> + + + 0 } ?: 443 else old.port, + tls = if (b.containsKey("tls")) b.getBoolean("tls") else old.tls, + name = b.getString("name")?.trim().takeUnless { it.isNullOrEmpty() } ?: old.name, + controlEnabled = if (b.containsKey("control")) b.getBoolean("control") else old.controlEnabled, + replyMode = b.getString("reply_mode")?.trim() + ?.takeIf { it == "overlay" || it == "speech" } ?: old.replyMode, + ) + if (cfg == old) return null + cfg.save(ctx) + return cfg + } +} diff --git a/host-agent/android/app/src/main/res/values/mdm_strings.xml b/host-agent/android/app/src/main/res/values/mdm_strings.xml new file mode 100644 index 0000000..7443dd1 --- /dev/null +++ b/host-agent/android/app/src/main/res/values/mdm_strings.xml @@ -0,0 +1,15 @@ + + + + RVS-Host + Hostname des RVS, z.B. aria-rvs.example.de + RVS-Port + TLS verwenden + Token + Derselbe Token wie im QR-Code + Geraetename + So erscheint das Geraet in der Diagnostic (leer = Modellname) + Steuern erlauben + Antwort als + overlay (Einblendung) oder speech (Sprache) + diff --git a/host-agent/android/app/src/main/res/xml/app_restrictions.xml b/host-agent/android/app/src/main/res/xml/app_restrictions.xml new file mode 100644 index 0000000..95bd122 --- /dev/null +++ b/host-agent/android/app/src/main/res/xml/app_restrictions.xml @@ -0,0 +1,41 @@ + + + + + + + + + + +