
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;
}
```