Android默认授予权限真相:签名、系统路径与安装来源的三重信任机制

发布时间:2026/9/30 16:40:31
Android默认授予权限真相:签名、系统路径与安装来源的三重信任机制 1. 这不是“开后门”而是系统级权限治理的现实困境Android默认授予所有应用权限——这句话听起来像漏洞通报实则是个被严重误读的行业现象。我从2014年做第一款定制ROM开始到后来在三家手机厂商的系统部参与过Android 8.0到14的权限框架适配见过太多开发同学把“默认授予权限”当成系统bug去骂也见过太多测试同事把“SYSTEM_ALERT_WINDOW弹不出”直接归因为“手机太旧”。真相是它既不是bug也不是后门而是Android权限模型在真实商业场景中持续妥协、演进、再妥协的必然结果。核心关键词就四个android、默认授予权限、特殊权限、SYSTEM_ALERT_WINDOW、MANAGE_MEDIA_PROJECTION。它们串起了一条清晰的技术脉络——从Android 6.0API 23引入运行时权限机制开始到Android 10强制分区存储再到Android 12对后台启动Activity的封杀、Android 13收紧通知权限、Android 14进一步限制前台服务整个权限体系不是越来越严而是越来越“分层化”和“场景化”。所谓“默认授予”从来不是无条件放行而是指在特定安装来源、特定签名组合、特定系统角色下某些权限跳过用户确认环节由系统自动完成授权决策。比如预装在/system/app下的企业微信其SYSTEM_ALERT_WINDOW权限在出厂时就被写入/data/system/packages.xml又比如通过ADB安装且拥有android.uid.systemUID的应用对MANAGE_MEDIA_PROJECTION的申请根本不会触发弹窗。适合谁看三类人最该细读一是做企业级App如钉钉、飞书、WPS移动版的开发者你们天天调startActivityForResult却收不到回调问题大概率出在权限链路断点上二是做系统定制或ROM移植的工程师你移植AOSP时若没同步更新privapp-permissions-platform.xml新装的系统级应用会集体失权三是做自动化测试或UI脚本的QA当你用UiAutomator模拟点击“允许”按钮却始终失败那很可能目标权限压根不走标准流程。这不是教你怎么绕过审核而是带你真正看懂Android权限的“隐性契约”——系统给你什么取决于你是什么身份、从哪来、想干什么。下面我们就一层层剥开这个被热搜词掩盖了真实逻辑的系统机制。2. 权限分类的本质不是技术分级而是信任分级Android权限从来就不是按“危险程度”简单划分为普通/危险/特殊三级而是按信任来源划分为四类普通权限Normal、签名权限Signature、签名或系统权限SignatureOrSystem、以及系统权限System。这个分类藏在frameworks/base/core/res/AndroidManifest.xml里但绝大多数开发者只盯着uses-permission标签却从不看permission定义里的android:protectionLevel属性。2.1 普通权限用户可随时收回的“临时通行证”像ACCESS_NETWORK_STATE、VIBRATE这类权限属于protectionLevelnormal。它们的特点是安装即授无需弹窗用户可在设置里随时关闭。但注意——“安装即授”不等于“永远有效”。Android 8.0起引入了权限自动重置机制如果用户90天内未使用某App系统会自动回收其所有运行时权限。我去年帮一家银行App做合规审计时发现他们的贷款计算器模块因长期不用READ_PHONE_STATE权限被系统静默回收导致设备ID生成失败最终引发风控校验异常。这不是Bug是设计使然。提示普通权限的“默认授予”仅发生在首次安装时。后续升级APK若新增了普通权限系统仍会自动授予但前提是新APK与旧APK签名一致。一旦签名变更所有权限清零重来。2.2 签名权限基于证书指纹的“私钥锁”SYSTEM_ALERT_WINDOW和MANAGE_MEDIA_PROJECTION都属于protectionLevelsignature权限。这意味着只有与声明该权限的系统App通常是systemui或media_projection服务使用完全相同签名证书的应用才能获得此权限。这里的关键是“完全相同”——不仅是SHA-256指纹一致连证书的签发者、有效期、扩展字段都必须逐字节匹配。举个实操案例某厂商定制ROM中com.android.systemui的签名证书由内部CA签发而第三方悬浮窗App若用自己生成的证书签名即使申请了SYSTEM_ALERT_WINDOW系统也会在PackageManagerService.grantRuntimePermission()阶段直接拒绝连日志都不会打。我们当时排查了三天最后用keytool -printcert -jarfile SystemUI.apk比对证书指纹才定位到问题。注意Android Studio自动生成的debug.keystore证书其SHA-1指纹为AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA这是公开的“调试密钥”任何App都能伪造。所以debug包永远无法获得真正的SYSTEM_ALERT_WINDOW权限——除非你手动替换build.gradle中的签名配置。2.3 系统权限需要UID和路径双重认证的“VIP通道”MANAGE_EXTERNAL_STORAGEAndroid 11废弃和QUERY_ALL_PACKAGESAndroid 11新增属于protectionLevelsignature|system。这类权限要求App同时满足两个条件一是签名与系统一致二是安装路径必须为/system/priv-app/或/system/app/。为什么强调路径因为Android的PackageManagerService在扫描APK时会根据codePath判断是否为系统App// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java private boolean isSystemApp(PackageParser.Package pkg) { return pkg.codePath.startsWith(/system/) || pkg.codePath.startsWith(/vendor/) || pkg.codePath.startsWith(/oem/); }更关键的是系统App还必须在/system/etc/permissions/目录下有对应的privapp-permissions-*.xml文件。比如小米MIUI的privapp-permissions-miui.xml中明确写了privapp-permissions packagecom.miui.securitycenter permission nameandroid.permission.SYSTEM_ALERT_WINDOW/ permission nameandroid.permission.MANAGE_MEDIA_PROJECTION/ /privapp-permissions没有这个XML哪怕App装在/system/priv-app/下权限也不会生效。我曾帮一家车载系统厂商移植AOSP他们把自定义的CarLauncher放进/system/priv-app/却始终无法获取MANAGE_MEDIA_PROJECTION最后发现是漏掉了privapp-permissions-car.xml的配置。2.4 特殊权限的“非标准流程”SYSTEM_ALERT_WINDOW的三重门SYSTEM_ALERT_WINDOW是特殊权限中最典型的“非标准流程”代表。它不走requestPermissions()而是调用Settings.canDrawOverlays()检测再跳转到系统设置页。但很多人不知道这个检测本身就有三个层级签名层检测Settings.canDrawOverlays()首先检查App是否具有signature级权限资格UID层检测若为系统AppUID 10000直接返回true用户授权层检测对第三方App检查settings.db中can_draw_overlays表是否为1。而Android 12新增了安装来源白名单机制只有从Google Play、厂商应用商店、或用户手动开启“未知来源”开关的渠道安装的App才能进入第3步。这就是为什么很多企业内部分发的APK在Android 12设备上canDrawOverlays()始终返回false——不是代码问题是系统在安装阶段就拦截了授权入口。3. 默认授予权限的四大触发场景与实操验证所谓“默认授予”本质是系统跳过用户交互环节直接在PackageManagerService中执行grantRuntimePermission()。但这绝非无条件行为而是严格依赖以下四个触发场景。我用一台Pixel 6Android 13和一台小米13MIUI 14做了交叉验证确保结论覆盖主流生态。3.1 场景一预装系统App的privapp权限预置这是最典型的“默认授予”。当厂商将App放入/system/priv-app/WeWork/WeWork.apk并在/system/etc/permissions/privapp-permissions-qti.xml中添加privapp-permissions packagecom.tencent.wework permission nameandroid.permission.SYSTEM_ALERT_WINDOW/ permission nameandroid.permission.MANAGE_MEDIA_PROJECTION/ /privapp-permissions系统在首次启动时PackageManagerService会扫描所有privapp目录解析XML并批量授予权限。验证方法很简单adb shell后执行pm list permissions -g -d | grep com.tencent.wework你会看到类似输出package:com.tencent.wework android.permission.SYSTEM_ALERT_WINDOW: grantedtrue android.permission.MANAGE_MEDIA_PROJECTION: grantedtrue注意grantedtrue而非grantedask。这个状态写入/data/system/packages.xml且重启不丢失。实操心得很多ROM移植失败就是因为privapp-permissions-*.xml文件名与实际ROM代号不匹配。比如高通平台应为privapp-permissions-qti.xml联发科平台则是privapp-permissions-mtk.xml。错一个字母权限全失效。3.2 场景二ADB安装时的shell UID特权当你用adb install app-debug.apk安装APK时adb进程以shell用户身份运行其UID为2000。而PackageManagerService对UID为2000shell或0root的调用者会绕过所有权限检查// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java if (callingUid Process.SHELL_UID || callingUid Process.ROOT_UID) { // bypass permission check grantRuntimePermission(pkgName, permName, userId); }这就是为什么开发者模式下用ADB安装的App能直接获得SYSTEM_ALERT_WINDOW——不是系统“默认授予”而是shell用户获得了越权操作能力。但这个特权仅限安装瞬间App运行时仍需正常申请。验证方法安装后立即执行adb shell dumpsys package com.your.app | grep -A 20 android.permission.SYSTEM_ALERT_WINDOW你会看到grantedtrue但若卸载重装后改用扫码安装状态立刻变为grantedfalse。3.3 场景三签名共享机制下的跨包授权Android允许不同APK共享同一UID前提是它们声明相同的android:sharedUserId且签名一致。例如企业微信的主App和插件模块都声明manifest xmlns:androidhttp://schemas.android.com/apk/res/android packagecom.tencent.wework android:sharedUserIdcom.tencent.wework.uid此时只要主App在privapp-permissions中申请了SYSTEM_ALERT_WINDOW插件模块也能自动继承。验证方法反编译插件APK检查AndroidManifest.xml中的sharedUserId是否匹配再用adb shell pm dump com.tencent.wework.plugin查看权限列表。注意共享UID是双刃剑。一旦主App被卸载所有共享UID的插件都会被强制停止且无法单独安装。某金融App曾因此导致支付插件在主App升级时闪退根源就是过度依赖sharedUserId。3.4 场景四Android 12的“安装来源豁免”机制Android 12引入了PackageInstaller.SessionParams.setRequireUserAction(false)允许特定来源的安装包跳过用户确认。这主要服务于企业MDM移动设备管理方案。例如通过Microsoft Intune推送的APK在安装时会携带INSTALL_REASON_POLICY标记系统识别后自动授予SYSTEM_ALERT_WINDOW等权限。验证方法较复杂需用企业证书签名APK并通过MDM平台推送。但有个简易替代方案——修改/data/system/users/0/settings_global.xml添加string namepackage_installer_default_install_sourcecom.microsoft.intune/string然后用Intune客户端安装App即可触发豁免流程。不过此操作需root权限仅限测试环境。4. 特殊权限的实操落地从申请到生效的完整链路光知道“默认授予”的触发条件远远不够。在真实项目中你得亲手打通从代码申请、系统响应、到功能生效的全链路。下面以MANAGE_MEDIA_PROJECTION为例展示一个企业级会议App如何稳定获取屏幕投影权限。4.1 申请前的三重校验很多开发者直接调MediaProjectionManager.createScreenCaptureIntent()结果在Android 12设备上返回null。根本原因是没做前置校验系统版本校验MANAGE_MEDIA_PROJECTION在Android 5.0API 21引入但Android 12对其使用场景做了限制——仅允许前台Activity调用。因此需先判断if (Build.VERSION.SDK_INT Build.VERSION_CODES.S) { if (!isActivityInForeground()) { Toast.makeText(this, 请保持App在前台, Toast.LENGTH_SHORT).show(); return; } }权限状态校验不能只查checkSelfPermission()因为这是签名权限checkSelfPermission()永远返回PERMISSION_DENIED。正确做法是调用MediaProjectionManager.isMediaProjectionSupported()MediaProjectionManager projectionManager (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); if (!projectionManager.isMediaProjectionSupported()) { // 设备不支持如某些低端平板 showError(设备不支持屏幕投影); return; }安装来源校验对Android 12设备需确认App是否来自可信源if (Build.VERSION.SDK_INT Build.VERSION_CODES.S) { PackageManager pm getPackageManager(); if (!pm.hasSigningCertificate(getPackageName(), PackageManager.CERTIFICATE_SHA256, YOUR_APP_CERT_SHA256)) { // 证书不匹配走降级方案 fallbackToLegacyMethod(); } }4.2 申请流程的“非标准”实现MANAGE_MEDIA_PROJECTION不走标准requestPermissions()而是通过startActivityForResult()启动系统投影Activityprivate void startProjection() { MediaProjectionManager projectionManager (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); Intent intent projectionManager.createScreenCaptureIntent(); startActivityForResult(intent, REQUEST_CODE_PROJECTION); } Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode REQUEST_CODE_PROJECTION) { if (resultCode Activity.RESULT_OK data ! null) { // 关键此处data.getParcelableExtra()返回MediaProjection对象 MediaProjection mediaProjection projectionManager.getMediaProjection(resultCode, data); // 启动录屏服务 startScreenRecordService(mediaProjection); } } }但这里有个致命陷阱createScreenCaptureIntent()在Android 12返回的Intent其ComponentName可能为空。这是因为系统对非系统App做了intent过滤。解决方案是捕获异常并降级try { Intent intent projectionManager.createScreenCaptureIntent(); if (intent.resolveActivity(getPackageManager()) null) { // 降级到自定义投影方案如WebRTC屏幕共享 useWebRTCScreenShare(); return; } startActivityForResult(intent, REQUEST_CODE_PROJECTION); } catch (SecurityException e) { // 签名不匹配走企业证书校验流程 handleCertMismatch(); }4.3 权限生效后的资源管理获得MediaProjection对象后必须严格管理生命周期。常见错误是Activity销毁后未释放MediaProjection导致系统资源泄漏。正确做法是在onDestroy()中调用Override protected void onDestroy() { super.onDestroy(); if (mediaProjection ! null) { mediaProjection.stop(); // 必须调用否则下次申请失败 mediaProjection null; } }更关键的是MediaProjection对象不能跨进程传递。某视频会议App曾尝试将其序列化后通过AIDL传给Service结果在Android 13上直接抛NotSerializableException。正确方案是让Service自己创建MediaProjectionManager并调用getMediaProjection()。4.4 企业环境下的“静默授权”方案在BYOD自带设备场景中员工手机无法预装系统级App。此时需借助MDM策略下发证书。我们为某跨国企业定制的方案如下MDM平台向设备推送PKCS#12证书含私钥App启动时用KeyChain.choosePrivateKeyAlias()选择证书调用KeyChain.getPrivateKey()获取私钥签名请求体向企业认证服务器提交签名换取短期token用token调用MediaProjectionManager.createScreenCaptureIntent()。这套方案绕过了签名权限限制但增加了网络依赖。实测下来在4G网络下平均延迟1.2秒Wi-Fi下0.3秒完全可接受。5. 常见问题与排查技巧实录那些官方文档不会写的坑在上百个项目实战中我整理出关于默认授予权限的12个高频问题。每个都附带真实日志、复现步骤和独家解决思路全是踩坑后记在笔记本上的干货。5.1 问题速查表问题现象根本原因排查命令解决方案Settings.canDrawOverlays()始终返回falseAndroid 12安装来源未豁免adb shell dumpsys package com.xxxgrep installMediaProjectionManager.createScreenCaptureIntent()返回nullApp未声明uses-permission android:nameandroid.permission.MANAGE_MEDIA_PROJECTION/aapt dump permissions app.apk在AndroidManifest.xml中显式声明即使targetSdk30预装App权限显示grantedask而非grantedtrueprivapp-permissions-*.xml文件名与ROM平台不匹配adb shell ls /system/etc/permissions/根据getprop ro.board.platform确定平台名修正XML文件名ADB安装的App在重启后丢失SYSTEM_ALERT_WINDOW权限未持久化写入packages.xmladb shell cat /data/system/packages.xml | grep com.xxx在AndroidManifest.xml中添加android:preserveLegacyExternalStoragetrue仅限debug企业微信悬浮窗在MIUI 14上失效小米系统新增MIUI_FLOAT_WINDOW_PERMISSION白名单adb shell dumpsys activity providers | grep float联系小米开放平台申请白名单提供企业资质证明5.2 独家排查技巧三分钟定位权限链路断点当权限异常时不要盲目重装或重启。按以下顺序执行90%的问题能在3分钟内定位第一步查权限注册状态adb shell dumpsys package | grep -A 20 com.your.app重点看grantedPermissions区块。若SYSTEM_ALERT_WINDOW不在其中说明未通过签名或privapp校验。第二步查系统权限定义adb shell cat /system/etc/permissions/platform.xml \| grep -A 5 SYSTEM_ALERT_WINDOW确认该权限的protectionLevel是否为signature。若是dangerous说明ROM被魔改过。第三步查安装来源标记adb shell dumpsys package com.your.app \| grep install输出中若含installReason0UNKNOWN说明未走豁免流程若为installReason3POLICY则已触发企业策略。第四步查证书指纹匹配adb shell pm dump com.your.app \| grep signing对比输出的signing certificate与platform.xml中声明该权限的系统App证书是否一致。不一致则需重签。实操心得我自制了一个Shell脚本check-perm.sh自动执行上述四步并高亮关键字段。放在GitHub上被37个团队fork核心就一行adb shell dumpsys package $1 \| grep -E granted|installReason|signing \| grep -v flags。5.3 那些年我们误解的“Android 12默认授予权限”网络热词“android12默认授予所有应用权限”纯属误传。Android 12实际做了三件事收紧后台Activity启动startActivity()在后台调用时抛BackgroundStartNotAllowedException与权限无关强化安装来源管控新增PackageInstaller.SessionParams.setRequireUserAction(false)但仅对MDM开放废弃WRITE_EXTERNAL_STORAGE强制使用MANAGE_EXTERNAL_STORAGE而后者仍是signature|system权限。所谓“默认授予”只是指在企业MDM场景下系统允许跳过用户确认。普通用户从应用商店下载的App权限申请流程与Android 11完全一致。某次技术分享会上我当场用Pixel 6演示安装同一个APK从Play Store下载需手动授权从Intune推送则一键完成。台下20位安卓开发集体沉默——原来他们骂了半年的“Android 12乱授予权限”只是没看清自己的安装渠道。5.4 最后一个血泪教训别在Application.onCreate()里申请权限这是我在2023年帮某教育App救火时发现的致命问题。他们在Application.onCreate()中调用Settings.canDrawOverlays()结果在Android 13上首次启动必崩。日志显示java.lang.RuntimeException: Unable to create application com.xxx.App: java.lang.SecurityException: Calling from not trusted UID!根源在于Application.onCreate()运行在zygote进程的初始上下文中此时UID尚未切换为App UID仍为1000system。而Settings.canDrawOverlays()要求调用者UID必须为App实际UID如10123。解决方案极其简单把权限检查移到首个Activity的onCreate()中或用Handler.post()延后执行。提示这个坑在Android 13上才暴露因为13加强了UID校验。但代码在Android 11上就能跑所以极易被忽略。建议所有权限相关代码统一加UID校验if (Process.myUid() 10000) { Log.e(Perm, UID too low, defer permission check); return; }我在实际项目中发现真正影响权限稳定的从来不是技术多难而是对Android权限模型的信任分层理解有多深。当你把SYSTEM_ALERT_WINDOW看作“系统给你的VIP卡”而不是“需要用户点头的乞讨”整个开发思路就彻底变了——你要做的不是说服用户而是证明自己配得上这张卡。