Skip to content

Keep Castla mirroring alive while the phone is locked #43

@Suprhimp

Description

@Suprhimp

Goal

Mirroring should remain fully functional when the user locks the phone. Today, locking the screen triggers a cascade of OEM-specific teardowns (most reliably reproducible on Samsung Galaxy Flip / OneUI) that kill the Shizuku-backed privileged service and, with it, the video/audio/input pipeline. The app either freezes on its last frame, stops accepting input, or silently disconnects the browser client.

This issue tracks the end-to-end goal; individual root causes are captured as subsections below. PRs that knock out any failure mode should link back here.

Why this matters

The primary car-dashboard use case expects the phone to be locked for the entire drive. Every unresolved failure mode here defeats the whole product. Users have no way to recover short of unlocking the phone and reopening Castla, which defeats hands-off operation.

Known failure modes on screen lock

  1. WADB disconnect cascade (Samsung)
    AdbDebuggingManager: Network disconnected. Disabling adbwifi fires 1–10s after screen-off when WiFi is allowed to sleep. This tears down adbd, and every shell-UID child (shizuku_server + our watchdog) dies with it.
    Partial mitigation shipped: WIFI_MODE_FULL_HIGH_PERF WifiLock (extends survival to ~40s on Galaxy Flip but not indefinite).

  2. Default USB Configuration = File Transfer
    Samsung OneUI restarts USB on every screen lock when Default USB Configuration is MTP/PTP. This kills Shizuku even with the WifiLock held.
    Partial mitigation shipped: advisory dialog prompting the user to switch to "Charging only". Does not fix devices where the setting can't be changed.

  3. Samsung doze / process freezer (unfixed)
    A separate death trigger still kicks in ~24s after lock on some devices, independent of WADB teardown. Likely the OneUI process freezer or doze app-standby promotion. Our watchdog can't respawn from a frozen UID.

  4. Shizuku manager itself being killed
    When the Shizuku manager app is frozen/killed, our silent "Shizuku stopped" notification is the only recovery path. If the user can't tap it because the screen is locked, mirroring stays down until they unlock.

  5. User-service token mismatch after recovery
    Even when shizuku_server is respawned by the watchdog, the user-service record cached by shizuku_server still points at our previous (dead) process binder, so the next bindUserService never delivers onServiceConnected.
    Mitigation shipped: force unbindUserService(..., remove=true) on install/update and on quick-death of the user-service connection.

  6. Video pipeline stall without a death signal
    Separately, the MediaCodec encoder on the virtual display can stall silently (no frames produced) without any of the above killing the process — the browser just sees a frozen last frame.
    Partial mitigation shipped: client-side frame-arrival watchdog that surfaces a stall to the user.

Acceptance criteria

  • On Galaxy Flip (SM-F741N, Android 14) with Default USB Configuration = "Charging only" and WiFi available, mirroring stays alive through at least 10 minutes of continuous screen-off.
  • Recovery from any transient Shizuku death (watchdog-visible or not) completes with no user interaction required — the browser client sees a ≤3s gap in video, not a permanent disconnect.
  • Screen-off with USB disconnected performs no worse than screen-off with USB connected, or the app clearly surfaces the dependency to the user.
  • No user-visible notification vibration / sound when the teardown is transient and we self-recover.

Investigation leads

  • Is there a way to observe our own UID being frozen (e.g. via ActivityManager.getProcessesInErrorState, UsageStatsManager, or a tight polling loop from a non-frozen companion process) so we can preemptively wake?
  • Can we hold a foreground service "media-projection" type lock that Samsung OneUI respects differently from our current dataSync type?
  • Can the Shizuku manager be persuaded to stay unfrozen (dozeWhitelist already applied — check if any app-standby bucket is demoting it anyway)?
  • Can we relocate enough of the mirroring pipeline to a shell-UID child that survives UID freezing, talking to our app UID only when needed?
  • Is there a post-lock onReceive broadcast we could use to trigger immediate re-verification of the pipeline?

Related history

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions