Skip to content

XiaoTong6666/Sui

Repository files navigation

Sui

🇨🇳中文README

Modern super user interface (SUI) implementation on 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:

  1. Use Android Framework APIs directly, almost as if calling system APIs from Java as root or shell.
  2. 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:

  1. Text-based output: there is no structured data like PackageInfo in Java. You have to parse text.
  2. Slow: running a command means at least one new process is started, and PackageManager#getInstalledApplications is still called inside pm list.
  3. 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 Action button in KernelSU/Magisk 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.

Permission modes

Sui stores permission states by UID. The main modes are:

  • Ask / default: the app can connect to Sui and request permission through the normal flow.
  • 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 ExecTransact stage. Its Sui bridge transaction is swallowed before it can enter BridgeService and obtain the Sui Binder.

When the permission state changes, Sui may force-stop affected apps to cut off old Binder handles and make them obtain the correct backend on the next launch.

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.

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 example 0 for root and 2000 for shell
  • SystemServiceHelper.getSystemService(name)
  • ShizukuBinderWrapper, used to wrap Android Framework service binders
  • bindUserService(), used to start an app-defined Java service running as root or shell

Build

Clone with submodules:

git clone --recurse-submodules https://github.com/XiaoTong6666/Sui.git

Gradle 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:flashRelease

Internals

Sui requires Zygisk. Zygisk allows us to inject into system_server, SystemUI, Settings and related app processes.

In short, there are five parts:

  • 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.

  • SystemServer inject

    • Hooks Binder#execTransact to intercept the dedicated Binder transaction used by Sui inside system_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
  • SystemUI inject

    • Opens the Sui APK fd from Sui service and loads Sui Resources plus 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
  • Settings inject

    • Opens the Sui APK fd from Sui service and loads Sui Resources plus SuiActivity
    • Replaces ActivityThread instrumentation during Settings process startup
    • Maintains dynamic/pinned shortcuts and handles pinned-shortcut requests relayed from SystemUI
    • When the target Activity intent carries the Sui extra and token, instantiates and displays SuiActivity instead

License

Sui is licensed under GPL-3.0-or-later.

About

Modern super user interface implementation on Android.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors