ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

预装app的默认授权权限问题总结

预装app的默认授权权限问题总结 预装 App 的默认授权权限问题总结一、问题背景三个预装 APK 在首次开机后出现权限弹窗需要手动授权才能使用应用包名弹窗权限com.mantou.test1访问通讯录、允许拨打电话和管理电话、允许从未知来源进行安装、设备管理应用、允许该应用访问使用数据、始终允许获取此设备的位置信息com.mantou.test2无障碍功能com.mantou.test3允许录音二、权限分类与方案选型Android 权限按保护级别分为不同类型不同类型的权限需要不同的授予方式。仅靠privapp-permissions-platform.xml无法解决所有弹窗问题。权限类型与对应方案权限类型保护级别授予方式对应弹窗运行时权限 (Runtime)PROTECTION_DANGEROUSPackageManager.grantRuntimePermission()通讯录、电话、位置、录音等AppOps 特殊权限signature|appopAppOpsManager.setMode()未知来源安装、悬浮窗、写入设置、使用数据无障碍服务signatureSettings 数据库默认值 (defaults.xml)无障碍功能设备管理员signature|roleDevicePolicyManagerService内部注入设备管理应用签名权限 (Signature)PROTECTION_SIGNATUREprivapp-permissions-platform.xml无弹窗但需要平台签名才生效为什么 privapp-permissions-platform.xml 不够privapp-permissions-platform.xml的作用是允许预装的特权应用priv-app使用 signature 级别的权限。它只能解决签名权限问题无法授予运行时权限如READ_CONTACTS— 运行时权限需要用户确认或代码主动调用grantRuntimePermission()设置 AppOps 模式如REQUEST_INSTALL_PACKAGES— AppOps 有独立的状态机MODE_DEFAULT→MODE_ALLOWED启用无障碍服务— 需要写入Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES激活设备管理员— 需要调用 DPM 内部 API 注册 ActiveAdmin三、各权限的解决方案1. 运行时权限通讯录、电话、位置、录音等涉及应用com.mantou.test1、com.mantou.test2、com.mantou.test3修改文件frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java方案在AMS.finishBooting()完成后系统已完全启动通过PackageManager.grantRuntimePermission()直接授予运行时权限并设置FLAG_PERMISSION_GRANTED_BY_DEFAULT | FLAG_PERMISSION_SYSTEM_FIXED标志位使权限显示为系统固定授予用户无法撤销。核心代码privatevoidgrantXxxRuntimePermissions(StringtargetPkg,String[]permissions){PackageManagerpmmContext.getPackageManager();UserHandleuserUserHandle.of(UserHandle.USER_SYSTEM);for(Stringpermission:permissions){if(pm.checkPermission(permission,targetPkg)!PackageManager.PERMISSION_GRANTED){pm.grantRuntimePermission(targetPkg,permission,user);pm.updatePermissionFlags(permission,targetPkg,PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT|PackageManager.FLAG_PERMISSION_SYSTEM_FIXED,PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT|PackageManager.FLAG_PERMISSION_SYSTEM_FIXED,user);}}}各应用授予的权限列表应用权限com.mantou.test1READ_CONTACTS,WRITE_CONTACTS,CALL_PHONE,READ_PHONE_STATE,READ_PHONE_NUMBERS,ANSWER_PHONE_CALLS,ADD_VOICEMAIL,PROCESS_OUTGOING_CALLS,ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION,ACCESS_BACKGROUND_LOCATION,RECORD_AUDIO,CAMERA,WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE,POST_NOTIFICATIONS,GET_ACCOUNTScom.mantou.test2RECORD_AUDIO,CAMERA,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE,POST_NOTIFICATIONScom.mantou.test3RECORD_AUDIO,CAMERA,MODIFY_AUDIO_SETTINGS为什么在 AMS.finishBooting() 中执行早期尝试在DefaultPermissionGrantPolicy.grantDefaultPermissions()中执行但该方法在系统启动早期运行AppOps 服务未完全初始化授权无效参考代码模式AppOpsManager.unsafeCheckOpNoThrow()检查当前模式仅在MODE_DEFAULT时设为MODE_ALLOWED不覆盖用户手动设置2. AppOps 特殊权限未知来源安装、悬浮窗等涉及应用com.mantou.test1修改文件frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java方案在AMS.finishBooting()后通过AppOpsManager.setMode()将 AppOps 从MODE_DEFAULT改为MODE_ALLOWED。核心代码privatevoidgrantXxxAppOpPermission(StringtargetPkg,StringopStr){ApplicationInfoinfopm.getApplicationInfo(targetPkg,0);AppOpsManagerappOpsmContext.getSystemService(AppOpsManager.class);intcurrentModeappOps.unsafeCheckOpNoThrow(opStr,info.uid,info.packageName);if(currentModeAppOpsManager.MODE_DEFAULT){appOps.setMode(opStr,info.uid,info.packageName,AppOpsManager.MODE_ALLOWED);}}授予的 AppOps 列表AppOps 操作对应权限弹窗说明OPSTR_REQUEST_INSTALL_PACKAGESREQUEST_INSTALL_PACKAGES允许从未知来源进行安装OPSTR_SYSTEM_ALERT_WINDOWSYSTEM_ALERT_WINDOW悬浮窗权限OPSTR_WRITE_SETTINGSWRITE_SETTINGS修改系统设置OPSTR_GET_USAGE_STATSPACKAGE_USAGE_STATS允许该应用访问使用数据OPSTR_MANAGE_EXTERNAL_STORAGEMANAGE_EXTERNAL_STORAGE管理所有文件3. 无障碍服务com.mantou.test2涉及应用com.mantou.test2修改文件frameworks/base/packages/SettingsProvider/res/values/defaults.xmlframeworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java方案在 Settings 数据库初始化时写入默认的无障碍服务组件名使系统在首次启动时自动启用该服务。关键信息 — 无障碍服务组件名com.mantou.test2/com.mantou.test2.service.ScreenshotAccessibilityServicedefaults.xml 修改!-- 默认启用的无障碍服务com.mantou.test2 --stringnamedef_enabled_accessibility_servicestranslatablefalsecom.mantou.test2/com.mantou.test2.service.ScreenshotAccessibilityService/stringDatabaseHelper.java 修改// 在 loadSecureSettings() 方法中添加loadStringSetting(stmt,Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,R.string.def_enabled_accessibility_services);为什么选择 SettingsProvider 方案数据库初始化在系统启动早期执行比运行时注入更早、更可靠不需要修改AccessibilityManagerService减少代码侵入系统检测到ENABLED_ACCESSIBILITY_SERVICES有值后会自动设置ACCESSIBILITY_ENABLED1走过的弯路最初在AccessibilityManagerService.switchUser()中注入 — Settings 值被 AMS 自己的readConfigurationForUserStateLocked()覆盖在AMS.finishBooting()中写入Settings.Secure— 时机太晚或被覆盖修改isAccessibilityServiceWarningRequired()跳过警告 — 默认值方案成功后不需要4. 设备管理员com.mantou.test1涉及应用com.mantou.test1修改文件frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java方案在 DPM 的systemReady(PHASE_BOOT_COMPLETED)阶段直接操作内部数据结构注入 ActiveAdmin。关键信息 — 设备管理员 Receiver 组件名com.mantou.test1/com.mantou.test1.AdminReceiver核心代码privatevoidinjectDefaultDeviceAdminLocked(intuserId){finalStringtargetPkgcom.mantou.test1;// 硬编码 DeviceAdminReceiver 组件名finalComponentNameadminReceivernewComponentName(targetPkg,com.mantou.test1.AdminReceiver);try{DevicePolicyDatapolicygetUserData(userId);// 跳过已存在的管理员for(ActiveAdminaa:policy.mAdminList){if(aa.info!nulltargetPkg.equals(aa.info.getPackageName())){return;}}// 查找 DeviceAdminInfoDeviceAdminInfoinfofindAdmin(adminReceiver,userId,false);if(infonull)return;checkActiveAdminPrecondition(adminReceiver,info,policy);// 注入 ActiveAdminmInjector.binderWithCleanCallingIdentity(()-{ActiveAdminnewAdminnewActiveAdmin(info,false);policy.mAdminMap.put(adminReceiver,newAdmin);policy.mAdminList.add(newAdmin);enableIfNecessary(info.getPackageName(),userId);saveSettingsLocked(userId);sendAdminCommandLocked(newAdmin,DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,null,null);});}catch(Exceptione){Slogf.w(LOG_TAG,AdminInject: Failed: e.getMessage(),e);}}调用位置在systemReady的PHASE_BOOT_COMPLETED分支caseSystemService.PHASE_BOOT_COMPLETED:factoryResetIfDelayedEarlier();ensureDeviceOwnerUserStarted();// 注入默认设备管理员synchronized(getLockObject()){injectDefaultDeviceAdminLocked(UserHandle.USER_SYSTEM);}break;为什么在 PHASE_BOOT_COMPLETED 执行最初在handleStartUser()中执行 — 包信息未完全就绪getPackageInfoAsUser()查不到 receiversPHASE_BOOT_COMPLETED时所有 APK 已安装和解析完成走过的弯路在handleStartUser()中通过PackageManager.getPackageInfoAsUser(GET_RECEIVERS)动态查找 Receiver — 返回 null预装 APK 的组件可能处于DISABLED_UNTIL_USED状态加了MATCH_DISABLED_UNTIL_USED_COMPONENTS | MATCH_DISABLED_COMPONENTSflag — 仍然查不到最终改为硬编码组件名com.mantou.test1.AdminReceiver直接调用findAdmin()— 成功最初在 AMS 中调用DevicePolicyManager.setActiveAdmin()公开 API — 权限检查失败改为在 DPM 内部直接操作ActiveAdmin对象 — 成功5. privapp-permissions-platform.xml签名权限修改文件frameworks/base/data/etc/privapp-permissions-platform.xml方案为三个应用添加 signature 级别的权限声明使平台签名的预装应用可以使用这些权限。注意此文件的修改是客户提供的但仅靠此文件无法解决上述弹窗问题。它解决的是签名权限问题如MANAGE_DEVICE_POLICY_*系列对运行时权限、AppOps、无障碍服务、设备管理员无效。四、如何从 APK 中获取组件名1. 无障碍服务组件名无障碍服务在AndroidManifest.xml中声明为service并带有meta-data指向android.accessibilityservice.AccessibilityServiceInfo。获取方式方法一设备上查询已安装的情况adb shell dumpsys accessibility|grepcom.mantou.test2方法二用 PowerShell 解析 APK 二进制# 读取 APK 中的 AndroidManifest.xmlAXML 格式字符串以 UTF-16LE 存储$apk[System.IO.File]::ReadAllBytes(path/to/app.apk)Add-Type-AssemblyName System.IO.Compression.FileSystem$zip[System.IO.Compression.ZipFile]::OpenRead(path/to/app.apk)$entry$zip.Entries|Where-Object{$_.FullName-eqAndroidManifest.xml}$msNew-ObjectSystem.IO.MemoryStream$entry.Open().CopyTo($ms)$bin$ms.ToArray()$zip.Dispose()# UTF-16LE 解码后搜索无障碍服务类名$s16[System.Text.Encoding]::Unicode.GetString($bin)[regex]::Matches($s16,[a-z][a-z0-9.]*AccessibilityService)|ForEach-Object{$_.Value}从com.mantou.test2APK 中提取到的组件名com.mantou.test2/com.mantou.test2.service.ScreenshotAccessibilityService2. 设备管理员 Receiver 组件名设备管理员在AndroidManifest.xml中声明为receiver带有android.permission.BIND_DEVICE_ADMIN权限。获取方式方法一设备上查询adb shell dumpsys package com.mantou.test1|grep-A5BIND_DEVICE_ADMIN方法二用 PowerShell 解析 APK 二进制# 同上读取 AndroidManifest.xml 后$s16[System.Text.Encoding]::Unicode.GetString($bin)# 搜索 BIND_DEVICE_ADMIN 附近的 Receiver 类名$idx$s16.IndexOf(BIND_DEVICE_ADMIN)# 输出附近 1000 字符查看上下文$s16.Substring([Math]::Max(0,$idx-1000),2000)从com.mantou.test1APK 中提取到的组件名com.mantou.test1/com.mantou.test1.AdminReceiver3. PowerShell 解析 AXML 的原理Android 的AndroidManifest.xml在 APK 中以AXMLAndroid Binary XML格式存储不是纯文本 XML。但其中的字符串池以UTF-16LE编码存储可以直接用System.Text.Encoding.Unicode.GetString()解码后用正则表达式搜索。五、修改文件清单文件修改内容base/data/etc/privapp-permissions-platform.xml添加三个应用的 signature 级别权限声明客户提供base/packages/SettingsProvider/res/values/defaults.xml添加def_enabled_accessibility_services默认值base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java在loadSecureSettings()中加载无障碍服务默认值base/services/core/java/com/android/server/am/ActivityManagerService.java在finishBooting()后授予运行时权限和 AppOps 权限base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java在PHASE_BOOT_COMPLETED时注入设备管理员六、验证方法日志验证# 运行时权限与 AppOps 授权adb logcat-d|grep-iEGrant# 设备管理员注入adb logcat-d|grep-iEAdminInject状态验证# 检查无障碍服务adb shell settings get secure enabled_accessibility_services adb shell settings get secure accessibility_enabled# 检查设备管理员adb shell dumpsys device_policy|grepActive admins# 检查 AppOpsadb shell dumpsys appops--opREQUEST_INSTALL_PACKAGES--packagecom.mantou.test1 adb shell dumpsys appops--opGET_USAGE_STATS--packagecom.mantou.test1# 检查运行时权限adb shell dumpsys package com.mantou.test1|grepgrantedtrue七、经验总结权限类型决定方案不能一种方案解决所有弹窗运行时权限、AppOps、无障碍服务、设备管理员各有不同的授予机制执行时机很重要DefaultPermissionGrantPolicy太早AppOps 未初始化→AMS.finishBooting()合适→PHASE_BOOT_COMPLETED设备管理员最晚执行SettingsProvider 默认值方案最可靠无障碍服务通过defaults.xmlDatabaseHelper.java在数据库初始化时写入比运行时注入更早更可靠硬编码组件名优于动态查找预装 APK 的组件可能处于DISABLED_UNTIL_USED状态PackageManager.getPackageInfoAsUser()即使加了 flag 也可能查不到直接硬编码组件名最可靠从 APK 提取组件名AXML 格式的AndroidManifest.xml字符串以 UTF-16LE 存储可以用 PowerShell 直接解码搜索
返回列表