Skip to content

Commit f267e94

Browse files
User-greenclaude
authored andcommitted
feat(dhizuku): unfreeze server via AMS-mediated provider call
Wake the server with a real ContentProvider transaction instead of only launching the Dhizuku app's UI, so cached/frozen servers recover without a launcher activity. Background: a server that keeps no foreground service (e.g. OwnDroid's built-in server) is frozen by the OS once backgrounded. The first bind to a frozen process returns "error -74 (sent to frozen apps)". Acquiring the client or a raw binder call does not unfreeze it; an AMS-mediated provider call does — delivering the transaction is what forces the thaw. - New wakeDhizukuServer(): resolves the server's provider authority from the active device owner (DhizukuVariables.getProviderAuthorityName) and pokes it with a throwaway call(). Best-effort, fully guarded. - ensureDhizukuAlive() now wakes via the provider (and re-wakes each poll, since the process can re-freeze mid-wait) and no longer hard-returns when com.rosan.dhizuku is absent — launching the standalone app's UI becomes an additional path rather than a prerequisite. Pairs with the server-agnostic-discovery change: that one lets isDhizukuAlive recognise a running built-in server, this one revives it when it's frozen. Together they make a frozen OwnDroid server usable; each stands alone too (this one also lets a frozen standalone Dhizuku recover without a UI bounce). ┌─────────────────────────────────────────────────────────────┐ │ This change was written with AI assistance (Claude / Claude │ │ Code). Please review the behavior on real hardware before │ │ merging. │ └─────────────────────────────────────────────────────────────┘ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f55f3f3 commit f267e94

1 file changed

Lines changed: 28 additions & 2 deletions

File tree

app/src/main/kotlin/com/looker/droidify/installer/installers/dhizuku/DhizukuPermission.kt

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import android.content.ComponentName
44
import android.content.Context
55
import android.content.Intent
66
import android.content.pm.PackageManager
7+
import android.net.Uri
8+
import android.util.Log
79
import com.looker.droidify.utility.common.extension.getLauncherActivities
810
import com.looker.droidify.utility.common.extension.intent
911
import com.rosan.dhizuku.api.Dhizuku
1012
import com.rosan.dhizuku.api.DhizukuRequestPermissionListener
13+
import com.rosan.dhizuku.shared.DhizukuVariables
1114
import kotlinx.coroutines.delay
1215
import kotlinx.coroutines.suspendCancellableCoroutine
1316
import kotlin.coroutines.resume
@@ -64,13 +67,36 @@ private const val DHIZUKU_WAKE_POLL_ATTEMPTS = 15
6467
private const val DHIZUKU_WAKE_POLL_DELAY_MS = 200L
6568
private const val DHIZUKU_INSTALLER_READY_ATTEMPTS = 25
6669
private const val DHIZUKU_INSTALLER_STABILITY_DELAY_MS = 200L
70+
private const val TAG = "DhizukuPermission"
71+
72+
/**
73+
* Gracefully unfreezes the Dhizuku server process. Built-in servers such as OwnDroid keep no
74+
* foreground service, so the process is frozen once backgrounded and the first bind returns
75+
* "error -74 (sent to frozen apps)". A *real* ContentProvider transaction is AMS-mediated and
76+
* unfreezes the target — unlike acquiring the client alone or a raw binder call. Best-effort; the
77+
* result is irrelevant, delivering the call is what forces the unfreeze. Callers still poll after.
78+
*/
79+
fun wakeDhizukuServer(context: Context) {
80+
runCatching {
81+
val authority = DhizukuVariables.getProviderAuthorityName(Dhizuku.getOwnerPackageName())
82+
val uri = Uri.parse("content://$authority")
83+
context.contentResolver.acquireUnstableContentProviderClient(uri)?.use { client ->
84+
runCatching { client.call("ping", null, null) }
85+
}
86+
}.onFailure { Log.w(TAG, "wakeDhizukuServer failed: ${it.message}") }
87+
}
6788

6889
suspend fun ensureDhizukuAlive(context: Context): Boolean {
6990
if (isDhizukuAlive(context)) return true
70-
if (!isDhizukuInstalled(context)) return false
71-
launchDhizuku(context)
91+
// Unfreeze a cached server (e.g. OwnDroid) without needing a launcher activity. The standalone
92+
// Dhizuku app additionally needs its UI launched to start the server; built-in servers have no
93+
// launcher and simply no-op here. Either way, poll until the server answers — and keep waking
94+
// it each round, since it can be re-frozen between attempts.
95+
wakeDhizukuServer(context)
96+
if (isDhizukuInstalled(context)) launchDhizuku(context)
7297
repeat(DHIZUKU_WAKE_POLL_ATTEMPTS) {
7398
delay(DHIZUKU_WAKE_POLL_DELAY_MS)
99+
wakeDhizukuServer(context)
74100
if (isDhizukuAlive(context)) return true
75101
}
76102
return false

0 commit comments

Comments
 (0)