ANDROID LIBRARY

Odin

Root operations, with a Kotlin API.

Odin is my Kotlin-first root-shell and RootService library for Android. It combines a persistent shell, suspend functions, streamed output and Binder/AIDL communication for code that needs a root process.

01

When a root shell belongs in the architecture

An Android utility may need to run shell commands or move privileged work into a separate process. Odin exposes both paths: a persistent shell for commands and a RootService framework for Binder/AIDL IPC. The library does not root a device; the device and user must already allow root access.

The design is inspired by topjohnwu’s libsu and reworks the interface around Kotlin and coroutines. Keeping root work behind ShellRepository also gives application code a clear boundary between its UI and privileged operations.

Run a command from a coroutineKotlin
import com.valhalla.superuser.ktx.RealShellRepository
import com.valhalla.superuser.ktx.ShellRepository

suspend fun inspectRoot() {
    val shell: ShellRepository = RealShellRepository()
    if (shell.isRootGranted()) {
        val result = shell.exec("id")
        if (result.isSuccess) println(result.stdout)
        else println("code=${result.code} err=${result.stderr}")
    }
}

Adapted from the public usage guide. Follow the repository’s installation instructions and check artifact availability before adding a dependency version.

02

Check access, then inspect the result

Root availability and command success are separate questions. isRootGranted() checks availability. exec() returns an exit code with separate stdout and stderr lists, so a caller can report a failure without treating every returned string as success.

A command that could not execute uses ShellResult.JOB_NOT_EXECUTED, with code −1. When multiple commands are passed to exec(), the returned exit code is from the last command. Execute and inspect separate calls when an earlier step must succeed before the next one runs.

03

Streaming has a cancellation boundary

Shell.cmd(...).asFlow() emits lines tagged as standard output or standard error. This is useful for presenting progress without losing the distinction between the two streams.

Cancelling a collector does not kill a command already running in the shared shell. The command drains to completion. Coroutine cancellation still propagates to the caller, so the app should distinguish ending observation from stopping the underlying operation.

A few practical questions

Does Odin work without root?

Its privileged operations require root access. Adding the library to an app does not grant Android permissions or root a device.

What is the minimum Android version?

The public README specifies minSdk 24, corresponding to Android 7.0.

Where are the complete APIs documented?

The repository’s docs/USAGE.md covers execution, streamed output, result handling and cancellation. It is the reference for integrating the library.

HAVE SOMETHING IN MIND?

Let’s make it happen.

A new app, an interesting role, or a good conversation.