RikkaApps / Shizuku-API

The API and the developer guide for Shizuku and Sui.
MIT License
950 stars 223 forks source link
android shizuku

Shizuku-API

Shizuku API is the API provided by Shizuku and Sui. With Shizuku API, you can call your Java/JNI code with root/shell (ADB) identity.

Requirements

To use Shizuku APIs, you need to guide the user to install Shizuku or Sui first. Both of them require Android 6.0+.

Shizuku

Shizuku is a standard Android application. You can guide the users to download Shizuku from https://shizuku.rikka.app/download/. Shizuku works for both rooted and non-rooted devices.

On non-rooted devices, Shizuku needs to be manually restarted with adb every time on boot. Before Android 11, a computer is required to run adb. Android 11 and above have built-in wireless debugging support, and users can start Shizuku directly on the device.

Sui

Sui is a Magisk module. Magisk requires an unlocked bootloader.

No additional setup is required except for the installation. You can guide the rooted users (searching su in PATH is enough) to download Sui from Magisk or https://github.com/RikkaApps/Sui.

Demo

A demo project is provided. See demo for more.

Guide

I'll say the difficult words first, using Shizuku APIs is similar to framework or system app development, some experience in developing common applications may not be enough. You have to get used to digging into Android source code to find out how things work, cs.android.com and AndroidXref sites will be your best friend.

Add dependency

Maven Central

def shizuku_version = (the version above)
implementation "dev.rikka.shizuku:api:$shizuku_version"

// Add this line if you want to support Shizuku
implementation "dev.rikka.shizuku:provider:$shizuku_version"

Acquire the Binder

The first step is to acquire the Binder from Shizuku or Sui.

Shizuku class provides listeners, Shizuku#addBinderReceivedListener() and Shizuku.addBinderDeadListener(), that allows you to track the life of the binder. You should call methods in Shizuku class when the binder is alive or you will get an IllegalStateException.

The steps to get a Binder from Sui and Shizuku are different.

Sui

Call Sui.init(packageName) before using Shizuku class. This method only needs to be called once. If this method returns true, means Sui is installed and available.

For multi-process applications, call this method in every process that needs to use Shizuku API.

Note, request the binder for Sui only requires two times of binder IPC, this is significantly cheaper than initialize Shizuku which uses ContentProvider. Sui.init(packageName) can be used in main thread, you don't need to worry about performance.

Shizuku

Add ShizukuProvider to AndroidManifest.xml.

<provider
    android:name="rikka.shizuku.ShizukuProvider"
    android:authorities="${applicationId}.shizuku"
    android:multiprocess="false"
    android:enabled="true"
    android:exported="true"
    android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />

<!-- android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" is to protect this provider from accessing by normal apps -->

For multi-process applications, you need to call ShizukuProvider.enableMultiProcessSupport() in every process which needs to use Shizuku API.

Starting from v12.1.0, Sui is initialized automatically in ShizukuProvider. You can opt-out this behavior by calling ShizukuProvider#disableAutomaticSuiInitialization() before ShizukuProvider#onCreate() is called. Unless there are special reasons, apps that support Shizuku should also support Sui, otherwise it will cause user confusion.

Request permission

Requesting permission is similar to requesting runtime permissions.

A simple example of requesting permission:

private void onRequestPermissionsResult(int requestCode, int grantResult) {
    boolean granted = grantResult == PackageManager.PERMISSION_GRANTED;
    // Do stuff based on the result and the request code
}

private final Shizuku.OnRequestPermissionResultListener REQUEST_PERMISSION_RESULT_LISTENER = this::onRequestPermissionsResult;

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...
    Shizuku.addRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER);
    // ...
}

@Override
protected void onDestroy() {
    // ...
    Shizuku.removeRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER);
    // ...
}

private boolean checkPermission(int code) {
  if (Shizuku.isPreV11()) {
    // Pre-v11 is unsupported
    return false;
  }

  if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
    // Granted
    return true;
  } else if (Shizuku.shouldShowRequestPermissionRationale()) {
    // Users choose "Deny and don't ask again"
    return false;
  } else {
    // Request the permission
    Shizuku.requestPermission(code);
    return false;
  }
}

Differents of the privilege betweent ADB and ROOT

Shizuku can be started with ADB or ROOT, and Sui is a Magisk module, so the privilege could be ADB or ROOT. You can use Shizuku#getUid() to check your privilege, for ROOT it returns 0, for ADB is 2000.

What ADB can do is significantly different from ROOT:

Remote binder call

This is a relatively simple way, but what you can do is limited to Binder calls. Therefore, this is only suitable for simple applications.

Shizuku API provides rikka.shizuku.ShizukuBinderWrapper class which forward Binder calls to Shizuku service which has ADB or ROOT privilege.

UserService

User Service is like Bound services which allows you to run Java or native codes (through JNI). The difference is that the service runs in a different process and as the identity (Linux UID) of root (UID 0) or shell (UID 2000, if the backend is Shizuku and user starts Shizuku with adb).

There are no restrictions on non-SDK APIs in the user service process. However, the User Service process is not a valid Android application process. Therefore, even if you can acquire a Context instance, many APIs, such as Context#registerReceiver and Context#getContentResolver will not work. You will need to dig into Android source code to find out how things work.

Be aware that, to let the service to use the latest code, "Run/Debug configurations" - "Always install with package manager" in Android Studio should be checked.

The use of non-SDK interfaces

For "Remote binder call", as the APIs are accessed from the app's process, you may need to use AndroidHiddenApiBypass or any ways you want to bypass restrictions on non-SDK interfaces.

We also provides HiddenApiRefinePlugin to help you to programing with hidden APIs conveniently.

Changelog

13.1.5

13.1.4

13.1.3

13.1.2

13.1.1

13.1.0

13.0.0

12.2.0

12.1.0

12.0.0

Migration guide for existing applications use Shizuku pre-v11

Click to expand ### Changes - Dependency changed (see Guide below) - Self-implemented permission is used from v11, the API is the same to runtime permission (see the demo, and existing runtime permission still works) - Package name was renamed to `rikka.shizuku` (replace all `moe.shizuku.api.` to `rikka.shizuku.`) - `ShizukuService` class is renamed to `Shizuku` - Methods in `Shizuku` class now throw `RuntimeException` on failure rather than `RemoteException` like other Android APIs - Listeners are moved from `ShizukuProvider` class to `Shizuku` class ### Add support for Sui - Call `Sui#init()` - It's better to use check Sui with `Sui#isSui` before using Shizuku only methods in `ShizukuProvider`