A unified system for executing privileged adb and shell commands by abstracting away the differences between Shizuku and Root (Magisk/KernelSU).
The app uses ShellUtils as a high-level wrapper. This allows the rest of the app to call shell commands without worrying about whether the user has granted Root or Shizuku permissions.
object ShellUtils {
fun runCommand(context: Context, command: String) {
if (isRootEnabled(context)) {
RootUtils.runCommand(command)
} else {
ShizukuUtils.runCommand(command)
}
}
}The following are the most frequent use cases for shell commands in our ecosystem.
Trigger native system animations that are usually inaccessible to third-party apps.
- Charging Ripple:
cmd statusbar charging-ripple - Auth Ripple:
cmd statusbar auth-ripple custom $x $y(Requires coordinates) - Dismiss Keyboard:
cmd statusbar dismiss-keyboard
Modify system-level toggles that are typically marked as "Secure" or "Global".
- Airplane Mode:
settings put global airplane_mode_on 1 - Mono Audio:
settings put system master_mono 1 - Window Animation Scale:
settings put global window_animation_scale 0.5
Granting sensitive permissions to our own app or others without a PC.
pm grant <package_name> android.permission.WRITE_SECURE_SETTINGSpm grant <package_name> android.permission.DUMP
Forcefully enabling or disabling apps/components (used for features like App Freezer).
pm disable-user --user 0 <package_name>pm enable <package_name>
When we need to retrieve data from the system (e.g., checking if a specific service is running), we use output capture.
fun isServiceActive(serviceName: String): Boolean {
val output = ShellUtils.runCommandWithOutput(context, "service check $serviceName")
return output?.contains("Service $serviceName: found") == true
}- Background Execution: Shell commands involve IPC (Inter-Process Communication) and can block the main thread. Always wrap them in
Dispatchers.IO. - User Preference: Always provide a "Use Root" toggle in settings. If disabled, default to Shizuku Integration.
- Escape Commands: Be careful with command strings. If a package name or coordinate is dynamic, ensure it's properly sanitized to prevent injection.
- Fire and Forget: For animations like ripples, use a fire-and-forget approach to avoid waiting for the process to terminate.