Shizuku开发

Shizuku开源地址:https://github.com/RikkaApps/Shizuku
Shizuku开发指南:https://github.com/RikkaApps/Shizuku-API

示例项目开源地址:https://github.com/xxinPro/AdbShellUtils

安装Shizuku

该方案通过Shizuku给自己的软件授予adb shell权限,但前提是Shizuku必须拥有adb shell权限,具体可以参考

Shizuku用户手册:https://shizuku.rikka.app/zh-hans/guide/setup/

引入依赖

可以选择引入maven central中的远程依赖

1
2
3
4
5
def shizuku_version = "13.1.5"
implementation "dev.rikka.shizuku:api:$shizuku_version"
// implementation "dev.rikka.shizuku:aidl:$shizuku_version" // 不需要在app gradle中引入
// implementation "dev.rikka.shizuku:shared:$shizuku_version" // 不需要在app gradle中引入
implementation "dev.rikka.shizuku:provider:$shizuku_version"

亦或者引入Shizuku-API中对应的模块

1
2
3
4
implementation project(':api')
// implementation project(':shared') // 不需要在app gradle中引入
// implementation project(':aidl') // 不需要在app gradle中引入
implementation project(':provider')

添加支持

在清单文件中加入如下代码

1
2
3
4
5
6
7
<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" />

进行完这一步,已经可以发现在Shizuku的应用管理中出现了目标app

判断权限

判断当前app是否拥有shizuku中的adb shell权限

1
2
3
4
5
6
/**
* 判断是否拥有shizuku adb shell权限
*/
private boolean checkPermission() {
return Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED;
}

动态申请权限

动态申请Shizuku adb shell权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void requestShizukuPermission() {
boolean checked = checkPermission();
if (checked) {
Toast.makeText(this, "已拥有权限", Toast.LENGTH_SHORT).show();
return;
}

if (Shizuku.isPreV11()) {
Toast.makeText(this, "当前shizuku版本不支持动态申请", Toast.LENGTH_SHORT).show();
return;
}

// 动态申请权限
Shizuku.requestPermission(MainActivity.PERMISSION_CODE);
}

Shizuku授权监听

1
2
3
4
5
6
7
8
9
10
11
private final Shizuku.OnRequestPermissionResultListener onRequestPermissionResultListener = new Shizuku.OnRequestPermissionResultListener() {
@Override
public void onRequestPermissionResult(int requestCode, int grantResult) {
boolean granted = grantResult == PackageManager.PERMISSION_GRANTED;
if (granted) {
Toast.makeText(MainActivity.this, "授权成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "授权失败", Toast.LENGTH_SHORT).show();
}
}
};

授权监听需要手动添加

1
2
// 添加权限申请监听
Shizuku.addRequestPermissionResultListener(onRequestPermissionResultListener);

Activity销毁时移除授权监听

1
2
// 移除权限申请监听
Shizuku.removeRequestPermissionResultListener(onRequestPermissionResultListener);

Shizuku状态监听

Shiziku服务被启动时会调用该监听,例如app运行期间,通过电脑abd命令启动Shizuku服务,将会立即调用该监听;此外,在app启动时倘若Shiziku服务处于启动状态,也会调用该监听

1
2
3
4
5
6
7
8
9
// Shiziku服务启动时调用该监听
Shizuku.addBinderReceivedListenerSticky(onBinderReceivedListener);

private final Shizuku.OnBinderReceivedListener onBinderReceivedListener = new Shizuku.OnBinderReceivedListener() {
@Override
public void onBinderReceived() {
Toast.makeText(MainActivity.this, "Shizuku服务启动", Toast.LENGTH_SHORT).show();
}
};

Shiziku服务被终止时调用该监听,若app运行期间,Shizuku服务异常终止或手动停止,将会立即调用该监听

1
2
3
4
5
6
7
8
9
// Shiziku服务终止时调用该监听
Shizuku.addBinderDeadListener(onBinderDeadListener);

private final Shizuku.OnBinderDeadListener onBinderDeadListener = new Shizuku.OnBinderDeadListener() {
@Override
public void onBinderDead() {
Toast.makeText(MainActivity.this, "Shizuku服务终止", Toast.LENGTH_SHORT).show();
}
};

Activity销毁时移除监听

1
2
Shizuku.removeBinderReceivedListener(onBinderReceivedListener);
Shizuku.removeBinderDeadListener(onBinderDeadListener);

执行命令

appgradle文件中添加如下配置

1
2
3
4
5
6
android {
buildFeatures {
buildConfig true // 开启BuildConfig类的生成
aidl true // 启用aidl
}
}

main包下添加aidl目录,其与javasrc目录同级,在aidl目录的指定包下创建IUserService.aidl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package xyz.xxin.adbshellutils;

interface IUserService {

/**
* Shizuku服务端定义的销毁方法
*/
void destroy() = 16777114;

/**
* 自定义的退出方法
*/
void exit() = 1;

/**
* 执行命令
*/
String exec(String command) = 2;
}

java目录的指定包下创建UserService.java,该服务继承IUserService.Stub,无需在app清单文件中注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class UserService extends IUserService.Stub {

@Override
public void destroy() throws RemoteException {
}

@Override
public void exit() throws RemoteException {
}

@Override
public String exec(String command) throws RemoteException {
}
}

UserService.java中实现IUserService.aidl中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class UserService extends IUserService.Stub {

@Override
public void destroy() throws RemoteException {
System.exit(0);
}

@Override
public void exit() throws RemoteException {
destroy();
}

@Override
public String exec(String command) throws RemoteException {
StringBuilder stringBuilder = new StringBuilder();
try {
// 执行shell命令
Process process = Runtime.getRuntime().exec(command);
// 读取执行结果
InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line).append("\n");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return stringBuilder.toString();
}
}

绑定服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Shizuku.bindUserService(userServiceArgs, serviceConnection);

// 指定服务的各项参数
private final Shizuku.UserServiceArgs userServiceArgs =
new Shizuku.UserServiceArgs(new ComponentName(BuildConfig.APPLICATION_ID, UserService.class.getName()))
.daemon(false)
.processNameSuffix("adb_service")
.debuggable(BuildConfig.DEBUG)
.version(BuildConfig.VERSION_CODE);

// 建立服务连接通道
private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Toast.makeText(MainActivity.this, "服务连接成功", Toast.LENGTH_SHORT).show();

if (iBinder != null && iBinder.pingBinder()) {
IUserService iUserService = IUserService.Stub.asInterface(iBinder);
try {
// 执行shell命令
iUserService.exec("ls");
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}

@Override
public void onServiceDisconnected(ComponentName componentName) {
Toast.makeText(MainActivity.this, "服务连接断开", Toast.LENGTH_SHORT).show();
}
};

解绑服务

1
Shizuku.unbindUserService(userServiceArgs, serviceConnection, true);

aidl参考:Android 接口定义语言 (AIDL)跟着例子,来整一遍AIDL吧