Skip to content

Commit

Permalink
API v2.4
Browse files Browse the repository at this point in the history
  • Loading branch information
iamr0s committed Jul 5, 2023
1 parent 4eaefed commit 009d517
Show file tree
Hide file tree
Showing 71 changed files with 889 additions and 773 deletions.
99 changes: 22 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Dhizuku API

Dhizuku API是[Dhizuku](https://github.com/iamr0s/Dhizuku)的API。你可以通过Dhizuku API调用系统接口,完成共享设备所有者功能(Device Owner)。
English | [简体中文](README_zh_rCN.md)

### 导入
Dhizuku API is the api of [Dhizuku](https://github.com/iamr0s/Dhizuku). Used to share device owner from Dhizuku.

### Import

![Maven Central](https://img.shields.io/maven-central/v/io.github.iamr0s/Dhizuku-API)

Expand All @@ -11,17 +13,17 @@ def dhizuku_version = "version of api"
implementation "io.github.iamr0s:Dhizuku-API:$dhizuku_version"
```

### 初始化
### Initialize

执行如下代码从而初始化Dhizuku-API,如果初始化失败(Dhizuku不存在、未激活)就开始调用其余API会抛出错误
Initialize the dhizuku-API, call other API interfaces may throws an exception when initialization fails.

```java
Dhizuku.init(context) // return boolean
```

### 请求权限
### Request Permission

某些API接口需要先请求权限才能运行
Some API interfaces require permission to run.

```java

Expand All @@ -39,87 +41,30 @@ Dhizuku.requestPermission(new DhizukuRequestPermissionListener() {
});
```

### 使用方法与更改记录

### v2.0 (3)

### 开启一个自定义服务(UserService)

会向Dhizuku服务器发起一个请求,用于开启UserService,类似于Android系统本身的Service,但是是基于AIDL。

具体实现请参考 [Demo UserService](https://github.com/iamr0s/Dhizuku-API/tree/main/demo-user_service)
### API interface and changes

如果你用过 [Shizuku](https://shizuku.rikka.app/zh-hans/) 的UserService,那么你应该可以很好的理解这个功能,但是他们之间也存在着一些不同,请仔细阅读下列内容
### Binder Wrapper

一般的使用步骤为:
1. 首先自定义一个[UserService AIDL](https://github.com/iamr0s/Dhizuku-API/blob/main/demo-user_service/src/main/aidl/com/rosan/dhizuku/demo_user_service/IUserService.aidl)文件
IBinder is often used for basic communication between applications and systems. Dhizuku provides an interface for proxy IBinder, the applications can communicate with the system as Dhizuku.

> 请注意,20及以内的transact code是保留给未来的Dhizuku APi使用的,您不能轻易使用它,你的自定义操作应该从21开始
[BinderWrapper Demo](https://github.com/iamr0s/Dhizuku-API/blob/main/demo-binder_wrapper)

2. Build你的项目,使得Android Studio生成对应于AIDL的Stub子类
### User Service

3. 实现这个Stub类型,即 [UserService](https://github.com/iamr0s/Dhizuku-API/blob/main/demo-user_service/src/main/java/com/rosan/dhizuku/demo_user_service/UserService.java)
> Dhizuku.getVersionCode() >= 3
> 你有两种构造器函数实现可选,即带Context参数或不带,当你两种构造器都存在时,有限选择带构造器的。
>
> 注意:
>
> 1. Context无法调用Android的四大组件
> 2. UserService的生命周期与发起者有关,当所有发起者的进程处于死亡状态时,UserService自动退出
> 3. 当UserService被Dhizuku关闭或被强制退出时,UserService的onDestory方法不保证一定调用,
A simple Service based on the AIDL mechanism that runs in the isolated space provided by Dhizuku.

4. 享用一个带有设备所有者权限的 Service

### v1.0.1

### 远程执行一段命令(newProcess)

```java
String[] cmd = new String[]{"whoami"};
String[] env = null;
File dir = null;
try {
Process process = Dhizuku.newProcess(cmd, env, dir);
process.waitFor();
InputStream input = process.getInputStream();
InputStream err = process.getErrorStream();
byte[] bytes = new byte[input.available()];
input.read(bytes);
Log.e("dhizuku-api", "input " + new String(bytes));
bytes = new byte[err.available()];
Log.e("dhizuku-api", "error " + new String(bytes));
} catch (InterruptedException | IOException e) {
throw new RuntimeException(e);
}
```
Usage: Declare an AIDL file, implement it in Service, and then launch it through the interface provided by Dhizuku.

### 代理一个IBinder(binderWrapper)
[UserService Demo](https://github.com/iamr0s/Dhizuku-API/blob/main/demo-user_service)

应用与应用之间、应用与系统服务之间的交流主要通过IBinder完成,Dhizuku支持代理IBinder。如果你用过[Shizuku](https://shizuku.rikka.app/zh-hans/)的api,我相信你能很好的理解这个接口。
### Delegated Scopes

### 方法一
> Dhizuku.getVersionCode() >= 5
直接使用IBinder、IInterface

```java
IPackageManager packageManager = IPackageManager.Stub.asInterface(Dhizuku.binderWrapper(ServiceManager.getService("package")));
IPackageInstaller packageInstaller = IPackageInstaller.Stub.asInterface(Dhizuku.binderWrapper(packageManager.getPackageInstaller().asBinder()));
// packageInstaller.uninstall(...) // do some code, use the IInterface
```
### 方法二

间接使用IBinder、IInterface

当然我更喜欢这种方法去使用binderWrapper。比如系统的PackageManager本质上是对IPackageManager的封装,因此我们完全可以替换其中的IPackageManager对象为我们代理过的,从而实现几乎无痛代理。

[PackageInstallerHelper.java](https://github.com/iamr0s/Dhizuku-API/blob/main/demo/src/main/java/com/rosan/dhizuku/demo/PackageInstallerHelper.java)

```java
PackageInstaller packageInstaller = context.getPackageManager().getPakageInstaller();
PackageInstallerHelper.proxy(packageInstaller);
// packageInstaller.uninstall(...) // do some code, just like you are Device Owner
```
Invoking the DevicePolicyManager from either the Binder Wrapper or the User Service can be complicated, but Delegated Scopes can simplify this operation.

比如UserManager,也是针对IUserManager的封装,同样的方法替换它的mService为经过代理的。
Usage: You grant Delegated Scopes to your application, which can then invoke the interface included in the Delegated Scopes itself through DevicePolicyManager.

需要注意,Google在Android P之后对部分接口做出了限制(@hide),禁止普通应用调用(禁止反射),需要通过[AndroidHiddenApiBypass](https://github.com/LSPosed/AndroidHiddenApiBypass)解除限制。
[DelegatedScopes Demo](https://github.com/iamr0s/Dhizuku-API/blob/main/demo-delegated_scopes)
72 changes: 72 additions & 0 deletions README_zh_rCN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Dhizuku API

简体中文 | [English](README.md)

Dhizuku API 是 [Dhizuku](https://github.com/iamr0s/Dhizuku) 的 API。

## 导入


![Maven Central](https://img.shields.io/maven-central/v/io.github.iamr0s/Dhizuku-API)


```groovy
def dhizuku_version = "version of api"
implementation "io.github.iamr0s:Dhizuku-API:$dhizuku_version"
```

### 初始化

执行如下代码从而初始化Dhizuku-API,如果初始化失败(Dhizuku不存在、未激活)就开始调用其余API会抛出错误

```java
Dhizuku.init(context) // return boolean
```

### 请求权限

某些API接口需要先请求权限才能运行

```java

if (Dhizuku.isPermissionGranted()) return

Dhizuku.requestPermission(new DhizukuRequestPermissionListener() {
@Override
public void onRequestPermission(int grantResult) throws RemoteException {
if (grantResult == PackageManager.PERMISSION_GRANTED) {
// do success code
}else {
// do failure code
}
}
});
```

### 接口与更改记录

### Binder Wrapper

IBinder 常用于普通应用与系统的基础通信方式。Dhizuku 提供一个接口用于代理 IBinder,应用可以通过此接口以 Dhizuku 的身份与系统通信。

[BinderWrapper Demo](https://github.com/iamr0s/Dhizuku-API/blob/main/demo-binder_wrapper)

### User Service

> Dhizuku.getVersionCode() >= 3
一个基于 AIDL 机制的简易 Service,Service 运行于 Dhizuku 提供的隔离空间。

通常的用法是先声明一个 AIDL 文件,再于 Service 中将其实现,随后通过 Dhizuku 提供的接口将其启动。

[UserService Demo](https://github.com/iamr0s/Dhizuku-API/blob/main/demo-user_service)

### Delegated Scopes

> Dhizuku.getVersionCode() >= 5
不论是在 Binder Wrapper 还是 User Service 调用 DevicePolicyManager 都显得颇为复杂,通过 Delegated Scopes 可以简化这个操作。

通常的用法是给应用授予 Delegated Scopes,随后应用可以自行通过 DevicePolicyManager 调用被纳入 Delegated Scopes 的接口。

[DelegatedScopes Demo](https://github.com/iamr0s/Dhizuku-API/blob/main/demo-delegated_scopes)
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
ext {
groupId = "io.github.iamr0s"
artifactId = "Dhizuku-API"
artifactVersion = "2.3"
artifactVersion = "2.4"
POM_NAME = "Dhizuku API"
POM_DESCRIPTION = "API of Dhizuku."
POM_URL = "https://github.com/iamr0s/Dhizuku-API"
Expand Down
File renamed without changes.
44 changes: 44 additions & 0 deletions demo-binder_wrapper/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
plugins {
id 'com.android.application'
}

android {
namespace 'com.rosan.dhizuku.demo'
compileSdk 33

defaultConfig {
applicationId "com.rosan.dhizuku.binder_wrapper"
minSdk 26
targetSdk 33
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildFeatures {
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

implementation project(":hidden-api")
implementation project(":dhizuku-api-impl")
}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.rosan.dhizuku.demo_user_service;
package com.rosan.dhizuku.demo;

import android.content.Context;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:name="com.rosan.dhizuku.demo.App"
android:allowBackup="true"
android:icon="@android:drawable/sym_def_app_icon"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.DhizukuAPI"
android:icon="@android:drawable/sym_def_app_icon">
android:theme="@style/Theme.DhizukuAPI">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.DhizukuAPI">
android:name="com.rosan.dhizuku.demo.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.rosan.dhizuku.demo_user_service;
package com.rosan.dhizuku.demo;

import android.app.Application;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.rosan.dhizuku.demo;

import android.widget.Toast;

import androidx.activity.ComponentActivity;
import androidx.annotation.StringRes;

import java.util.Arrays;
import java.util.List;

public class BaseActivity extends ComponentActivity {
protected void toast(@StringRes int resId) {
toast(getString(resId));
}

protected void toast(Object... objects) {
runOnUiThread(() -> {
Toast.makeText(this, join(objects), Toast.LENGTH_SHORT).show();
});
}

String join(List<Object> objects) {
String sep = " ";
StringBuilder builder = new StringBuilder();
int count = 0;
for (Object element : objects) {
if (++count > 1) builder.append(" ");
builder.append(element == null ? "null" : element);
}
return builder.toString();
}

String join(Object... objects) {
return join(Arrays.asList(objects));
}
}
Loading

0 comments on commit 009d517

Please sign in to comment.