补丁sdk用于生成补丁模块,可以通过dg命令安装在云手机中,实现系统及app适配能力。
补丁模块安装后会实时生效,无需修改系统源码,无需设备重启,无需应用重启。
下载最新版本sdk及demo: 点击下载
补丁apk会在指定进程(支持系统及app进程)app启动前加载,并执行其中的入口方法: com.android.hp.Entry.init(Context context, Bundle params)
您可以在这个方法中实现自己的逻辑,sdk会提供基础的Xposed风格的api用于拦截,干预各类方法。
流程图如下:
+---------+
| 进程启动 |
+---------+
↓
+---------+
| 获取补丁 |<----------------
+---------+ |
↓ |
+---------+ |
| 加载补丁 | |
+---------+ |
↓ |
+---------+ |
| 应用运行 | |
+---------+ |
↓ |
/ \ |
/ \ 是 +---------+
|更新补丁?|----------->| 卸载补丁 |
\ / +---------+
\ /
|
↓
+---------+
| 进程退出 |
+---------+
如果您的设备不支持,请联系接口人更新系统版本。
// 项目配置
{
ext // 这里设置各类android sdk/ndk等版本信息,建议直接使用如下默认的配置。请使用Android SDK Manager下载对应版本的sdk/ndk/cmake等工具
= 23
minSdkVersion = 33
compileSdkVersion = 33
targetSdkVersion = "33.0.0"
buildToolsVersion = JavaVersion.VERSION_1_8
javaVersion = "24.0.8215888"
ndkVersion = "3.22.1"
cmakeVersion
// 这里填写sdk目录所在的路径,demo中是放在项目根目录下的sdk目录
= "$rootDir/sdk"
patchSdkDir = "$patchSdkDir/patch.gradle"
patchGradle }
{
buildscript {
dependencies fileTree(dir: "$rootProject.ext.patchSdkDir", include: 'agp_*.jar')
classpath }
}
// 只修改这里的配置即可
.patch = [
ext// 补丁id,作为确认补丁的唯一标识,注意不要冲突!
: "sdktest",
id// 补丁版本,如果为0,则自动根据当前的epoch时间戳生成,例如1696822576
: 0,
version// 可以加载补丁的包名正则表达式,如果存在pkg.txt文件,则忽略这里的配置
: '',
targetPattern// 补丁加载优先级, 数越小越先加载,默认为0
: 0,
priority// 是否支持热加载,热加载可以在一次进程的生命周期中多次加载,卸载,加载...
: true,
hotpatch// 补丁描述
: "补丁测试demo",
description// 补丁负责方,这里可以写你的公司的名字,用于排查问题
: "unknown"
owner]
: "$rootProject.ext.patchGradle" apply from
package com.android.hp;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import com.android.server.dexguard.DGLog;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
/**
* 补丁模块的入口类,固定使用com.android.hp.Entry。
* 注意: 请不要修改这个类文件的路径及类名!!!
*/
public class Entry {
private static final String TAG = "Entry";
/**
* 在补丁加载时会调用,调用时机:
* 1. 进程启动时。
* 2. 进程已启动,补丁首次安装或升级后。
*
* @param appContext 进程的应用Context
* @param params 其它信息,一般不会使用,具体参考文档。
*/
public static void init(Context appContext, Bundle params) {
// 在补丁中,可以使用DGLog工具类来打印log,注意log分级
.i(TAG, "init() loaded pkg = %s", appContext.getPackageName());
DGLog
// 使用例子1:干预系统获取应用api(android.app.ApplicationPackageManager.getPackageInfoAsUser(String, int, int)),禁止返回com.abc的应用包
try {
// 通过反射获取 "android.app.ApplicationPackageManager" 类
Class<?> pmClass = Class.forName("android.app.ApplicationPackageManager", true, appContext.getClassLoader());
// 使用 xposed-api,hook "android.app.ApplicationPackageManager.getPackageInfoAsUser" 方法
// 这个方法是PackageManager.getPackageInfo()的底层实现,所以这里hook这个方法即可
// 注意:xposed的部分api是会抛出异常的,请注意处理异常!!!
.findAndHookMethod(pmClass, "getPackageInfoAsUser", String.class, int.class, int.class, new XC_MethodHook() {
XposedHelpers@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
// 获取查询的包名
String pkg = (String) param.args[0];
// 如果查询的包名是 "com.abc",那么抛出异常来隐藏这个应用
if ("com.abc".equals(pkg)) {
.d(TAG, "hide app %s from %s", pkg, param.method.getName());
DGLog
// 抛出 NameNotFoundException,使其看起来像这个包没有安装在设备上
.setThrowable(new PackageManager.NameNotFoundException(pkg));
param}
}
});
// 记录成功的日志
.d(TAG, "hide app by pms api success!");
DGLog} catch (Throwable t) {
// 如果有任何异常,记录异常日志
.e(TAG, "init package manager guard error", t);
DGLog}
}
/**
* 在补丁卸载时会调用,调用时机:
* 1. 如果该进程加载过补丁,补丁被卸载后会触发。
*
* @param appContext 进程的应用Context
* @param params 其它信息,一般不会使用,具体参考文档。
*/
public static void deInit(Context appContext, Bundle params) {
.i(TAG, "deInit() loaded pkg = %s", appContext.getPackageName());
DGLog}
}
# 补丁只会在如下设置的目标包名的应用进程中加载,注意覆盖范围尽量小,只针对特定应用即可
com.example.abc
com.example.def
# 正则方式,匹配所有com.baidu.开头的包名
com\.baidu\..*
# 如果需要干预system_server,填写android
android
# linux或mac环境
./gradlew clean app:assembleRelease
# windows环境
gradlew clean app:assembleRelease
或者您也可以直接在Android Studio中通过右侧gradle任务菜单找到app项目的assembleRelease任务,并点击执行编译。
# 安装补丁
dg apt install patch:/sdcard/sdktest.apk
# 查看补丁安装信息,补丁id为你在app项目中build.gradle的设置的
dg apt show patch:sdktest
# 请启动目标app,然后通过log查看补丁是否正常加载,其中sdktest为你的补丁id。所有通过DGLog打印的日志都可以通过如下tag过滤
logcat | grep patch-sdktest
# 卸载补丁id为sdktest的补丁模块
dg apt uninstall patch:sdktest