From ca01a7b918da1a4932503fbb149c8719a94ed9b5 Mon Sep 17 00:00:00 2001 From: subochev Date: Fri, 25 Sep 2026 14:47:12 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20SPEC=20=C2=A75.3.1=20=E2=80=94=20docume?= =?UTF-8?q?nt=20Shizuku=20v12+=20integration=20on=20RayNeo=20X3=20Pro?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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). --- docs/SPEC.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/docs/SPEC.md b/docs/SPEC.md index 90e1081..b1763e5 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -915,8 +915,21 @@ class AudioPlaybackService : Service() { - - + + +``` + +**Shizuku Provider (v12+ обязательно, см. ниже §5.3.1):** + +```xml + ``` **PhoneApp (`app-phone-vm/AndroidManifest.xml`):** @@ -934,9 +947,57 @@ class AudioPlaybackService : Service() { - + ``` +### 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. `` (v12+). Старое `moe.shizuku.permission.SHIZUKU` (v11) НЕ работает — BinderSender на стороне сервера проверяет, что в `requestedPermissions` есть именно `API_V23`. +2. `` — этот ContentProvider из либы `rikka.shizuku:provider` через свой `onCreate` сам вытаскивает binder у Shizuku-сервера через ContentProvider IPC. **Без него binder к нам никогда не придёт.** Кастомные ContentProvider'ы (например, наш `ShizukuInitProvider`) этого **не заменяют** — у `rikka.shizuku.ShizukuProvider` специальные системные AIDL-хуки. +3. `` — для 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 (по правилу «брать последнее по возможности»):**