Android 自定义权限

一、 Android 自定义权限核心机制解释

关键点:自定义权限不是运行时动态申请的权限,是安装期解析、一次性固化到应用包信息里的系统元数据,不是服务端装完之后可以 "事后补发"。

1、权限是谁注册到系统的

xml

xml 复制代码
<!-- 服务端App Manifest -->
<permission android:name="com.cars.xxxcenter.china.PERMISSION_STORAGE"
    android:protectionLevel="normal" />

只有定义这个<permission>的服务端 APK 安装的时候 ,Android PackageManager 才会把这条权限:com.cars.xxxcenter.china.PERMISSION_STORAGE注册到系统全局权限数据库。

  • 服务端没装 → 系统完全不知道这个权限字符串是什么,数据库里没有这条记录。
  • 权限名字字符串完全一样也没用,系统数据库没有注册条目,它就是未知权限

2、客户端<uses‑permission>做了什么

客户端 manifest:

xml

ini 复制代码
<uses‑permission android:name="com.cars.xxxcenter.china.PERMISSION_STORAGE" />

uses‑permission 只在客户端自己安装那一刻生效

  1. 安装客户端时,PackageManager 扫描客户端 manifest;

  2. 去系统权限数据库查找这个权限名字;

    • ✅ 如果此时服务端已经装好,权限已注册:就把该权限授予客户端,记录到客户端的权限集合;
    • ❌ 如果此时服务端还没装,数据库查不到该权限 → 标记为 unknown permission,直接丢弃,不会保存这条权限申请
  3. 安装完成之后,PackageManager 不会再回头重新扫描一遍客户端的 uses‑permission

⚠️ 重点:后续你再把服务端装上去,系统不会遍历所有已安装 App,补授予之前被忽略掉的 uses‑permission。客户端已经安装完毕,它的权限快照已经固化。

3、现象对应你的故障

  1. 先装 demo (客户端):

    • 此时服务端未安装,权限未注册;
    • demo 安装阶段uses‑permission遇到未知权限,被静默丢弃;logcat 输出Unknown permission: xxx
    • demo 的权限列表里,从来就没有拿到过这个自定义权限
  2. 后装服务端:

    • 服务端安装,把PERMISSION_STORAGE注册进系统;
    • 但是 demo 不会被重新处理,不会自动补发这个权限
  3. 运行时调用 checkCallingPermission():去查调用方 (demo) 已授予的权限集合,里面根本没有该权限,直接返回PERMISSION_DENIED,ContentProvider 返回 403。

✅ 修复方式:服务端先安装,再安装客户端 demo ;如果顺序错了,必须卸载重装客户端,触发重新解析uses‑permission

4、为什么 normal 级别也不能运行时动态申请?

  • normal/signature自定义权限,不属于危险权限
  • requestPermissions()只针对dangerous权限;
  • 自定义 normal 权限没有弹窗申请这一套逻辑 ,全部是安装时一次性授予,运行时无法主动申请补发。

这是 Android 非常经典的坑:跨 App 自定义权限强依赖安装顺序,很多车载项目都会踩。

5、可选的规避方案(不想严格控制安装顺序)

  1. 签名权限 signature:两个 App 使用同一个系统签名,不需要依赖这套自定义权限校验,直接信任调用方签名;车载系统开发最常用。
  2. 不用自定义权限,改用 UID 校验:getCallingUid()判断调用方的 uid、包名,自己做白名单校验,完全绕开自定义权限安装顺序问题。
  3. 启动时客户端检测:客户端运行时检测自己是否拥有该自定义权限,如果没有,提示用户 "请先安装服务端并重装本应用"。

一句话总结

自定义权限是服务端安装时注册到系统数据库 ;客户端的 uses‑permission 只在客户端自己安装那一刻查询一次数据库。客户端先装的时候权限还没注册,申请直接丢掉;就算后面装服务端,旧客户端不会被重新处理,权限不会自动补上。

二、如果A(客户端)和B(服务端)是系统应用,那么还有安装顺序限制吗?

A (客户端)、B (服务端) 都是系统应用(预置在 system/system_ext/product/vendor 分区,ROM 烧录进去)
分两种场景:整机开机 PMS 扫描预置 APKadb push 动态更新系统 app,行为完全不一样。

场景 1:整机 ROM 烧录,开机扫描全部预置系统 App(车机量产 ROM 最常见)

开机时PackageManagerService一次性扫描全部系统分区下所有 APK,不是按普通 install 安装流程走 稀土掘金。PMS 会先把所有<permission>全部收集注册进权限数据库,再处理所有 App 的<uses‑permission>。✅ 此时不受安装顺序困扰

  1. B(服务端,定义<permission>)、A(客户端,<uses‑permission>)都是镜像预置;
  2. PMS 第一轮遍历全部系统 APK,把 B 声明的PERMISSION_STORAGE注册;
  3. 第二轮处理 A 的 uses‑permission,权限已经存在,正常授予 A;
  4. checkCallingPermission()正常返回成功,不会出现Unknown permission

量产车机镜像,两个 app 都预置到镜像,不存在 "谁先装谁后装" 的坑

场景 2:adb push 更新系统 App(调试阶段,非常高频踩坑)

你调试车机经常做的:adb remount && adb push B.apk /system/priv-app/xxx,这是动态更新已经开机跑起来的设备 ,不是整机开机扫描。这里行为和普通第三方 app 几乎一致,依然存在顺序坑

  1. 设备已经开机运行,PMS 已经完成 systemReady;

  2. 如果设备上先 push 安装 A (客户端) ,此时 B 还没 push 上去;

    • A 的 uses‑permission 遇到未知权限,被静默丢弃;
  3. 后续再 push B (服务端),B 把自定义权限注册进系统;

  4. ⚠️ PMS 不会回头重新扫描 A 的 manifest,不会补发权限给 A

  5. A 依旧拿不到该自定义权限,checkCallingPermission返回 DENIED,报 403。

👉 调试阶段 push 更新系统 app,依然要遵守:先 push B(定义权限的服务端),再 push A(客户端) ;如果顺序搞反,必须 卸载 A,再 push A,触发重新解析 uses‑permission。

关键区分总结表

表格

场景 是否受自定义权限顺序坑影响 说明
整机烧录 ROM,两个 app 都是镜像预置 ❌ 不受 PMS 开机一次性扫描全部系统 APK,先收集全部 permission 再授权 uses‑permission
adb push 动态更新系统 App(调试) ✅ 仍然受 等价运行时安装,遵循普通 app 安装逻辑,顺序错权限丢失,需要重装客户端
data/app 下普通第三方 App ✅ 受 最经典坑,必须先装定义权限的服务端

补充两个车载项目高频踩坑点

  1. 跨分区预置 B 在system_ext,A 在product:整机烧录场景没问题;PMS 开机扫描所有分区的 manifest,权限可以跨分区注册使用。
  2. protectionLevel=signature 特殊行为 如果权限是signature两个 app 相同签名,整机烧录没问题;调试 push 动态更新,同样遵守上面规则:A 先 push、B 后 push,A 依然拿不到权限,需要重装 A稀土掘金。
  3. 不要误以为 "系统 app 就可以绕开自定义权限安装顺序",只对整机开机扫描生效;调试 adb push 更新系统 app,坑完全保留,这是车载调试非常容易踩的。

工程上的稳妥方案(车机项目建议)

  1. 量产镜像:两个 app 全部预置,无顺序问题;
  2. 调试脚本:写 shell 脚本,先 push 服务端 B,再 push 客户端 A
  3. 兜底:代码增加自检逻辑,客户端 A 启动检测是否拥有该自定义权限,缺失则打印明确 log,方便定位调试问题;
  4. 备选方案:放弃自定义权限,校验调用方 UID / 包名白名单,彻底规避整套机制坑。

对应你之前的 xxx项目:烧录整机镜像没问题;但你日常 adb push 替换 apk 调试,依然会踩这个权限丢失 bug。

三、queries的作用?

xml 复制代码
<queries> 
    <package android:name="com.cars.xxxcenter.china" /> 
</queries>

1、<queries>到底做什么

Android 11+((API30+)) 开始引入包可见性:

默认情况下,你的 App 看不到设备上绝大多数第三方 / 系统 App。

写上:

xml

xml 复制代码
<queries><package android:name="com.cars.xxxcenter.china"/></queries>

✅ 允许你的客户端 App:

  1. PackageManager.getPackageInfo() / resolveContentProvider() 查到 com.volvocars.datacenter.china 这个包;
  2. 可以查询到它的 ContentProvider 信息(authority)。

它不能:

  • 不会让不存在的 Provider 变成存在;

  • 不会帮你申请自定义权限;

  • 不能防止 Unknown authority 崩溃。

相关推荐
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui