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:provider:$shizuku_version"
|
亦或者引入Shizuku-API中对应的模块
1 2 3 4
| implementation project(':api')
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
|
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
| 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
| 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);
|
执行命令
在app
级gradle
文件中添加如下配置
1 2 3 4 5 6
| android { buildFeatures { buildConfig true aidl true } }
|
在main
包下添加aidl
目录,其与java
、src
目录同级,在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 {
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 { 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 { 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吧