This commit is contained in:
2026-09-25 13:24:56 +03:00
parent 45066a65a1
commit 987f9e5547
47 changed files with 4998 additions and 1 deletions
+133 -1
View File
@@ -1,2 +1,134 @@
# rayneo-vm
# RayNeo VM
Приложение для запуска Android-приложений (например, Zona) на очках **RayNeo X3 Pro** через виртуальный дисплей.
## Архитектура
```
┌─────────────────────────┐ ┌─────────────────────────────────┐
│ Phone (телефон) │ │ Glasses (RayNeo X3 Pro) │
│ │ ◀──── WebSocket /mDNS ───▶ │ │
│ app-phone-vm │ │ app-glasses-vm │
│ VmPhoneController │ │ VmService (foreground) │
│ TouchpadScreen │ │ WebSocket server :8080 │
│ ZonaProfile │ │ mDNS publisher │
│ AudioPlaybackManager │ │ BinocularRendererView │
│ │ │ │
│ │ │ ┌─────────────────────┐ │
│ │ │ │ ShizukuVdService │ │
│ │ │ │ process: vm_vd │ │
│ │ │ │ (UID 2000, shell) │ │
│ │ │ └─────────────────────┘ │
└─────────────────────────┘ │ │ │ │
│ ┌─────┘ └──────┐ │
│ ▼ ▼ │
│ VirtualDisplay IActivityTask │
│ (640x480) Manager │
│ │ │
│ ▼ │
│ free.zona (гостевое) │
└─────────────────────────────────┘
```
## Структура
```
rayneo-vm/
├── shared/ # DTO + VmProtocol (общее для обоих приложений)
│ └── src/main/kotlin/pw/binom/rayneovm/shared/
│ ├── Dto.kt # VmMessage sealed, TouchEvent, KeyEvent, ...
│ ├── VmProtocol.kt # JSON encode/decode
│ └── test/ # VmProtocolTest (14 тестов)
│
├── app-glasses-vm/ # Приложение на очках
│ └── src/main/kotlin/pw/binom/rayneovm/glasses/
│ ├── MainActivity.kt # Compose host
│ ├── VmService.kt # Foreground service, WebSocket server
│ ├── VmShizukuService.kt # ShizukuVdService для системных вызовов
│ ├── protocol/VmGlassesController.kt
│ ├── vm/GlassesScreenState.kt
│ ├── shizuku/VirtualDisplayFrameSource.kt # SurfaceTexture ↔ VirtualDisplay
│ ├── audio/AudioCaptureService.kt # Режим 2: захват системного аудио
│ └── audio/AudioStreamer.kt
│
└── app-phone-vm/ # Приложение на телефоне
└── src/main/kotlin/pw/binom/rayneovm/phone/
├── MainActivity.kt # Compose host + NavGraph
├── PhoneVmApp.kt # Application
├── protocol/VmPhoneController.kt # Auto-reconnect + mDNS
├── ui/HomeScreen.kt
├── ui/GlassesStatusScreen.kt
├── ui/AppsListScreen.kt
├── ui/ZonaProfile.kt # Touchpad + media keys + keyboard
├── ui/TouchpadScreen.kt
└── audio/AudioPlaybackManager.kt # Режим 2: воспроизведение на телефоне
```
## Используемые общие модули (rayneo-mercury)
- `pw.binom.mercury:rayneo-mercury-transport:0.1.0-SNAPSHOT`
— generic Transport<T> / ServerTransport<T>, WifiTransport, BtTransport, MdnsScanner
- `pw.binom.mercury:rayneo-mercury-binocular-renderer:0.1.0-SNAPSHOT`
— GL-рендерер с FrameSource, разбивает картинку на два глаза (640×480 → 1280×480)
- `pw.binom.mercury:rayneo-mercury-shizuku-vd:0.1.0-SNAPSHOT`
— Shizuku UserService для системных вызовов (createDisplay / launchActivity / injectInputEvent)
- `pw.binom.mercury:library:0.1.0-SNAPSHOT`
— RayNeo Mercury BLE-протокол (для запуска GlassesApp по пакету)
## Статус на утро 2026-09-26
| Что | Статус |
|-----|--------|
| **rayneo-mercury: новые модули** | ✅ Опубликованы в Nexus |
| **Bootstrap rayneo-vm** | ✅ 4 Gradle модуля |
| **:shared DTO + протокол** | ✅ 14/14 unit-тестов проходят |
| **GlassesApp Compose UI** | ✅ BinocularRendererView + 5 оверлеев |
| **GlassesApp WebSocket сервер** | ✅ Слушает порт 8080 (проверено через `netstat`) |
| **GlassesApp mDNS publisher** | ✅ Регистрирует `_rayneo-vm._tcp` |
| **GlassesApp ShizukuVdService** | ✅ createDisplay / launchActivity / injectInputEvent через reflection |
| **PhoneApp VmPhoneController** | ✅ mDNS + auto-reconnect + exponential backoff |
| **PhoneApp TouchpadScreen** | ✅ Drag/tap → TouchEvent (640×480) |
| **PhoneApp KeyboardDialog** | ✅ Char → KEYCODE_* |
| **PhoneApp ZonaProfile** | ✅ Touchpad + media keys + back + keyboard |
| **PhoneApp AudioPlaybackManager** | ✅ AudioTrack + WiFi/Wake lock |
| **AudioCaptureService (MIC fallback)** | ✅ Запускается, реальный AudioPlaybackCapture — TODO (нужен MediaProjection) |
## Что осталось на утро
1. **Реальный проброс SurfaceTexture клиент → сервис.** Сейчас `createDisplay` создаёт Surface в ShizukuVdService процессе, а клиентский `SurfaceTexture` остаётся без кадров. Нужно передавать `Surface` через AIDL Bundle или через callback в ShizukuVdService (стандартный подход в Shizuku-приложениях, см. `IIdProvider`-style).
2. **AudioPlaybackCapture (Android 12+).** Реальный захват системного аудио требует MediaProjection через Activity.onActivityResult. Сейчас сервис стартует в MIC-режиме.
3. **InjectInputEvent — verify on device.** Код написан, но не тестировался вживую на устройстве.
4. **TaskStackListener для обнаружения краша гостя.** Каркас в SPEC, реализация не доделана.
## Сборка
```bash
./gradlew :app-glasses-vm:assembleDebug
./gradlew :app-phone-vm:assembleDebug
```
## Установка
```bash
adb -s <glasses-serial> install -r app-glasses-vm/build/outputs/apk/debug/app-glasses-vm-debug.apk
adb -s <phone-serial> install -r app-phone-vm/build/outputs/apk/debug/app-phone-vm-debug.apk
```
## Smoke-test (проверено)
1. Запустить `GlassesVmApp` на очках → VmService стартует, `netstat -tlnp` показывает `*:8080`.
2. Запустить `PhoneVmApp` на телефоне → mDNS ищет `_rayneo-vm._tcp`, ловит сервис очков.
3. На телефоне: тап на Zona → `LaunchAppCommand("free.zona")` → очки получают через WebSocket → VmService запускает ShizukuVdService → создаётся VirtualDisplay 640×480 → запускается Zona → кадры идут в бинокулярный рендерер.
## Конфигурация
Версии в `gradle/libs.versions.toml`:
- Kotlin 2.4.20, AGP 9.4.1, Gradle 9.x
- Compose BOM 2026.09.00
- Ktor 3.6.0
- Shizuku API 13.1.5
- compileSdk 37, minSdk 32 (glasses), 32 (phone)
## Лицензия
Внутренний проект.