Android P非SDK接口限制

写在前头


Android Pie(API 28)开始对非SDK接口的使用开始做了限制.只要应用引用非 SDK 接口或尝试使用反射或 JNI 来获取其句柄,就会被限制。这些限制旨在帮助提升用户体验和开发者体验,为用户降低应用发生崩溃的风险,同时为开发者降低紧急发布的风险.

官网针对非SDK接口限制的解释



SDK接口和非SDK接口


SDK接口是指Android 框架软件包索引中记录的那些接口,非SDK即指不包含在Android 框架软件包下的接口.当你使用反射去获取SDK未列举的方法和属性时就会被限制.



使用非SDK接口的影响

没有兼容性保证:

  • 非SDK接口可能会在将来发生改变,甚至消失
  • 在不同设备上可能不兼容
  • 兼容性问题可能要到运行时才能发现

绕过隐式保护:

  • 影响用户体验
  • 妨碍用户隐式
  • 被Google Play Protect判定为恶意软件


非SDK接口分类


非SDK一共分为三类,黑名单,灰名单,白名单,其中灰名单又被分为浅灰名单和深灰名单.(非受限灰名单中的非 SDK 接口称为浅灰名单,而受限灰名单中的非 SDK 接口称为深灰名单)

  • 黑名单(Blacklist):只能被Android系统或者系统应用使用,无论targetSdkVersion的版本都禁止使用.意味着禁止开发者使用这些接口.
  • 灰名单(Dark Greylist和Light Greylist):深灰黑名单当用户的targetSdkVersion小于P(API 28)时可以使用,当大于等于P时无法使用.浅灰黑名单是目前仍然有应用正在使用的非SDK接口,Google将会在后期考虑提供这样的SDK接口.目前还能正常使用.但是当targetSdkVersion大于或者等于P时,系统会抛出警告.
  • 白名单(Whitelist):此列表中的接口已在 Android 框架软件包索引中正式记录,就是Android公开SDK接口本身,它们是受支持的接口,没有任何访问限制,您可以自由使用。

真正的名单会在系统编译时生成,直接将访问标志编码在dex文件的相关字段和方法中,系统中不存在单独的列表文件,运行时高效率的检查应用是否使用了非SDK接口.



访问受限的非 SDK 接口时可能会出现的预期行为


访问方式 结果
Dalvik 指令引用某个字段 抛出 NoSuchFieldError
Dalvik 指令引用某个方法 抛出 NoSuchMethodError
通过 Class.getDeclaredField() 或 Class.getField() 进行反射 抛出 NoSuchFieldException
通过 Class.getDeclaredMethod()、Class.getMethod() 进行反射 抛出 NoSuchMethodException
通过 Class.getDeclaredFields()、Class.getFields() 进行反射 结果中未获取到非 SDK 成员
通过 Class.getDeclaredMethods()、Class.getMethods() 进行反射 结果中未获取到非 SDK 成员
通过 env->GetFieldID() 进行 JNI 调用 返回 NULL,抛出 NoSuchFieldError
通过 env->GetMethodID() 进行 JNI 调用 返回 NULL,抛出 NoSuchMethodError


测试您的应用是否使用非 SDK 接口


  • 使用可调试的应用进行测试
    您可以通过在搭载 Android 9(API 级别 28)或更高版本的设备或模拟器上构建和运行可调试应用来测试该应用是否使用非 SDK 接口。请确保您使用的设备或模拟器与您应用的目标 API 级别相匹配。
    在您的应用上运行测试时,如果该应用访问了某些非 SDK 接口,系统就会输出一条日志消息。您可以检查应用的日志消息,查找以下详细信息:
    您可以使用 adb logcat 来查看这些日志消息,这些消息显示在所运行应用的 PID 下。

Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI) //表示使用JNI方法访问了light greylist中的非SDK接口


  • 使用 StrictMode API (严苛模式)进行测试
    您还可以利用 StrictMode API 来测试您的应用是否使用非 SDK 接口。请使用 detectNonSdkApiUsage 方法来启用此 API。启用 StrictMode API 后,您可以使用 penaltyListener 来接收每次使用非 SDK 接口的行为所对应的回调,并且您可以在其中实现自定义处理。回调中提供的 Violation 对象派生自 Throwable,并且封闭式堆栈轨迹会提供相应使用行为的上下文。

使用detectNonSdkApiUsage()方法来在应用调用非SDK接口时抛出警告.
使用permitNonSdkApiUsage()方法来阻止StrictMode 对这些调用抛出警告.

StrictMode 简单使用:

StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                 .detectCustomSlowCalls() //API等级11,使用StrictMode.noteSlowCode
                 .detectDiskReads()
                 .detectDiskWrites()
                 .detectNetwork()   // or .detectAll() for all detectable problems
                 .penaltyDialog() //弹出违规提示对话框
                 .penaltyLog() //在Logcat 中打印违规异常信息
                 .penaltyFlashScreen() //API等级11
                 .build())
         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                 .detectLeakedSqlLiteObjects()
                 .detectLeakedClosableObjects() //API等级11
                 .penaltyLog()
                 .penaltyDeath()
                 .build())

  • 使用 veridex 工具进行测试
    您还可以在 APK 上运行静态分析工具 veridex。veridex 工具会扫描 APK 的整个代码库(包括所有第三方库),并报告发现的所有使用非 SDK 接口的行为。
    veridex 工具存在以下局限性:
    它无法检测到通过 JNI 实现的调用。
    它只能检测到一部分通过反射实现的调用。
    它对非活动代码路径的分析仅限于 API 级别的检查。


最后


绕过Android P对非SDK的限制方法
Android P 访问非SDK 接口
其实方法很简单,无非是干涉Android的反射流程中对非SDK判断的返回进行修改.或者使用第三方库来绕过这种限制.FreeReflection

你可能感兴趣的:(Android)