AI prompts
base on Modern super user interface implementation on Android. # Sui
Modern super user interface (SUI) implementation on Android. <del>The name, Sui, also comes from [a character](https://www.pixiv.net/artworks/71435059).</del>
## Introduction
Sui provides Java APIs, [Shizuku API](https://github.com/RikkaApps/Shizuku-API), for root apps. It mainly provides the ability to use Android APIs directly (almost in Java as the identity of the root, and start app's own AIDL-style Java service under root. This will make root app development much more comfortable.
Another advantage is that Sui does not add binaries to `PATH` and does not install a manager app. This means we no longer need to spend a huge amount of time to fight with 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. Sui is not a full root solution, it requires Magisk to run.
<details>
<summary>Why "su" is unfriendly for app development</summary>
The "su", a "shell" runs as root, is too far from the Android world.
To explain this, we need to talk about how system API works. For example, we can use `PackageManager#getInstalledApplications` to get the app list. This is actually an interprocess communication (IPC) process of the app process and system server process, just the Android framework did the inner works for us. Android uses `Binder` to do this type of IPC. `Binder` allows the server-side to learn the uid and pid of the client-side so that the system server can check if the app has the permission to do the operation.
Back to "su", there are commands provided by the Android system. In the same example, to get the app list with "su", we have to use `pm list`. This is too painful.
1. Text-based, this means there is no structured data like `PackageInfo` in Java. You have to parse the output text.
2. It is much slower because run a command means at least one new process is started. And `PackageManager#getInstalledApplications` is used inside `pm list`.
3. The possibility is limited to how the command can do. The command only covers a little amount of Android APIs.
Although it is possible to use Java APIs as root with `app_process` (there are libraries like libsu and librootjava), transfer binder between app process and root process is painful. If you want the root process to run as a daemon. When the app process restarts, it has no cheap way to get the binder of the root process.
In fact, for Magisk and other root solutions, makes the "su" to work is not that easy as some people think (let "su" itself work and the communication between the "su" and the manager app have a lot of unhappy work behind).
</details>
## User guide
Note, the behavior of existing apps that only supports "su" will NOT change.
### Install
You can download and install Sui from Magisk directly. Or, download the zip from [release](https://github.com/RikkaApps/Sui/releases/) and use "Install from storage" in Magisk.
### Management UI
- (Android 8.0+, Sui 12.1+) Long press system settings from the home app, you will find the shortcut of Sui
- (Android 8.0+, Sui 12+) Enter "Developer options" in system settings, the system will ask you to add the shortcut of Sui
- Enter `*#*#784784#*#*` in the default dialer app
Note, the shortcut way requires your home app supports shortcut APIs that adds from Android 7.0 and 8.0. Unless you are using a old home app, you can the shortcut with no problem.
### Interactive shell
Sui provides interactive shell.
Since Sui does not add files to `PATH`, the 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'.
## Application development guide
https://github.com/RikkaApps/Shizuku-API
## Build
Clone with `git clone --recurse-submodules`.
Gradle tasks:
`Flavor` could be `Riru` and `Zygisk`, and `BuildType` could be `Debug` and `Release`.
* `:module:assemble<Flavor><BuildType>`
Generate Magisk module zip to `out`.
* `:module:push<Flavor><BuildType>`
Push the zip with adb to `/data/local/tmp`.
* `:module:flash<Flavor><BuildType>`
Install the zip with `adb shell su -c magisk --install-module`.
* `:module:flashAndReboot<Flavor><BuildType>`
Install the zip and reboot the device.
## Internals
Sui requires [Magisk](https://github.com/topjohnwu/Magisk) (and [Riru](https://github.com/RikkaApps) for non-Zygisk version). Magisk allows us to run processes as uid 0 and a "do anything" SELinux context. Riru or Zygisk allows us to inject into system server process and app processes.
In short, there are four parts:
- Root process
This is a root process started by Magisk. This process starts a Java server that implements Shizuku API and private APIs used by other parts.
- SystemServer inject
- Hooks `Binder#execTransact` and finally allow us to handle an unused binder call
- Implements "get binder", "set binder" logic in that binder call, so that root process can send its binder to the system server, and the apps can acquire root process's binder
- SystemUI inject
- Acquire the fd of our apk from the root server, create a `Resource` instance from it
- Show confirmation window with our `Resource` and `ClassLoader` when recevied callback from the root server
- Settings inject
- Acquire the fd of our apk from the root server, create a `Resource` instance from it
- Publish shortcut which targets an existing `Acitivty` but with a special intent extra
- Replace `ActivityThread#mInstrumentation` to intervene the `Acitivty` instantiate process, if the intent has the speical extra, create our `Activity` which uses our `Resource` and `ClassLoader`
", Assign "at most 3 tags" to the expected json: {"id":"9002","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"