如何在安卓开发中调用 Shizuku API?

Shizuku API 使用教程

项目配置

要使用 Shizuku,首先需要在项目中完成基础配置。以下是关键步骤:

  1. 添加依赖:在项目级的 build.gradle.kts 中引入 Shizuku 的 API 和 Provider 库

    dependencies {
        implementation("dev.rikka.shizuku:provider:${latest_version}")
        implementation("dev.rikka.shizuku:api:${latest_version}")
    }
    

    最新版本依赖可在Shizuku 官方仓库 查看

  2. 启用 AIDL:在刚刚的build.gradle.kts中在 buildFeatures 开启 AIDL 支持

    android {
        ...
        buildFeatures {
            aidl = true // 启用 AIDL
            ...
        }
        ...
    }
    
  3. 配置 Manifest:添加 Shizuku Provider

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
        ...
        <application
    		... >
            ...
            <provider
                android:name="rikka.shizuku.ShizukuProvider"
                android:authorities="${applicationId}.shizuku"
                android:enabled="true"
                android:exported="true"
                android:multiprocess="false"
                android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
        </application>
    </manifest>
    

    直接复制provider块即可

AIDL 的作用: AIDL(Android Interface Definition Language)是 Shizuku 的核心通信机制,用于定义客户端与特权进程之间的接口,进而通过特权进程代理运行代码。通过自定义 AIDL 文件,你可以灵活设计需要的功能。


定义 AIDL 接口

目录创建

在这里插入图片描述

app/
├── src/
│   ├── main/
│   │   ├── aidl/com/.../...
│   │   ├── java/com/.../...
│   │   ├── res/...
│   │   ├── AndroidManifest.xml

像这样创建一个和 java 资源同级、包名相同的文件夹并创建这些 AIDL 文件

核心接口设计

  • IUserService.aidl:定义核心服务接口,如执行 Shell 命令、退出服务等。

    package com.niki.cmd;
    
    import com.niki.cmd.ExecResult;
    
    interface IUserService {
        // 销毁服务
        void destroy() = 16777114;  // 固定的方法编号, 由 Shizuku 规定
    
        // 自定义方法
        void exit() = 1;
        
        // 执行 shell 命令
        ExecResult exec(String command) = 2;
        
        // 可以添加更多自定义方法, 最后要自己实现
    }
    
  • ExecResult.aidl(示例):自定义可序列化的数据结构,用于返回命令执行结果(如标准输出、错误输出和退出码)

    package com.niki.cmd;
    
    parcelable ExecResult {
        String stdout;
        String stderr;
        int exitCode;
    }
    

AIDL 使用注意

AIDL parcelable 编译出来的对应 java 类似乎不能直接调用有参构造函数,所以赋值操作可以这样写:

val result = ExecResult().apply {
	stdout = ""
    stderr = ""
    exitCode = 0
}

这个示例通过AIDL接口,用shizuku特权进程代理运行shell命令


实现 UserService

UserService 的核心逻辑

UserService 是 AIDL 接口的具体实现,负责处理实际操作,你可以在接口定义任何操作,实现的时候没什么特别之处,只是这些代码运行在了更高权限的进程中,进而实现安装应用、修改系统设置等高级操作

以下是一个实现

package com.niki.cmd

import android.os.Build
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import kotlin.system.exitProcess

internal class UserService : IUserService.Stub() {
    override fun destroy() = exitProcess(0)

    override fun exit() = destroy()

    override fun exec(command: String?): ExecResult {
        var process: Process? = null
        val stdoutSB = StringBuilder()
        val stderrSB = StringBuilder()

        val result = ExecResult().apply {
            stdout = ""
            stderr = ""
            exitCode = 0
        }

        if (command == null || command.trim { it <= ' ' }.isEmpty()) {
            result.apply {
                stdout = ""
                stderr = "Error: Empty command"
                exitCode = -1
            }
            return result
        }

        try {
            process = Runtime.getRuntime()
                .exec(command.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
            val stdoutReader = BufferedReader(InputStreamReader(process.inputStream))
            var line: String?
            while ((stdoutReader.readLine().also { line = it }) != null) {
                stdoutSB.append(line).append('\n')
            }
            stdoutReader.close()

            val stderrReader = BufferedReader(InputStreamReader(process.errorStream))
            while ((stderrReader.readLine().also { line = it }) != null) {
                stderrSB.append(line).append('\n')
            }
            stderrReader.close()

            result.apply {
                stdout = stdoutSB.toString()
                stderr = stderrSB.toString()
                exitCode = process.waitFor()
            }
        } catch (e: SecurityException) {
            result.apply {
                stdout = ""
                stderr = "Permission denied: ${e.message}"
                exitCode = -2
            }
        } catch (e: IOException) {
            result.apply {
                stdout = ""
                stderr = "IO error: ${e.message}"
                exitCode = -3
            }
        } catch (e: InterruptedException) {
            Thread.currentThread().interrupt()
            result.apply {
                stdout = ""
                stderr = "Execution interrupted: ${e.message}"
                exitCode = -4
            }
        } finally {
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
                process?.destroyForcibly()
        }

        return result
    }
}

使用

Shizuku 的使用分为三个关键步骤:

  1. 权限请求:授予 Shizuku 权限,就是唤起 Shizuku 授权对话框来让用户同意授权的操作
  2. 服务绑定:通过 Shizuku 绑定我们写好的的 UserService,获取 AIDL 接口实例
  3. 大功告成:获得 AIDL 接口实例(IUserService)后我们可以直接调用我们实现的方法了

授权

private const val REQUEST_CODE_PERMISSION = 666 // 自行定义, 和 activity result那个类似

fun checkAndRequestPermission(callback: OnPermissionResultCallback) {
    if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
        Log.d(TAG, "Shizuku 权限已授予")
        callback.onResult(true, null)
        return
    }

    if (Shizuku.shouldShowRequestPermissionRationale()) {
        Log.e(TAG, "用户拒绝授权 Shizuku")
        callback.onResult(false, "用户拒绝授权")
        return
    }

    val listener = OnRequestPermissionResultListener { _, grantResult ->
        Shizuku.removeRequestPermissionResultListener(listener)
        val isGranted = grantResult == PackageManager.PERMISSION_GRANTED
        Log.d(TAG, "Shizuku 授权结果: $isGranted")
        callback.onResult(isGranted, if (isGranted) null else "授权失败")
    }

    Shizuku.addRequestPermissionResultListener(listener)
    try {
        Shizuku.requestPermission(REQUEST_CODE_PERMISSION)
    } catch (e: Exception) {
        Log.e(TAG, "授权失败", e)
        Shizuku.removeRequestPermissionResultListener(listener)
        callback.onResult(false, "授权异常: ${e.message}")
    }
}

interface OnPermissionResultCallback {
    fun onResult(isGranted: Boolean, errorMessage: String?)
}

绑定服务

在确保你有 Shizuku 权限后,进行服务绑定

private var userService: IUserService? = null
private var serviceConnection: ServiceConnection? = null

fun bind(context: Context, callback: OnBindResultCallback) {
    if (userService != null) {
        Log.d(TAG, "Shizuku service 已经连接")
        callback.onResult(true, null, userService)
        return
    }

    try {
        val args = UserServiceArgs(ComponentName(context, UserService::class.java.name))
            .daemon(true)
            .processNameSuffix("${context.packageName}.shizuku.service")
            .debuggable(true)

        serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName, service: IBinder) {
                userService = IUserService.Stub.asInterface(service)
                Log.d(TAG, "Shizuku service 成功连接: ${name.className}")
                callback.onResult(true, null, userService)
            }

            override fun onServiceDisconnected(name: ComponentName) {
                userService = null
                Log.d(TAG, "Shizuku service 断开: ${name.className}")
            }
        }

        Shizuku.bindUserService(args, serviceConnection!!)
    } catch (e: Exception) {
        Log.e(TAG, "绑定失败", e)
        callback.onResult(false, "绑定异常: ${e.message}", null)
    }
}

fun unbind(context: Context) {
    try {
        if (serviceConnection != null) {
            Shizuku.unbindUserService(
                UserServiceArgs(ComponentName(context, UserService::class.java.name)),
                serviceConnection!!,
                true
            )
        }
    } catch (e: Exception) {
        Log.e(TAG, "解绑出错", e)
    } finally {
        userService = null
        serviceConnection = null
        Log.d(TAG, "Shizuku service 解绑")
    }
}

interface OnBindResultCallback {
    fun onResult(success: Boolean, errorMessage: String?, userService: IUserService?)
}

大功告成:现在直接使用即可

val execResult = userService.exec(command)

至此,就是基于 Shizuku 开发的教程


更高级的使用

感兴趣可以去了解一下ShizukuBinderWrapper

通过它可以将我们的应用所发起的对系统服务的Binder调用路由到Shizuku的特权进程中执行,典型的应用场景有:

  • 应用安装/卸载:通过IPackageManager进行应用的安装、卸载或权限管理,
  • 文件管理:访问/Android/data/Android/obb等受限目录,这些目录通常在Android 11及更高版本中对普通应用不可访问
  • 系统设置修改:更改网络设置、Wi-Fi密码、传感器状态、私有DNS服务器等
  • 日志读取:读取系统日志(Logcat)
  • 其他系统控制:控制应用音量、冻结/禁用/隐藏/卸载Android应用、管理设备所有者权限等

以下是一个使用ShizukuBinderWrapper的示例

private static final IPackageManager PACKAGE_MANAGER =
    IPackageManager.Stub.asInterface(new ShizukuBinderWrapper(
        SystemServiceHelper.getSystemService("package")));
public static void grantRuntimePermission(String packageName, String permissionName, int userId) {
    try {
        PACKAGE_MANAGER.grantRuntimePermission(packageName, permissionName, userId);
    } catch (RemoteException e) {
        // Handle exception
    }
}

基于协程的优雅封装

cmd-android

这是一个基于 Kotlin 协程的 Android Shell 命令执行框架,支持 User、Shizuku 和 Root 三种权限的命令行调用,提供统一易用的挂起式 API,使用起来就像:

val shell: Shell = ShizukuShell(context)
if (shell.isAvailable()) {
    val result = shell.exec("echo test", timeout = 20_000L)
    println("标准输出: ${result.stdout}")
    println("错误输出: ${result.stderr}")
    println("退出码: ${result.exitCode}")
}

这是作者封装的包括 Shizuku 在内的 Shell 执行工具,使用非常简单,有关 AIDL、接口回调等复杂操作都封装起来了,也就是说,导个依赖就能调用 Shizuku API

喜欢的话请给我点个 Star,这对我很重要,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值