Sui
Modern super user interface (SUI) implementation for Android. The name, Sui, also comes from a character.
Introduction
Sui provides Java APIs, namely Shizuku API, for root / shell apps. It mainly provides two abilities:
- Use Android Framework APIs directly, almost as if calling system APIs from Java as root or shell.
- Start an app-defined AIDL-style Java service under root or shell.
This makes privileged Android app development much more comfortable.
Another advantage is that Sui does not add binaries to PATH and does not install a standalone manager app. This means we no longer need to spend a huge amount of time fighting apps that detect them.
To be clear, the full implementation of "root" is far more than su itself. There is a lot of hard work to be done before it. Sui is not a full root solution. It requires an existing root environment and runs as a Zygisk module.
Why "su" is unfriendly for app development
su, a "shell" running as root, is too far from the Android world.
To explain this, we need to briefly talk about how system APIs work. For example, we can use PackageManager#getInstalledApplications to get the app list. This is actually an inter-process communication (IPC) process between the app process and the system_server process. The Android Framework just hides the details for us.
Android uses Binder for this type of IPC. Binder allows the server side to learn the uid and pid of the client side, so system_server can check whether the app has permission to perform the operation.
Back to su. In a su environment, we usually only have commands provided by the Android system. In the same example, to get the app list with su, we have to run pm list. This is painful:
- Text-based output: there is no structured data like
PackageInfoin Java. You have to parse text. - Slow: running a command means at least one new process is started, and
PackageManager#getInstalledApplicationsis still called insidepm list. - Limited ability: commands only cover a small part of Android APIs.
Although it is possible to use Java APIs as root with app_process through libraries such as libsu or librootjava, transferring Binder objects between the app process and the root process is painful. If you want the root process to run as a daemon, once the app process restarts, there is no cheap way to get the Binder of the root process again.
In fact, for Magisk and other root solutions, making su work is not as easy as some people think. Both su itself and the communication between su and the manager app involve a lot of unpleasant work behind the scenes.
User guide
Note: the behavior of existing apps that only support su will NOT change.
Install
You can install Sui directly in KernelSU or another compatible root manager such as Magisk or APatch. Or download the zip from release and use Install from storage in your root manager.
Sui requires a compatible root environment. On Magisk, this means Magisk 24.0+ with Zygisk enabled. On KernelSU or APatch, it additionally requires a separate Zygisk implementation such as Zygisk Next, ReZygisk, or NeoZygisk. Do not add SystemUI or Settings to Zygisk DenyList, otherwise the injected management UI may not work properly.
Management UI
- Long press the System Settings icon on the home screen to see the Sui shortcut
- In the Sui management interface, tap the menu button in the top-right corner and select Add shortcut to home screen
- Enter
*#*#784784#*#*in the default dialer app - Open the Sui management interface via the module Action button in a supported root manager
Note: On some systems, the Sui shortcut may not appear when long-pressing Settings.
Additionally, to avoid disturbing users, newer versions have removed the feature that automatically prompts to add a shortcut when entering Developer options.
The top-right menu is also the preferred place to configure Sui. Besides search/filter, shortcut and appearance options, it currently provides:
- Modify default value: set the permission inherited by apps that do not have an explicit per-UID rule. The default can be Ask, root, shell, Deny, or Hide.
- Legacy Shizuku support: optionally serve the old explicit
REQUEST_BINDERbroadcast protocol used by older Shizuku clients. The compatibility hook is bypassed when Shizuku itself is installed and keeps the normal Sui permission/Hide routing rules. - Block Shell root (KSU): on supported KernelSU versions, mark the Sui shell process before it drops to UID 2000 so Rish, shell-mode Shizuku API calls, UserService processes, and their descendants cannot use KernelSU to escalate back to root. A reboot is required. If the running KernelSU does not support the required UAPI, Sui automatically disables this option instead of preventing the shell backend from starting.
- ADB Root: choose Off, Enable for next boot once, or Always enable. A reboot is required for the selected mode to take effect.
These options are persisted by the Sui service. Some boot-time options still use files under /data/adb/sui/ internally, but normal users should change them from the management UI instead of creating or deleting marker files manually.
Permission modes
Sui stores explicit permission states by UID. Tap an app in the management UI to select one of the explicit modes, or choose Default to remove the explicit rule and inherit the global default configured through Modify default value.
- Allow root: the app will be routed to the root backend.
- Allow shell: the app will be routed to the shell backend.
- Deny: deny the app from using Sui.
- Hide: hide Sui from the target app. When Hide is enabled, the target app UID is intercepted in the Native Binder
execTransactstage. Its Sui bridge transaction is swallowed before it can enter BridgeService and obtain the Sui Binder. - Default: no explicit per-UID permission is stored. The app inherits the current global default. When that global default is Ask, the app can connect to Sui and request permission through the normal flow.
When a permission state changes, Sui synchronizes the system_server and shell routing state and migrates compatible clients when it is safe to do so. Transitions that revoke a previously held capability, and legacy clients that cannot accept a live Binder handoff, may be force-stopped or restarted so stale privileged Binder handles cannot survive the permission change.
Interactive shell
Sui provides an interactive shell.
Since Sui does not add files to PATH, the required files need to be copied manually. See /data/adb/sui/post-install.example.sh to learn how to do this automatically.
After the files are correctly copied, use rish as sh to start an interactive shell.
adb root
Sui also provides optional adb root support. When enabled, Sui sets up an adbd wrapper plus preload hook so that adbd can run under the current root implementation's SELinux domain while keeping the expected adbd socket label.
This feature is disabled by default. Open the Sui management UI, tap the top-right menu, choose ADB Root, then select one of the following modes:
- Off: keep
adb rootsupport disabled. - Enable for next boot once: enable the setup for the next boot only. The one-shot state is consumed during
post-fs-data, so the UI falls back to Off after that boot. - Always enable: enable the setup on every boot until you change the mode back to Off.
Reboot after changing the mode. After the device boots with ADB Root enabled, use adb root normally.
This feature depends on your root implementation and SELinux policy. Sui checks the required
setcurrent,dyntransition, andsetsockcreatepermissions before enabling it. Existing app behavior does not change. This only affects the deviceadbdpath. If your device uses a heavily customizedadbdimplementation, compatibility may vary.
Application development guide
Sui app development should still primarily follow the upstream Shizuku API documentation:
https://github.com/RikkaApps/Shizuku-API
Apps are recommended to use rikka.shizuku.Shizuku as the unified compatibility layer. Do not maintain a Sui-only code path. In this way, one wrapper can support both Shizuku and Sui.
In the normal integration pattern, you only need ShizukuProvider plus the regular Shizuku API flow. ShizukuProvider already attempts Sui initialization automatically, so app code usually does not need to import or call rikka.sui.Sui directly.
If you intentionally disable ShizukuProvider's automatic Sui initialization, you can still call Sui.init(packageName) manually inside your wrapper. If it receives a Binder, it passes it to the Shizuku API layer; if not, the app can continue with the normal Shizuku flow.
Example pattern with the normal auto-initialization flow:
import android.content.pm.PackageManager
import android.content.pm.IPackageManager
import rikka.shizuku.Shizuku
import rikka.shizuku.ShizukuBinderWrapper
import rikka.shizuku.SystemServiceHelper
fun initPrivilegedApi() {
Shizuku.addBinderReceivedListener {
checkShizukuPermission()
}
if (Shizuku.pingBinder()) {
checkShizukuPermission()
}
}
fun checkShizukuPermission() {
if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
val binder = SystemServiceHelper.getSystemService("package")
?: return
val pm = IPackageManager.Stub.asInterface(
ShizukuBinderWrapper(binder)
)
pm.isPackageAvailable("android", 0)
} else {
Shizuku.requestPermission(0)
}
}If you want manual initialization instead, add import rikka.sui.Sui and call Sui.init(packageName) before waiting for the binder.
Common APIs include:
Shizuku.pingBinder()Shizuku.checkSelfPermission()Shizuku.requestPermission(requestCode)Shizuku.getUid(), which can be used to check the current backend identity, for example0for root and2000for shellSystemServiceHelper.getSystemService(name)ShizukuBinderWrapper, used to wrap Android Framework service bindersbindUserService(), used to start an app-defined Java service running as root or shell
Build
Note: Clone the repository with submodules, otherwise required API projects will be missing.
git clone --recurse-submodules https://github.com/XiaoTong6666/Sui.gitGradle tasks:
BuildType could be Debug or Release.
-
:module:assemble<BuildType>Build the module. After assemble finishes, the flashable module zip will be generated to
out. -
:module:zip<BuildType>Generate the flashable module zip to
out. -
:module:push<BuildType>Push the zip with adb to
/data/local/tmp. -
:module:flash<BuildType>Install the zip with
adb shell su -c magisk --install-module. -
:module:flashWithKsud<BuildType>Install the zip with
adb shell su -c ksud module install. -
:module:flashAndReboot<BuildType>Install the zip and reboot the device.
-
:module:flashWithKsudAndReboot<BuildType>Install the zip with ksud and reboot the device.
For example:
./gradlew :module:assembleRelease
./gradlew :module:zipRelease
./gradlew :module:flashReleaseTroubleshooting
Capture Sui logs
adb logcat -v time | grep -i suiHow to report problems
If you need to report a problem, please provide logs reproduced on a debug build.
- Install or flash a debug build of Sui and reproduce the issue.
- If you use KernelSU or APatch, export logs from the root manager first. These logs are usually more complete for module mounting, Zygisk injection, SELinux, and early boot/runtime issues.
- Also capture Sui logs.
- Include basic environment information:
- root implementation and version
- Zygisk implementation and version
- Android version / ROM
- whether
SystemUIorSettingsis in DenyList - exact reproduction steps
If the issue cannot be reproduced on the debug build and only happens on release builds, include a short description of the release-only behavior and the exact reproduction steps.
Cannot access the Sui management interface
- Your root environment is supported (Magisk with Zygisk enabled, or KernelSU/APatch with a compatible Zygisk implementation).
SystemUIandSettingsare not included in the Zygisk DenyList.- The device has been rebooted after installing or updating Sui.
Optional features do not work as expected
- For ADB Root and Block Shell root (KSU), change the option from the Sui management UI and reboot once so the boot-time configuration can take effect.
- If needed, export logs from KernelSU / APatch and capture Sui logs.
- Marker files under
/data/adb/sui/are implementation details. Inspect them only when diagnosing a problem; they normally do not need to be edited by hand.
Internals
Sui requires Zygisk. Zygisk allows us to inject into system_server, SystemUI, Settings and related app processes.
Overall, there are five main parts, plus optional boot-time paths such as adb root and KernelSU shell no-escape protection:
-
Root process
This is a root process started by the root implementation during the post-fs-data stage. It starts a Java server that implements Shizuku API and private APIs used by other parts.
The root server is the main source of permission configuration. It maintains the UID permission database and syncs hidden, root allowed, shell allowed, denied and default mode states to system_server.
-
Shell process
The shell server runs as shell and serves apps granted with shell permission.
It loads UID permission states from the configuration file mirrored by the root server. When the shell backend needs to show a permission confirmation window, it delegates the request to the root server, which then triggers the SystemUI confirmation UI.
When Block Shell root (KSU) is enabled, the native launcher asks a supported KernelSU driver to apply
DISABLE_ESCAPE_TO_ROOTbefore dropping the shell child to UID 2000. The restriction is inherited by descendants. Unsupported KernelSU versions cause the option to be cleared and shell startup continues without this protection. -
SystemServer inject
- Hooks
Binder#execTransactto intercept the dedicated Binder transaction used by Sui insidesystem_server - Keeps the root binder, shell binder, and permission caches for hidden/root allowed/shell allowed/denied/default mode
- Chooses which backend Binder to return based on the UID's effective permission: root gets the root binder, shell gets the shell binder
- For hidden UIDs, blocks the Sui bridge request directly; for ask/deny, still returns the root binder so the client can continue through the normal permission or denial result flow
- When Legacy Shizuku support is enabled, also recognizes the old explicit Shizuku
REQUEST_BINDERbroadcast path and returns the same permission-routed Sui Binder through the caller's callback Binder
- Hooks
-
SystemUI inject
- Opens the Sui APK fd from Sui service and loads Sui
Resourcesplus the permission dialog class - Attaches to the service and shows permission confirmation dialogs on callback
- Registers secret-code style entry points and, when triggered, launches the Sui management UI hosted in the Settings process
- Opens the Sui APK fd from Sui service and loads Sui
-
Settings inject
- Opens the Sui APK fd from Sui service and loads Sui
ResourcesplusSuiActivity - Replaces
ActivityThreadinstrumentation during Settings process startup - Maintains dynamic/pinned shortcuts and handles pinned-shortcut requests relayed from SystemUI
- When the target
Activityintent carries the Sui extra and token, instantiates and displaysSuiActivityinstead
- Opens the Sui APK fd from Sui service and loads Sui
-
adbd wrapper / preload (optional)
- During
post-fs-data, whenadb rootsupport is enabled, Sui prepares anadbdwrapper and preload library for/apex/com.android.adbd/bin/adbdor/system/bin/adbd - The wrapper rewrites
--root_seclabel=...to the current root implementation's SELinux domain and injectsLD_PRELOAD - The preload hook intercepts
selinux_android_setcon()/setcon()soadbdcan switch into the root domain while restoringsockcreateto the expectedadbdlabel
- During
License
Sui is licensed under GPL-3.0-or-later.