去掉安卓的“读取已安装应用列表”,隐私合规

vivo:https://dev.vivo.com.cn/documentCenter/doc/747

XML 复制代码
<meta-data
     android:name="oem_installed_apps_runtime_permission_enable"
     android:value="true"/>

小米:https://dev.mi.com/xiaomihyperos/documentation/detail?pId=1619

XML 复制代码
<meta-data
     android:name="do_not_need_get_installed_apps"
     android:value="true"/>

通过上述方案适配,小米和vivo设备基本就不会弹出了。不过oppo和华为(mate 30 pro) 还是会弹出

最终方案:

在工程AndroidManifest.xml里加上以下代码:

XML 复制代码
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" 

tools:node="remove" />

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" 

tools:ignore="QueryAllPackagesPermission" 

tools:node="remove" />

在混淆文件proguard-rules.pro里加上以下代码:

XML 复制代码
-assumenosideeffects class android.content.pm.PackageManager {
    public java.util.List getInstalledPackages(int) return null;
    public java.util.List getInstalledApplications(int) return null;
}

即可解决oppo和华为设备的 "读取已安装应用列表" 弹窗

最最终方案:

与隐私合规检测三方进行沟通,机审存在误判

XML 复制代码
截图内给到的日志1是:

```
2026-03-30 15:24:08.416  2524-3019  TelephonyPermissions  com.android.phone
E Exception caught obtaining package info for package null 
android.content.pm.PackageManager$NameNotFoundException
    at android.app.ApplicationPackageManager.getApplicationInfoAsUser(ApplicationPackageManager.java:452)
    at android.content.pm.PackageManager.getApplicationInfoAsUser(PackageManager.java:4759)
    at com.android.internal.telephony.TelephonyPermissions.reportAccessDeniedToReadIdentifiers(TelephonyPermissions.java:
    at com.android.internal.telephony.TelephonyPermissions.checkPrivilegedReadPermissionOrCarrierPrivilegePermission(Tele
    at com.android.internal.telephony.TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(TelephonyPermissions.j
    at com.android.internal.telephony.PhoneSubInfoController.callPhoneMethodForPhoneIdWithReadDeviceIdentifiersCheck(Phon
    at com.android.internal.telephony.PhoneSubInfoController.getDeviceIdForPhone(PhoneSubInfoController.java:74)
    at com.android.internal.telephony.PhoneSubInfoController.getDeviceIdWithFeature(PhoneSubInfoController.java:68)
    at com.android.internal.telephony.PhoneSubInfoController.getDeviceId(PhoneSubInfoController.java:64)
    at com.android.internal.telephony.IPhoneSubInfo$Stub.onTransact(IPhoneSubInfo.java:457)
    at android.os.Binder.execTransactInternal(Binder.java:1184)
    at android.os.Binder.execTransact(Binder.java:1143)
```

然后 上面的 d2f34955c580bff6d5c54bf3fb821cda.xlsx 给到的日志2是 :

```
	at android.app.ApplicationPackageManager.getInstalledPackagesAsUser()
	at android.app.ApplicationPackageManager.getInstalledPackages(ApplicationPackageManager.java:735)
```

上述反馈日志 1 是具体可以本地复线的场景,从日志 1 内也能看出缺少关键的 getInstalledPackages 方法,我们已经仔细分析 sdk 内的逻辑,在设置 Main.setConfig("pkglist", "1"); 方法关闭应用列表采集之后,模块内就不会触发 getInstalledPackages 的调用。

还有获取设备已安装应用列表的系统标准 API 是 getInstalledPackages 或 getInstalledApplications。而本次堆栈中触发的是 getApplicationInfoAsUser ,这两个接口有着本质区别:后者仅用于查询单一指定包名的应用信息,完全不具备"获取列表"的功能。

从源码内可以看到触发日志的方法是
```
callingPackageInfo = context.getPackageManager().getApplicationInfoAsUser(callingPackage, 0, UserHandle.getUserHandleForUid(uid));
```
系统传入的参数是 callingPackage(即当前正在运行的 App 自身的包名)。系统调用此接口的唯一目的,是为了在第 442 行核对当前 App 的 targetSdkVersion 是否小于 Android 10 (Q):
```
if (callingPackageInfo != null && (callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q))
```


这是 Android 系统底层为了兼容老版本 App 的权限逻辑,系统自身发起的一次"查询当前 App 自身的 targetSdkVersion"的操作,完全不涉及对外部其他应用的扫描,更未触碰任何"应用列表"隐私数据。建议将此源码逻辑反馈给审核机构,以解除自动化工具的误判。


日志对应的源码:

https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/telephony/common/com/android/internal/telephony/TelephonyPermissions.java;l=417?q=reportAccessDeniedToReadIdentifiers

```
/**
     * Reports a failure when the app with the given pid/uid cannot access the requested identifier.
     *
     * @returns false if the caller is targeting pre-Q and does have the READ_PHONE_STATE
     * permission or carrier privileges.
     * @throws SecurityException if the caller does not meet any of the requirements for the
     *                           requested identifier and is targeting Q or is targeting pre-Q
     *                           and does not have the READ_PHONE_STATE permission or carrier
     *                           privileges.
     */
    private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
            int uid, String callingPackage, String message) {
        ApplicationInfo callingPackageInfo = null;
        try {
            callingPackageInfo = context.getPackageManager().getApplicationInfoAsUser(
                    callingPackage, 0, UserHandle.getUserHandleForUid(uid));
        } catch (PackageManager.NameNotFoundException e) {
            // If the application info for the calling package could not be found then assume the
            // calling app is a non-preinstalled app to detect any issues with the check
            Log.e(LOG_TAG, "Exception caught obtaining package info for package " + callingPackage,
                    e);
        }
        // The current package should only be reported in StatsLog if it has not previously been
        // reported for the currently invoked device identifier method.
        boolean packageReported = sReportedDeviceIDPackages.containsKey(callingPackage);
        if (!packageReported || !sReportedDeviceIDPackages.get(callingPackage).contains(
                message)) {
            Set invokedMethods;
            if (!packageReported) {
                invokedMethods = new HashSet<String>();
                sReportedDeviceIDPackages.put(callingPackage, invokedMethods);
            } else {
                invokedMethods = sReportedDeviceIDPackages.get(callingPackage);
            }
            invokedMethods.add(message);
            TelephonyCommonStatsLog.write(TelephonyCommonStatsLog.DEVICE_IDENTIFIER_ACCESS_DENIED,
                    callingPackage, message, /* isPreinstalled= */ false, false);
        }
        Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message + ":"
                + subId);
        // if the target SDK is pre-Q then check if the calling package would have previously
        // had access to device identifiers.
        if (callingPackageInfo != null && (
                callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q)) {
            if (context.checkPermission(
                    android.Manifest.permission.READ_PHONE_STATE,
                    pid,
                    uid) == PackageManager.PERMISSION_GRANTED) {
                return false;
            }
            if (checkCarrierPrivilegeForSubId(context, subId)) {
                return false;
            }
        }
        throwSecurityExceptionAsUidDoesNotHaveAccess(message, uid);
        return true;
    }
```
相关推荐
十六年开源服务商4 小时前
2026服务器配置优化与WordPress运维实战指南
android·运维·服务器
音视频牛哥6 小时前
大牛直播SDK(SmartMediaKit)Android平台Unity3D RTSP/RTMP播放器集成实践
android·unity3d·rtsp播放器·rtmp播放器·unity3d rtmp播放器·安卓unity rtsp播放器·安卓unity rtmp播放器
w1wi6 小时前
安卓抓包完全指南(一):从入门到 SSL Pinning 绕过
android·网络协议·ssl
aqi008 小时前
一文理清 HarmonyOS 6.0.2 涵盖的十个升级点
android·华为·harmonyos·鸿蒙·harmony
赏金术士8 小时前
Jetpack Compose 状态提升(State Hoisting)完全指南
android·kotlin·compose
BoomHe9 小时前
git Rebase 为任意一笔提交补上 Change-Id
android·git·android studio
TDengine (老段)9 小时前
TDengine 超级表/子表/普通表 — 设计理念与内部表示
android·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
shuaiqinke10 小时前
【分享】Edge浏览器|内置扩展仓库|支持油猴|上网无限制
android·前端·人工智能·edge
Carson带你学Android10 小时前
见证历史!Swift 6.3 官方支持 Android,跨平台要变天了?
android