Android P非SDK的隐藏API调用检测

为什么要检测?

Google在2018年的I/O大会上发布了Android P的Developer Preview 2(简称DP2)版本,其中还说明了在以后的Android P上,将对非SDK API的调用进行限制。目前,开发者对于非SDK API的调用,只能采取反射或JNI间接调用的方法进行调用。由于Android是开源的,所以开发者对非公有SDK的调用十分混乱,Google此举也是为了进一步防止碎片化,规范开发者的API使用行为。

Android P引入非SDK API检测

而在运行Android P的手机上,启动APP时,ART虚拟机就会对APP的Dex文件进行检测,比对内置的黑名单、暗灰名单、亮灰名单,一旦发现APP内含有上面名的中的调用,就会发出警告,严重的,会直接抛出异常,如果没有进行处理,将导致APP的Crash,即使进行了try-catch,这些反射调用的功能,也会使用不正常。

调用了黑名单(BlackList)中的API,APP在Android P上运行时抛出异常:

抛出异常

Google官方给出的使用非SDK API的影响:

Android P上使用非SDK API的影响

因此,开发者应该尽量避免使用反射调用非SDK API,防止在Android P上出现问题,为了避免APP体验受到影响,兼容Android P,我们需要对现有APP的API使用情况进行检测。

如何检测?

Google官方并没有直接放出APP检测工具,可能是因为还处于开发者预览版的阶段,最终的黑白名单还没有完全确定下来,但可以通过AOSP的master源码编译后获取这个名为veridex的工具

AOSP的获取方法这里不详细讨论,可以参考:https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/

veridex的编译方法

首先需要建立好AOSP的构建环境,可以使用macOS或者Ubuntu,这里以64位的Ubuntu 16.04.4 LTS为例:

  1. 安装OpenJDK 8,命令:sudo apt install openjdk-8-jdk

  2. 安装依赖的库和组件,命令:sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev libgl1-mesa-dev libxml2-utils xsltproc unzip

AOSP根目录

接着进入AOSP的根目录(如上图所示),执行:make appcompat,如果机器性能较好,可以加上-j8或者其他数字(根据CPU线程数),来并行编译,从而加快编译速度

等待一会儿以后,出现如下内容即为编译完成(中间有任何failed的提示,或者最终进度不到100%,都为失败):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
yuanguozheng@pc:~/aosp/aosp$ make appcompat -j32
============================================
PLATFORM_VERSION_CODENAME=Q
PLATFORM_VERSION=Q
TARGET_PRODUCT=aosp_arm
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a
TARGET_CPU_VARIANT=generic
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86

.....

DroidDoc took 11 sec. to write docs to out/soong/.intermediates/frameworks/base/hiddenapi-lists/android_common/docs/out
javadoc: option --boot-class-path not allowed with target 1.9
[100% 2601/2601] build out/target/common/obj/PACKAGING/hiddenapi-blacklist.txt
yuanguozheng@pc:~/aosp/aosp$

可以看到在out/target/common/obj/PACKAGING/目录下,生成了三个隐藏API的列表文件hiddenapi-blacklist.txt(黑名单)、hiddenapi-dark-greylist.txt(暗灰名单)、hiddenapi-light-greylist.txt(亮灰名单),这些即为ART检查API调用情况的依据

黑名单

此时veridex组件编译完成,接下来即可使用它对apk文件进行检测

API调用情况检测

准备好需要检测的APK,这里我们使用反射,获取一个APP内部类的私有成员变量值、调用一个私有方法,再使用反射调用Android的Hidden API中的值,进行举例,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 初始化一个APP中的类
AppInnerClass obj = new AppInnerClass();

ReflectionUtils.getFieldValue(obj, "mTestValue");
ReflectionUtils.invokeMethod(obj, "printTestValue", null, null);

// 反射调用Android私有API
ReflectionUtils.getFieldValue("android.annotation.Dimension", null, "DP");
ReflectionUtils.getFieldValue("android.Manifest$permission", null, "BLUETOOTH_STACK");
}
}

Build一个测试用的APK以后,进入到AOSP源码的根目录下(必须在AOSP源码根目录下),执行以下命令

1
./art/tools/veridex/appcompat.sh --dex-file=[待检测apk的路径]  --imprecise(这个参数可选,建议加上,更准确)

如下图所示,经过veridex工具检测的结果会在终端中输出,如果结果比较多,可以使用>>保存到一个文本文件中,需要时详细查看

veridex检测结果

注意事项

目前,Android P还处于Developer Preview(DP2)的版本阶段,一些API和设计还不稳定,更多的API可能会被加入黑名单或者深灰名单,当然,某些API调用,也不排除被从名单中移除的可能,Google在官方文档中,还说到不保证灰名单API可以正常调用,所以,如果被检测出来有Hidden API的使用,应对尽快修正

引述Google官方对灰名单的立场:

Greylisted non-SDK interfaces encompass methods and fields which continue to function in Android P, but to which we do not guarantee access in future versions of the platform. If there is a reason that you cannot implement an alternative strategy to a greylisted API, you may file a bug to request reconsideration of the restriction.

总结

随着Android P预览版的发布,众多的新特性也随之而来。从限制非SDK的隐藏API来看,可以体现出Google对Android平台进一步控制碎片化、规范开发行为的的决心,而开发者需要做的,则是不断地分析这些新特性和API调用变化,跟进Google的步伐,让Android平台APP体验越来越好!