Privilege UI
priv-ui is an optional Compose module for runtime authorization status, activation entry points, and exact replay of the last successful foreground method.
Public entry points
PrivilegeScaffoldprovides the embedded Compose page.PrivilegeUiViewModelis an openAndroidViewModelcontroller.PrivilegeUiConfigenables startup modes and external providers.PrivilegeUiExternalStartProviderintegrates an app-owned external path.PrivilegeUi.startSilentlyIfEnabled(...)replays only when the desired-state latch is enabled.PrivilegeUi.startSilently(...)performs the same exact replay regardless of that latch.
The scaffold consumes the host application's Material 3 theme. Wrap it in the app's own theme for branded light, dark, or dynamic colors.
Keep one application-scoped configuration
Create external providers and PrivilegeUiConfig once, then pass the same instance to the foreground ViewModel and the headless entry point:
class App : Application() {
val privilegeUiConfig by lazy {
PrivilegeUiConfig(
externalStartProviders = listOf(myShizukuProvider),
)
}
}
val serverInfo = PrivilegeUi.startSilentlyIfEnabled(
context = app,
config = app.privilegeUiConfig,
)Keep external providers application-scoped and Activity-free. Their identifiers are persistent keys and should remain stable across app upgrades.
Embed the scaffold
class MyPrivilegeUiViewModel(
application: Application,
) : PrivilegeUiViewModel(
application,
(application as App).privilegeUiConfig,
) {
override fun onBackClick(): Boolean {
return true
}
override fun onConnected(serverInfo: PrivilegeServerInfo) {
// Update host state after a new connection.
}
}
PrivilegeScaffold(
viewModel = viewModel<MyPrivilegeUiViewModel>(),
)The scaffold owns its Activity Result launchers and returns permission results to the same suspended ViewModel operation.
Understand exact replay
After a matching foreground operation receives its initial Binder connection, the UI stores one method identifier:
rootadb-wirelessadb-tcpipexternal:<providerId>
Silent replay is a headless, method-exact operation. It uses the saved method and returns null when history, authorization, or startup prerequisites are unavailable, leaving permission prompts, pairing, and external authorization to the foreground flow.
Foreground and silent attempts share a process-local start gate. Multi-process apps must designate one process to initialize and invoke Priv Kit startup. Accepted foreground effects retain their interactive lease until completion. While a silent attempt owns the gate, the built-in UI disables side-effecting entries and reconciles runtime state before enabling them again. A root manager may still show its own authorization UI when a remembered grant is no longer valid.
Desired state
The desired-state latch is stored as one byte, 1 or 0, in filesDir/.priv-kit/ui-desired-enabled. Every accepted INITIAL_LAUNCH connection writes 1, including a server started from a copied external shell command. OWNER_RECONNECT, disconnection, process death, and failed replay preserve the stored value. A confirmed stop or the built-in disconnected-state "Disable automatic recovery" action writes 0.
The latch tracks user intent separately from current server liveness. A launch outside a matching UI operation can enable it while replay history remains unchanged. Silent recovery starts when a UI-confirmed method is available; otherwise, startSilentlyIfEnabled(...) returns null and the built-in warning remains visible.
The last UI-confirmed method is stored in filesDir/.priv-kit/ui-start-method. startSilentlyIfEnabled(...) checks the desired-state byte first, then applies the same exact replay behavior as startSilently(...).
Host-owned integrations
The host app supplies Shizuku binding and any package, input, settings, app-ops, or logging tools it needs. Privilege UI stays focused on runtime status, activation, and recovery.