Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/topjohnwu/libsu

A complete solution for apps using root permissions
https://github.com/topjohnwu/libsu

Last synced: about 2 months ago
JSON representation

A complete solution for apps using root permissions

Awesome Lists containing this project

README

        

# libsu

[![](https://jitpack.io/v/topjohnwu/libsu.svg)](https://jitpack.io/#topjohnwu/libsu)

An Android library providing a complete solution for apps using root permissions.

`libsu` comes with 2 main components: the `core` module handles the creation of the Unix (root) shell process and wraps it with high level, robust Java APIs; the `service` module handles the launching, binding, and management of root services over IPC, allowing you to run Java/Kotlin and C/C++ code (via JNI) with root permissions.

## [Changelog](./CHANGELOG.md)

## [Javadoc](https://topjohnwu.github.io/libsu/)

## Download

```groovy
android {
compileOptions {
// The library uses Java 8 features
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
def libsuVersion = '6.0.0'

// The core module that provides APIs to a shell
implementation "com.github.topjohnwu.libsu:core:${libsuVersion}"

// Optional: APIs for creating root services. Depends on ":core"
implementation "com.github.topjohnwu.libsu:service:${libsuVersion}"

// Optional: Provides remote file system support
implementation "com.github.topjohnwu.libsu:nio:${libsuVersion}"
}
```

## Quick Tutorial

Please note that this is a quick demo going through the key features of `libsu`. Please read the full Javadoc and check out the example app (`:example`) in this project for more details.

### Configuration

Similar to threads where there is a special "main thread", `libsu` also has the concept of the "main shell". For each process, there is a single globally shared "main shell" that is constructed on-demand and cached. Set default configurations before the main `Shell` instance is created:

```java
public class SplashActivity extends Activity {

static {
// Set settings before the main shell can be created
Shell.enableVerboseLogging = BuildConfig.DEBUG;
Shell.setDefaultBuilder(Shell.Builder.create()
.setFlags(Shell.FLAG_MOUNT_MASTER)
.setInitializers(ShellInit.class)
.setTimeout(10));
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showSplashScreen();
// As an example, preload the main root shell in the splash screen
// so the app can use it afterwards without interrupting application
// flow (e.g. waiting for root permission prompt)
Shell.getShell(shell -> {
// The main shell is now constructed and cached
exitSplashScreen();
});
}
}

```

### Shell Operations

`Shell` operations can be performed through static `Shell.cmd(...)` methods that directly use the main root shell:

```java
Shell.Result result;
// Execute commands synchronously
result = Shell.cmd("find /dev/block -iname boot").exec();
// Aside from commands, you can also load scripts from InputStream.
// This is NOT like executing a script like "sh script.sh", but rather
// more similar to sourcing the script (". script.sh").
result = Shell.cmd(getResources().openRawResource(R.raw.script)).exec();

List out = result.getOut(); // stdout
int code = result.getCode(); // return code of the last command
boolean ok = result.isSuccess(); // return code == 0?

// Async APIs
Shell.cmd("setenforce 0").submit(); // submit and don't care results
Shell.cmd("sleep 5", "echo hello").submit(result -> updateUI(result));
Future futureResult = Shell.cmd("sleep 5", "echo hello").enqueue();

// Run commands and output to specific Lists
List mmaps = new ArrayList<>();
Shell.cmd("cat /proc/1/maps").to(mmaps).exec();
List stdout = new ArrayList<>();
List stderr = new ArrayList<>();
Shell.cmd("echo hello", "echo hello >&2").to(stdout, stderr).exec();

// Receive output in real-time
List callbackList = new CallbackList() {
@Override
public void onAddElement(String s) { updateUI(s); }
};
Shell.cmd("for i in $(seq 5); do echo $i; sleep 1; done")
.to(callbackList)
.submit(result -> updateUI(result));
```

### Initialization

Optionally, a similar concept to `.bashrc`, initialize shells with custom `Shell.Initializer`:

```java
public class ExampleInitializer extends Shell.Initializer {
@Override
public boolean onInit(Context context, Shell shell) {
InputStream bashrc = context.getResources().openRawResource(R.raw.bashrc);
// Here we use Shell instance APIs instead of Shell.cmd(...) static methods
shell.newJob()
.add(bashrc) /* Load a script */
.add("export ENV_VAR=VALUE") /* Run some commands */
.exec();
return true; // Return false to indicate initialization failed
}
}
Shell.Builder builder = /* Create a shell builder */ ;
builder.setInitializers(ExampleInitializer.class);
```

### Root Services

If interacting with a root shell is too limited for your needs, you can also implement a root service to run complex code. A root service is similar to [Bound Services](https://developer.android.com/guide/components/bound-services) but running in a root process. `libsu` uses Android's native IPC mechanism, binder, for communication between your root service and the main application process. In addition to running Java/Kotlin code, loading native libraries with JNI is also supported (`android:extractNativeLibs=false` **is** allowed). For more details, please read the full Javadoc of `RootService` and check out the example app for more details. Add `com.github.topjohnwu.libsu:service` as a dependency to access `RootService`:

```java
public class RootConnection implements ServiceConnection { ... }
public class ExampleService extends RootService {
@Override
public IBinder onBind(Intent intent) {
// return IBinder from Messenger or AIDL stub implementation
}
}
RootConnection connection = new RootConnection();
Intent intent = new Intent(context, ExampleService.class);
RootService.bind(intent, connection);
```

##### Debugging Root Services

If the application process creating the root service has a debugger attached, the root service will automatically enable debugging mode and wait for the debugger to attach. In Android Studio, go to **"Run > Attach Debugger to Android Process"**, tick the **"Show all processes"** box, and you should be able to manually attach to the remote root process. Currently, only the **"Java only"** debugger is supported.

### I/O

Add `com.github.topjohnwu.libsu:nio` as a dependency to access remote file system APIs:

```java
// Create the file system service in the root process
// For example, create and send the service back to the client in a RootService
public class ExampleService extends RootService {
@Override
public IBinder onBind(Intent intent) {
return FileSystemManager.getService();
}
}

// In the client process
IBinder binder = /* From the root service connection */;
FileSystemManager remoteFS;
try {
remoteFS = FileSystemManager.getRemote(binder);
} catch (RemoteException e) {
// Handle errors
}
ExtendedFile bootBlock = remoteFS.getFile("/dev/block/by-name/boot");
if (bootBlock.exists()) {
ExtendedFile bootBackup = remoteFS.getFile("/data/boot.img");
try (InputStream in = bootBlock.newInputStream();
OutputStream out = bootBackup.newOutputStream()) {
// Do I/O stuffs...
} catch (IOException e) {
// Handle errors
}
}
```

## License

This project is licensed under the Apache License, Version 2.0. Please refer to `LICENSE` for the full text.