glasses: launch guest on Shizuku VirtualDisplay + binocular render

- VmShizukuService is a Shizuku UserService only; remove its <service> manifest entry (ServiceStarter instantiates it reflectively, an Android service entry causes ClassCastException)
- VirtualDisplayFrameSource: await binder via ShizukuVdClient.bindForResult(5s), expose virtualDisplayId + shizukuBinder, stop the UserService on release
- VmGlassesController: serialize acquire via Mutex, use double-buffered SurfaceTexture (DisplayManagerService rejects single-buffer), launch the guest on the VD with ShizukuVdBinder.launchActivity
- GlassesScreen: render controller.frameSource (Running) directly; drop the LaunchedEffect acquire
- DebugTestReceiver: register at runtime from MainActivity (background manifest receivers are blocked on Android 12+)
This commit is contained in:
2026-09-25 17:19:49 +03:00
parent 5afdde70e1
commit 03f1d7dcc7
6 changed files with 112 additions and 89 deletions
@@ -25,14 +25,23 @@ class VirtualDisplayFrameSource private constructor(
private val surface: Surface,
private val binder: IBinder,
private val displayId: Int,
private val client: ShizukuVdClient,
width: Int,
height: Int,
) : SurfaceTextureFrameSource(source, width, height) {
/** ID виртуального дисплея (нужен для запуска Activity через ActivityOptions.setLaunchDisplayId). */
val virtualDisplayId: Int get() = displayId
/** Binder к ShizukuVdService (для последующих вызовов launchActivity / injectInputEvent). */
val shizukuBinder: IBinder get() = binder
override fun release() {
runCatching { ShizukuVdBinder.releaseDisplay(binder, displayId) }
runCatching { surface.release() }
super.release()
// Отвязываем UserService — держим его живым ровно на время жизни дисплея.
runCatching { client.stop() }
Log.i(TAG, "VirtualDisplayFrameSource released: displayId=$displayId")
}
@@ -60,10 +69,10 @@ class VirtualDisplayFrameSource private constructor(
val surface = Surface(surfaceTexture)
val client = ShizukuVdClient(context, serviceClass)
return try {
var binder: IBinder? = null
client.start { b -> binder = b }
val b = binder ?: run {
Log.w(TAG, "Shizuku binder not ready, returning null")
// bindUserService асинхронен — ждём Binder через suspend-обёртку (5с таймаут).
val b = client.bindForResult(timeoutMs = 5000)
if (b == null) {
Log.w(TAG, "Shizuku binder not ready (timeout или pingBinder=false), returning null")
surface.release()
client.stop()
return null
@@ -78,7 +87,7 @@ class VirtualDisplayFrameSource private constructor(
return null
}
Log.i(TAG, "Acquired VirtualDisplay id=$displayId ${width}x${height}")
VirtualDisplayFrameSource(surfaceTexture, surface, b, displayId, width, height)
VirtualDisplayFrameSource(surfaceTexture, surface, b, displayId, client, width, height)
} catch (t: Throwable) {
Log.e(TAG, "acquire failed", t)
runCatching { surface.release() }