Skip to content

fix(android): extend SCVH pre-check to API 30..36 + v0.1.10#7

Merged
amillez merged 2 commits into
mainfrom
fix/android-scvh-skip-api-30-to-36
Jun 2, 2026
Merged

fix(android): extend SCVH pre-check to API 30..36 + v0.1.10#7
amillez merged 2 commits into
mainfrom
fix/android-scvh-skip-api-30-to-36

Conversation

@amillez

@amillez amillez commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Summary

v0.1.10 — extends the SCVH INTERNAL_SYSTEM_WINDOW pre-check from API 30..32 (v0.1.9) to API 30..36, after the user reported the Invalid window token crash recurring on Android 13 (API 33) production devices.

Empirical SCVH behaviour across the suspect API range

Tested host.setView() on stock Google APIs system images for every API:

API Android Stock SCVH attach Stock crash
30 11 SecurityException: Requires INTERNAL_SYSTEM_WINDOW yes (without safety net)
31 12 SecurityException yes (without safety net)
32 12L ❌ (inferred) yes
33 13 RuntimeException: Adding window failed no on stock; yes on OEM (user reports)
34 14 RuntimeException no on stock; OEM unknown
35 15 RuntimeException no on stock; OEM unknown
36 15 QPR RuntimeException no on stock; OEM unknown
37 16 attached size=… sc=ok n/a

The exception type differs but the underlying SCVH behaviour is the same: ViewRootImpl.setView calls requestLayout() before the throwing addToDisplay, leaving a TraversalRunnable queued in the SCVH Choreographer. On Android 14+, hidden-API enforcement blocks the reflective unscheduleScvhTraversals safety net (the SCVH mViewRoot field is @hide), so we cannot cancel the runnable; at the next vsync it fires against an unregistered WWM token and the process dies from Looper.loop.

Stock emulator images happened not to queue a problematic runnable on the RuntimeException path (no observed crash on Pixel 6 / 33..36), but OEM-customised WMS builds (Samsung One UI on Android 13 specifically) do — confirmed by the user's production crash reports.

Fix

-if (Build.VERSION.SDK_INT in Build.VERSION_CODES.R..Build.VERSION_CODES.S_V2 &&
+if (Build.VERSION.SDK_INT in Build.VERSION_CODES.R..36 &&
   activity.checkSelfPermission("android.permission.INTERNAL_SYSTEM_WINDOW")
     != PackageManager.PERMISSION_GRANTED
 ) {
   scvhDisabled = true
   return false
 }

API 37+ keeps SCVH active (Pixel_9 emulator confirmed) — the SurfaceFlinger-direct alpha toggle continues to win the Home-press snapshot race (broadcast: fast scvh=true dt=0ms).

Trade-off

API 30..36 devices use the legacy view.alpha path instead of the SF-direct alpha. On Android 14+ the reflection-based SC capture used by the legacy fast path is also blocked, so the actual SF-direct benefit loss is concentrated on Android 11..13 — where the pre-check is non-negotiable to prevent crashes.

Verified

Device API Result
Pixel 6 emulator 31 ✅ Pre-check fires, legacy attach, no crash, 5x home/app-switcher + 10x toggle stress clean
Pixel 6 emulator 33 ✅ Pre-check fires, legacy attach, no crash
Pixel 6 emulator 34 ✅ Pre-check fires, legacy attach, no crash
Pixel 6 emulator 35 ✅ Pre-check fires, legacy attach, no crash
Pixel 6 emulator 36 ✅ Pre-check fires, legacy attach, no crash
Pixel_9 emulator 37 ✅ Pre-check skipped (out of range), SCVH attaches, broadcast: fast scvh=true

Files changed

  • android/src/main/java/com/margelo/nitro/cover/HybridCover.kt — range expanded from R..S_V2 to R..36, comments updated.
  • package.json0.1.90.1.10.
  • CHANGELOG.md — new 0.1.10 - 2026-06-02 entry.

amillez added 2 commits June 2, 2026 16:19
…0..32 to API 30..36

v0.1.9 confirmed the same `Invalid window token` crash class re-surfaces
on API 31 (Android 12), with the user later reporting it on API 33
(Android 13) as well. v0.1.9's pre-check was scoped 30..32. Production
reports show the crash also affecting Android 13+ on OEM builds
(Samsung One UI confirmed; others likely).

Empirically tested SCVH `host.setView()` behaviour on stock Google APIs
system images for every API in the suspect range:

  API 30 (Android 11):    fails - SecurityException: Requires INTERNAL_SYSTEM_WINDOW
  API 31 (Android 12):    fails - SecurityException
  API 32 (Android 12L):   fails (inferred from 31 + AOSP behaviour)
  API 33 (Android 13):    fails - RuntimeException: Adding window failed
  API 34 (Android 14):    fails - RuntimeException
  API 35 (Android 15):    fails - RuntimeException
  API 36 (Android 15 QPR / "Baklava"): fails - RuntimeException
  API 37 (Android 16):    succeeds - "attached size=… sc=ok"

The exception type differs across the range but the underlying SCVH
behaviour is the same: `ViewRootImpl.setView` calls `requestLayout()`
before the throwing `addToDisplay`, leaving a `TraversalRunnable`
queued in the SCVH's Choreographer. The reflective
`unscheduleScvhTraversals` safety net latches off on Android 14+
hidden-API enforcement (the SCVH internal `mViewRoot` field is `@hide`),
so on those devices the runnable cannot be cancelled — at the next
vsync it fires in the TRAVERSAL phase, calls
`WindowlessWindowManager.relayout` against a token that was never
registered with the WWM, and throws `IllegalArgumentException: Invalid
window token (never added or removed already)` from `Looper.loop`.

On stock emulator images the legacy fallback path absorbs the failure
without crashing because the RuntimeException variant happens not to
queue a problematic runnable. But production reports from OEM builds
(notably Samsung One UI on Android 13) do show the crash signature
post-failure — OEM WMS customisations differ from AOSP in ways that
trigger the queued-runnable scenario.

Resolution: skip SCVH entirely on the entire empirically-confirmed-
broken range (API 30..36). The legacy non-SCVH attach uses regular
`activity.windowManager.addView` with `TYPE_APPLICATION_PANEL` and
never touches `WindowlessWindowManager`, so the crash class is
impossible there.

API 37 / Pixel_9 keeps SCVH active and continues to win the
Home-press snapshot race via the SurfaceFlinger-direct alpha toggle
(`broadcast: fast scvh=true dt=0ms`). If future Android releases
re-introduce a similar restriction, extend the upper bound.

Verified post-change:

  API 31 / Pixel 6: pre-check fires, legacy attach, no crash
  API 33 / Pixel 6: pre-check fires, legacy attach, no crash
  API 34 / Pixel 6: pre-check fires, legacy attach, no crash
  API 35 / Pixel 6: pre-check fires, legacy attach, no crash
  API 36 / Pixel 6: pre-check fires, legacy attach, no crash
  API 37 / Pixel_9: SCVH still active, attached=true, snapshot race
                    fix preserved
@amillez amillez merged commit 3146da7 into main Jun 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant