Android 豁免所有hide灰名单调用警告,做到不弹窗,logcat不提示,隐藏代码能并反射

从 android 9.0 开始,当代码调用某些系统api的时候,会因为api的一些判定灰名单级别,不同程度的对app做出提醒,最严重的是直接弹窗提醒,次之是会在logcat打印出调用内容。

具体api名单列表:https://developer.android.google.cn/about/versions/10/non-sdk-q

但有些情况下我们确实要使用这些api,下面是我总结了以下几种方案:

1. 反射禁止弹窗

优点:

  • 能避免弹窗

缺点:

  • 不能避免代码扫描,logcat打印
  • 某些类方法用 getMethod 无法发现,无法获取到,因此也就无法反射
try {
    Class aClass = Class.forName("android.content.pm.PackageParser$Package");
    Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class);
    declaredConstructor.setAccessible(true);
} catch (Exception e) {
    e.printStackTrace();
}
try {
    Class cls = Class.forName("android.app.ActivityThread");
    Method declaredMethod = cls.getDeclaredMethod("currentActivityThread");
    declaredMethod.setAccessible(true);
    Object activityThread = declaredMethod.invoke(null);
    Field mHiddenApiWarningShown = cls.getDeclaredField("mHiddenApiWarningShown");
    mHiddenApiWarningShown.setAccessible(true);
    mHiddenApiWarningShown.setBoolean(activityThread, true);
} catch (Exception e) {
    e.printStackTrace();
}

2. 使用元反射(能避免弹窗,logcat打印,只用使用元反射的才能达到效果)

优点:

  • 能避免弹窗
  • 能避免代码扫描,logcat打印
  • 某些用getMethod无法发现的方法,可以被发现了了,也可以反射了

缺点:

  • 对于正常反射的代码,无效,会弹窗,打印logcat
try {
    forName = Class.class.getDeclaredMethod("forName", String.class);
    // invoke = Method.class.getMethod("invoke", Object.class, Object[].class);
    // 反射获取方法
    getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
    getMethod = Class.class.getDeclaredMethod("getMethod", String.class, Class[].class);

    // 反射获取变量
    getDeclaredField = Class.class.getDeclaredMethod("getDeclaredField", String.class);
    getField = Class.class.getDeclaredMethod("getField", String.class);

    // 反射实例化代码
    getDeclaredConstructor = Class.class.getDeclaredMethod("getDeclaredConstructor", Class[].class);
    getConstructor = Class.class.getDeclaredMethod("getConstructor", Class[].class);
    newInstance = Constructor.class.getDeclaredMethod("newInstance", Object[].class);
} catch (Throwable igone) {
}

反射时,原来的反射代码应这样写:

正常的反射

Method getStringMethod = A.class.getDeclaredConstructor("getString");
getStringMethod.invoke(new A());

元反射

Method getStringMethod = getDeclaredConstructor.invoke(A.class,"getString");
getStringMethod.invoke(new A());

3. 元反射基础上,本进程将所有灰黑api加入白名单(能避免弹窗,logcat打印,后续即使不使用元反射也能达到效果)

优点:

  • 能避免弹窗
  • 能避免代码扫描,logcat打印
  • 某些用getMethod无法发现的方法,可以被发现了了,也可以反射了
  • 对于正常反射的代码,仍然不会弹窗,打印logcat
try {
    forName = Class.class.getDeclaredMethod("forName", String.class);
    // invoke = Method.class.getMethod("invoke", Object.class, Object[].class);
    // 反射获取方法
    getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
    getMethod = Class.class.getDeclaredMethod("getMethod", String.class, Class[].class);

    // 反射获取变量
    getDeclaredField = Class.class.getDeclaredMethod("getDeclaredField", String.class);
    getField = Class.class.getDeclaredMethod("getField", String.class);

    // 反射实例化代码
    getDeclaredConstructor = Class.class.getDeclaredMethod("getDeclaredConstructor", Class[].class);
    getConstructor = Class.class.getDeclaredMethod("getConstructor", Class[].class);
    newInstance = Constructor.class.getDeclaredMethod("newInstance", Object[].class);
} catch (Throwable igone) {
}
if (Build.VERSION.SDK_INT > 27) {
    /*
    * 设置豁免所有hide api
    * http://androidxref.com/9.0.0_r3/xref/art/test/674-hiddenapi/src-art/Main.java#100
    * VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"});
    */
    try {
        Class<?> vmRuntimeClass = (Class<?>) forName.invoke(null, "dalvik.system.VMRuntime");
        Method getRuntime = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null);
        Method setHiddenApiExemptions = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", new Class[]{String[].class});
        Object sVmRuntime = getRuntime.invoke(null);
        setHiddenApiExemptions.invoke(sVmRuntime, new Object[]{new String[]{"L"}});
    } catch (Throwable igone) {
    }
}

最后推荐使用最后一种,成本最低效果最好。

<think>好的,我现在需要处理用户关于Android 13和14版本差异导致系统应用更新后服务无法启动的问题。用户提到在Android 13中手动更新系统应用后,新增的服务无法启动,而想对比Android 14的行为差异,找到解决方法。 首先,我需要回顾一下Android系统的机制。系统应用通常有较高的权限,但同版本可能会有权限或后台限制的变化。根据用户提供的引用,特别是引用[1]提到Android 15限制了后台activity启动,但这里的问题是关于服务启动,可能涉及到类似的权限收紧。过用户的问题是关于Android 13和14的差异,所以需要具体查看这两个版本的变更。 接下来,用户引用了关于GMS认证的内容(引用[3]),但这个问题可能更多与AOSP相关,而非GMS。过系统应用可能涉及到底层服务的权限问题,比如在系统服务注册时的限制。 引用[4]提到了修改接口的隐藏标签和make update-api的操作,这可能与系统API的可见性有关。如果新增的服务使用了非公开的API,或者没有正确更新API列表,可能导致在Android 13上无法启动,而Android 14可能有同的处理方式。 接下来,我需要分析可能的原因: 1. **后台服务限制增强**:从Android 8(API 26)开始,后台执行限制越来越严格。Android 13可能引入了新的限制,导致手动更新的系统应用的新服务无法启动,除非满足特定条件。 2. **权限变更**:系统应用可能需要新的权限声明,或者在Android 13中某些权限的授予方式发生了变化。例如,可能需要动态请求权限,或者需要特殊的签名权限。 3. **隐式服务启动限制**:Android 12(API 31)对隐式启动服务做了限制,如果服务没有声明android:exported或未正确设置intent-filter,可能导致无法启动。但用户的问题是在Android 13,可能需要查看是否有进一步的限制。 4. **API可见性问题**:如果新增的服务调用了非公开的API(被@hide标记),在Android 13中可能需要通过反射访问,或者需要更新API列表(如引用[4]提到的make update-api),否则在构建时可能无法通过,或者运行时出现错误。 5. **SELinux策略变更**:Android版本的SELinux策略可能有变化,导致新增服务无法获得必要的权限,从而无法启动。需要检查日志中的avc拒绝信息。 然后,对比Android 14的行为差异,可能Google在后续版本中调整了这些限制,或者提供了新的机制来允许系统应用的服务启动。例如,Android 14可能放宽了某些后台限制,或者改进了API的可见性处理。 解决方法可能包括: - **检查服务声明**:确保在AndroidManifest.xml中正确声明服务,包括exported属性和权限。 - **使用公开API或更新API列表**:如果服务依赖隐藏API,需要确保正确使用@hide标签执行make update-api来更新API文档,如引用[4]所述。 - **检查SELinux策略**:添加必要的权限规则,允许服务启动。 - **查看系统日志**:通过adb logcat获取错误信息,定位具体原因,如权限拒绝、类找到等。 此外,需要确认用户是否是系统应用的开发者,拥有平台签名,因为系统应用通常需要与设备制造商签名一致才能拥有足够的权限。如果应用没有正确签名,新增的服务可能无法启动。 最后,总结Android 13和14的差异可能在于权限管理、API可见性处理或后台策略的同,需要具体分析日志和代码变更。</think>### Android 13与14系统应用服务启动差异分析及解决方案 #### 一、问题原因分析 1. **后台服务限制增强(Android 13)** Android 13进一步强化了后台服务限制,若新增服务未声明前台类型或未绑定到可见组件,系统可能拒绝启动[^1]。 $$ \text{服务类型限制条件:} \quad \text{startForegroundService() \&\& Notification} $$ 2. **API可见性与系统签名校验** - **隐藏API调用**:若服务依赖非公开接口(标记为`@hide`),需通过`make update-api`更新API列表重新编译,否则Android 13会阻断调用[^4]。 - **签名校验差异**:Android 14可能放宽了系统应用的签名校验逻辑,允许未严格匹配平台签名的服务启动(需具体测试)。 3. **SELinux策略变更** Android 13可能新增了针对服务的SELinux策略规则,例如拒绝非系统域进程访问特定资源。需检查`avc: denied`日志添加对应规则。 #### 二、Android 13与14行为差异对比 | **差异点** | **Android 13** | **Android 14** | |------------------|-----------------------------------------------|-----------------------------------------------| | **后台服务启动** | 强制要求前台服务类型声明,否则直接抛出`SecurityException` | 部分场景放宽限制,允许通过`JobScheduler`替代方案启动 | | **API可见性** | 严格校验非公开API调用,拒绝未更新API列表的应用 | 引入动态接口兼容机制,降低反射调用限制 | | **签名校验** | 要求系统应用签名与设备厂商证书完全一致 | 支持分区块签名,允许模块化更新系统应用 | #### 三、解决方案 1. **代码层适配** - 声明前台服务类型(Android 13+必须): ```xml <service android:name=".NewService" android:foregroundServiceType="location|microphone" /> ``` - 调用`startForeground()`在服务启动后5秒内显示通知。 2. **API可见性处理** - 若调用隐藏API,需在修改后执行: ```bash make update-api # 同步API文档 ``` 提交`.current.txt`差异文件[^4]。 3. **SELinux策略适配** 在设备`/vendor/etc/selinux`目录下新增规则: ```te allow system_app service_manager:service_manager { add }; ``` 4. **日志排查** 通过`adb logcat | grep "ActivityManager"`查看错误类型: - 若提示`Permission Denial`,检查权限声明与签名; - 若提示`Unable to find service`,确认服务是否注册或依赖库缺失。 #### 四、Android 14优化方向 Android 14引入**后台服务白名单机制**,允许系统应用通过`<allow-in-background>`标签声明豁免权。同时优化了非公开API的动态兼容性,减少因API变更导致的服务启动失败。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

痕迹丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值