内容简介:上篇文章一般来说,SDK接口是指在Android框架软件包索引中记录的接口,其对立面自然就叫非SDK接口了。Android 9引入了针对非SDK接口的使用限制,无论是直接使用还是通过反射或JNI间接使用。这意味着,通过反射之类的语义来操作某个类时,不应访问SDK中未列出的函数或字段(通常都有Android 9将非SDK接口都放在了各名单中:
上篇文章 Android修炼之Pie 适配的搬运工 中介绍了Android P的一些行为变更并提供了一些对齐刘海和非SDK接口的适配建议,大部分人还是更加关心非SDK接口的问题,所以本文来说一下如何检测非SDK接口。
区分SDK接口和非SDK接口
一般来说,SDK接口是指在Android框架软件包索引中记录的接口,其对立面自然就叫非SDK接口了。Android 9引入了针对非SDK接口的使用限制,无论是直接使用还是通过反射或JNI间接使用。这意味着,通过反射之类的语义来操作某个类时,不应访问SDK中未列出的函数或字段(通常都有 @hide
标志)。
Android 9将非SDK接口都放在了各名单中:
- 白名单:自然就是可以直接使用的SDK接口;
- 浅灰名单:仍可以访问的非 SDK 函数/字段;
- 深灰名单:包含我们在开发期间未看到使用,但是我们可能已经忽视的函数/字段。列入深灰名单的函数/字段是那些与SDK和浅灰名单中的公开接口紧密相关的函数/字段。
- 对于目标SDK低于API级别28的应用,允许使用深灰名单接口;
- 对于目标SDK为API 28或更高级别的应用:行为与黑名单相同;
- 黑名单:受限,无论目标SDK如何。平台将表现为似乎接口并不存在。
这些名单都可以在:非SDK接口名单中找到。如果不想编译AOSP,可以直接下载该页的 tgz
文件,解压即可看到对应的 txt
文件。
如何检测非SDK接口?
查看logcat日志
在Android 9的系统中,如你的应用中使用了非SDK接口,系统将会在APP运行时打印日志;如果访问了某些“列入灰名单的”非SDK接口,系统还可能显示Toast;如果访问了“列入黑名单的”非SDK接口,系统将引发异常。
日志消息
在开发过程中,我们可以通过AS的logcat来查看日志,否则就是通过 adb logcat
了。非SDK接口的日志会显示在正在运行的应用的PID下,大概格式是:
Accessing hidden field Landroid/net/wifi/WifiManager;->WIFI_SCAN_AVAILABLE:Ljava/lang/String; (dark greylist, reflection) 复制代码
- Accessing hidden:固定格式
- field:可能是method,表示调用的是属性还是方法
- Landroid/net/wifi/WifiManager:表示调用的类名
- ->WIFI_SCAN_AVAILABLE:Ljava/lang/String;:表示调用的属性和方法名
- (dark greylist, reflection):前半部分表示该非SDK接口在哪个名单中,后半部分表示被调用的方式,比如:reflection(反射)和JNI
这里给出几个示例:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_third); WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); Field field = null; try { field = wifiManager.getClass().getDeclaredField("WIFI_SCAN_AVAILABLE"); Log.d("ThirdActivity", (String) field.get(wifiManager)); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } // 直接在浅灰名单中找的接口 public void testLightGreyList(View view) { ReflectUtils.getMethod(TelephonyManager.class, "isMultiSimEnabled"); } // 直接在深灰名单中找的接口 @TargetApi(Build.VERSION_CODES.M) public void testDarkGreyList(View view) { ReflectUtils.getField(CarrierConfigManager.class, "KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL"); } // 直接在黑名单中找的接口 public void testBlackList(View view) { try { ReflectUtils.getMethod(ReflectUtils.getClass("android.net.util.IpUtils"), "ipChecksum", ByteBuffer.class, int.class); } catch (ClassNotFoundException e) { e.printStackTrace(); } } 复制代码
他们的日志如下:
Accessing hidden field Landroid/net/wifi/WifiManager;->WIFI_SCAN_AVAILABLE:Ljava/lang/String; (dark greylist, reflection) Accessing hidden method Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z (light greylist, reflection) Accessing hidden field Landroid/telephony/CarrierConfigManager;->KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL:Ljava/lang/String; (dark greylist, reflection) Accessing hidden method Landroid/net/util/IpUtils;->ipChecksum(Ljava/nio/ByteBuffer;I)S (blacklist, reflection) 复制代码
注意:在debug的时候,只有触发了对应的调用逻辑,才会打印日志,而不是静态检测。所以新项目中,我们在开发过程中就要注意非SDK接口的调用。
Toast和Dialog提示
可能因为我的模拟器用的是Android P的正式版,去除了Toast提示。所以,在测试的时候并没有看到相关的Toast提示。在预览版中应该有Toast提示功能。当然,也可能是我没有触发“某些灰名单中的非SDK接口”(官方文档原话)的原因。
不过,当我把TargetSDK改为27时,出现过这种对话框提示:
引发异常
官方说明:
经测试,只有 深灰名单 和 黑名单 中的非SDK接口会引发异常和错误。
注意:当TargetSDK>=28时,深灰名单和黑名单都会引发异常;而当TargetSDK<28时,只有黑名单会引发异常。示例结果如下:
使用veridex检测工具
谷歌提供了一个静态检测工具:veridex,可以帮助我们检测apk中是否使用了非SDK接口。这里使用 veridex-linux.zip
进行测试,解压后目录结构如下:
appcompat.sh
就是运行脚本。
将待检测的apk文件拷贝到veridex目录下,运行以下命令即可:
./appcompat.sh --dex-file=文件名.apk --imprecise 复制代码
结果:
这里显示的数量明显比我们示例中的多,这是为什么?观察详细内容,发现veridex把Android自己调用的也检测出来了。
如果去掉 --imprecise
会检测不出来黑名单的接口。
我们可以通过重定向将日志保存在文件中,再进行细致的分析。
我们主要关注黑名单和深灰名单。可以看到,日志中给出了调用非SDK接口的位置,很容易定位。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。