背景
在Android系统中,出于保护目的,Android framework对有些API 添加了@hide,@SystemApi注解,三方APP是无法直接通过SDK调用的。但有时我们有功能需求,期望我们的APP可以调用系统隐藏API,或我们在framework service开发了某个API,期望我们的APP可以调用这些隐藏API。
这里介绍3种Android三方APP调用系统隐藏API的方法
系统隐藏API,我们以WifiManager中的getVerboseLoggingLevel()为例,其没有权限检查,三方APP可以通过下面演示的三种方式直接调用。
3556 /**
3557 * Get the WiFi verbose logging level.This is used by settings
3558 * to decide what to show within the picker.
3559 * @hide
3560 */
3561 public int getVerboseLoggingLevel() {
3562 try {
3563 return mService.getVerboseLoggingLevel();
3564 } catch (RemoteException e) {
3565 throw e.rethrowFromSystemServer();
3566 }
3567 }
1. 通过APP加载framework编译的classes jar的方式访问隐藏API
1.1 我这里访问的WifiManager.getVerboseLoggingLevel API,其位于com.android.wifi.apex里面,所以其classes jar位于SSI分支编译结果的下面目录。
ssi-code-branch\out_sys\target\common\obj\JAVA_LIBRARIES\framework-wifi.com.android.wifi_intermediates\classes.jar
如果你们API位于framework目录,相应需要去比如下面目录找。
ssi-code-branch\out_sys\target\common\obj\JAVA_LIBRARIES\framework_intermediates\classes.jar
1.2 拷贝classes.jar到APP源码HideApiDemo\app\libs目录,并更名classes.jar 为com.android.wifi.classes.jar,方便识别。

1.3 在HideApiDemo\app\build.gradle的dependencies里面增加jar编译引入。
implementation files('libs\\com.android.wifi.classes.jar')
1.4 在Android studio编译过程中Android SDK加载优先级高于我们的com.android.wifi.classes.jar。会导致隐藏API访问失败,所以我们需要修改一下sdk加载优先级。在HideApiDemo\app\build.gradle增加下面内容。
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
Set<File> fileSet = options.bootstrapClasspath.getFiles()
List<File> newFileList = new ArrayList<>();
newFileList.add(new File("libs/com.android.wifi.classes.jar"))
newFileList.addAll(fileSet)
options.bootstrapClasspath = files(
newFileList.toArray()
)
}
}
1.5 接着我们就可以直接在java代码调用WifiManager.java中的隐藏API。
private int getVerboseLoggingLevel_method_1() {
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
try {
return wifiManager.getVerboseLoggingLevel();
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
2. 通过反射方式调用系统隐藏API。
可以使用java现有反射API实现下面逻辑。
private int getVerboseLoggingLevel_method_2() {
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
try {
Class<?> wmClass = Class.forName("android.net.wifi.WifiManager");
Method getVerboseLoggingLevelMethod = wmClass.getMethod("getVerboseLoggingLevel");
getVerboseLoggingLevelMethod.setAccessible(true);
int verboseLoggingLevel = (int) getVerboseLoggingLevelMethod.invoke(wifiManager);
return verboseLoggingLevel;
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
3. 通过代理方式调用系统隐藏API。
3.1 首先我们在APP根目录创建一个AndroidStub的java-library库模块,并根据WifiManager.java在Android源码目录创建其代理类WifiManager.java文件。

3.2 接着我们在APP根目录再创建一个FrameworkProxy的java-library库模块,并创建一个WifiManagerProxy.java 代理类,由该类去调用WifiManager.java中隐藏API。

3.3 在APP根目录的settings.gradle为这两个模块引入编译。
include ':AndroidStub'
include ':FrameworkProxy'
3.4 在HideApiDemo\app\build.gradle的dependencies里面增加代理工程编译引入。
implementation project(':FrameworkProxy')
3.5 接着我们就可以直接在java代码调用WifiManager.java中的隐藏API。
private int getVerboseLoggingLevel_method_3() {
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
try {
if (wifiManager == null) {
Log.e(TAG, "getVerboseLoggingLevel fails, WifiManager is null");
return -1;
}
return new WifiManagerProxy().getVerboseLoggingLevel(wifiManager);
} catch (NoClassDefFoundError | NoSuchMethodError e) {
Log.e(TAG, "getVerboseLoggingLevel API does not support");
}
return -1;
}
这三种方式中,个人最推荐使用第三种方式调用系统隐藏API。因为第一种方式会给APP加载很多不用的类,占用较大APP内存。第二种方式不方便阅读,看着比如复杂。第三方方式兼顾第一第二种方式的优点。
功能调试
编译APK安装手机,执行下面命令,打开Wifi verbose log,
adb shell cmd wifi set-verbose-logging enabled
接着点击APP test1, test2, test3按钮,就可以获取到1的状态值。

执行下面命令,关闭Wifi verbose log,接着点击APP test1, test2, test3按钮,就可以获取到0的状态值。隐藏API调用成功。
adb shell cmd wifi set-verbose-logging disabled

demo 代码及apk见下面链接
链接: https://pan.baidu.com/s/1Owlz9ioYBXA9Tz_uxy2KSw?pwd=5rtm
Android第三方APP调用系统隐藏API的三种方法
1734

被折叠的 条评论
为什么被折叠?



