docs: SPEC §5.3.1 — document Shizuku v12+ integration on RayNeo X3 Pro

- Replace obsolete moe.shizuku.permission.SHIZUKU (v11) with the v12+
  moe.shizuku.manager.permission.API_V23 in the GlassesApp permission list.
- Document the rikka.shizuku.ShizukuProvider provider block required by
  the Shizuku-API guide; without it the binder never arrives.
- Add §5.3.1 'Интеграция с Shizuku' explaining:
  * why dev.rikka.shizuku:provider is required (ContentProvider.onCreate
    pulls the binder via IPC);
  * the activity-side listener pattern (addBinderReceivedListenerSticky +
    addRequestPermissionResultListener);
  * the RayNeo system launcher race condition (PID 24482
    com.ffalconxr.mercury.launcher, UID system, forceStopPackage on
    RequestPermissionActivity within ~14ms).
This commit is contained in:
2026-09-25 14:47:12 +03:00
parent e01fafd5a9
commit ca01a7b918
+64 -3
View File
@@ -915,8 +915,21 @@ class AudioPlaybackService : Service() {
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" /> <!-- AudioPlaybackService -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Android 13+ для foreground notification -->
<uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- PARTIAL_WAKE_LOCK для AudioPlaybackService -->
<!-- Shizuku permission для VM-сервиса -->
<uses-permission android:name="moe.shizuku.permission.SHIZUKU" />
<!-- Shizuku permission v12+ (BinderSender проверяет requestedPermissions на наличие
этого permission; старое moe.shizuku.permission.SHIZUKU v11 не работает). -->
<uses-permission android:name="moe.shizuku.manager.permission.API_V23" />
```
**Shizuku Provider (v12+ обязательно, см. ниже §5.3.1):**
```xml
<provider
android:name="rikka.shizuku.ShizukuProvider"
android:authorities="${applicationId}.shizuku"
android:multiprocess="false"
android:enabled="true"
android:exported="true"
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
```
**PhoneApp (`app-phone-vm/AndroidManifest.xml`):**
@@ -934,9 +947,57 @@ class AudioPlaybackService : Service() {
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- для BLE scan на Android 6-11 -->
<!-- Shizuku permission (необязательно, у нас только Mercury-канал) -->
<uses-permission android:name="moe.shizuku.permission.SHIZUKU" />
<uses-permission android:name="moe.shizuku.manager.permission.API_V23" />
```
### 5.3.1. Интеграция с Shizuku
**Цель.** GlassesApp использует Shizuku (см. память #4779) как обёртку над `IDisplayManager.createVirtualDisplay` без флага `OWN_CONTENT_ONLY` — единственный способ создать VirtualDisplay, на который можно стартовать чужие Activity (`ActivityOptions.setLaunchDisplayId`).
**Зависимости (`gradle/libs.versions.toml`):**
```toml
shizukuApi = "13.1.5"
shizukuProvider = "13.1.5"
shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizukuApi" }
shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizukuProvider" }
```
В `app-glasses-vm/build.gradle.kts` — `implementation(libs.shizuku.api)` + `implementation(libs.shizuku.provider)`.
**Манифест — обязательная структура (см. https://github.com/RikkaApps/Shizuku-API):**
1. `<uses-permission android:name="moe.shizuku.manager.permission.API_V23"/>` (v12+). Старое `moe.shizuku.permission.SHIZUKU` (v11) НЕ работает — BinderSender на стороне сервера проверяет, что в `requestedPermissions` есть именно `API_V23`.
2. `<provider android:name="rikka.shizuku.ShizukuProvider" android:authorities="${applicationId}.shizuku" android:exported="true" android:permission="android.permission.INTERACT_ACROSS_USERS_FULL"/>` — этот ContentProvider из либы `rikka.shizuku:provider` через свой `onCreate` сам вытаскивает binder у Shizuku-сервера через ContentProvider IPC. **Без него binder к нам никогда не придёт.** Кастомные ContentProvider'ы (например, наш `ShizukuInitProvider`) этого **не заменяют** — у `rikka.shizuku.ShizukuProvider` специальные системные AIDL-хуки.
3. `<queries><package android:name="moe.shizuku.privileged.api"/></queries>` — для Android 11+ package visibility (`bindService` к сервису внутри пакета Shizuku-менеджера иначе возвращает false).
**Activity-side (MainActivity.onCreate, строго после `super.onCreate`):**
```kotlin
Shizuku.addBinderReceivedListenerSticky(object : Shizuku.OnBinderReceivedListener {
override fun onBinderReceived() { refresh() }
})
Shizuku.addRequestPermissionResultListener { _, grant -> refresh() }
if (Shizuku.pingBinder()) refresh() else state.value = AllowState.Waiting
// где refresh():
// running = Shizuku.pingBinder(); perm = Shizuku.checkSelfPermission()
// если perm == GRANTED -> Granted, иначе NeedsPermission + requestPermission(code)
```
И override `onRequestPermissionsResult(...)` в Activity (нужен для прокидывания результата диалога).
**Запрос разрешения и диалог.** Когда пользователь первый раз запускает GlassesApp:
1. `Shizuku.checkSelfPermission()` возвращает `PERMISSION_DENIED`.
2. `Shizuku.requestPermission(code)` → Shizuku-сервер стартует `RequestPermissionActivity` от `moe.shizuku.privileged.api`.
3. Activity показывает диалог «Allow rayneo-vm to use Shizuku?». После Allow — `onRequestPermissionResult` выстреливает, `refresh()` ставит state=Granted.
**Race condition на RayNeo X3 Pro (см. память #4778).** Системный лаунчер `com.ffalconxr.mercury.launcher` (PID 24482, UID 1000) **иногда** принудительно убивает (`forceStopPackage`) только что запущенную `RequestPermissionActivity` в течение ~14мс. То есть диалог либо показывается, либо нет — гонка. Workaround: при `state==NeedsPermission` после рестарта активности пользователь тапнет ещё раз. В коде: в `refresh()` если `requestPermission()` бросил — повторить на следующем `onResume()`.
**Что НЕ работает:**
- Кастомный ContentProvider вместо `rikka.shizuku.ShizukuProvider` — binder не придёт.
- `addBinderReceivedListener` без `Sticky` — пропустим событие, если binder пришёл до того, как мы зарегистрировались.
- `requestPermission()` без Activity-контекста — сервер стартует Activity, но если она умерла до того, как пользователь нажал Allow, результат не вернётся.
### 5.4. Дефолты (фиксировано для ночной сессии)
**Версии — latest stable на сентябрь 2026 (по правилу «брать последнее по возможности»):**