Elevation to shell identity for Android apps.
Shellom allows an Android app to adopt privileged shell (ADB) permissions at runtime, no root required.
- Android 12+ (API 31): Required for
--no-restartinstrumentation. - ADB Connection: Required to trigger the elevation command.
Register the ShellRunner in your manifest to handle the instrumentation process.
<instrumentation
android:name="com.indidevs.android.shellom.ShellRunner"
android:targetPackage="your.package.id" />The ShellExecutor routes the elevation command through your ADB channel.
Simple connection:
val executor = ShellExecutor { cmd ->
myAdb.execute(cmd) // Must return Result<Unit>
}Reactive connection:
If your ADB session is dynamic (e.g. a StateFlow), use fromFlow. It suspends until an executor is available.
val executor = ShellExecutor.fromFlow(adbSessionFlow.map { session ->
session?.let { s -> ShellExecutor { cmd -> s.execute(cmd) } }
})Initialize the provider and call awaitContext(). It will trigger elevation if needed and return a Context with the requested permissions.
val provider = InstrumentationShellProvider(context, scope, executor, listOf(
"android.permission.WRITE_SECURE_SETTINGS"
))
// Suspend until ready
val privilegedContext = provider.awaitContext() Monitor the elevation lifecycle (IDLE, ELEVATING, READY, ERROR) via the status StateFlow or the observeStatus helper.
provider.observeStatus(lifecycleScope) { status ->
when(status) {
ShellStatus.ELEVATING -> showLoading()
ShellStatus.READY -> proceed()
ShellStatus.ERROR -> showError()
else -> Unit
}
}To clear the cached privileged context and allow a fresh elevation attempt:
provider.reset()If you use a custom AndroidJUnitRunner subclass, pass its name to the provider:
val provider = InstrumentationShellProvider(..., runnerClass = "com.myapp.MyRunner")dependencies {
implementation("com.indidevs.android:shellom:0.1.0")
}