4. Android 权限机制

Android 的权限体系是应用安全模型的基石。在 Android Automotive 场景中,权限不仅决定普通应用能否访问摄像头、位置等资源,还直接关系到车辆信号、CAN 总线、诊断接口等敏感能力的开放边界。理解 normal、dangerous、signature、privileged 四类权限的定义、声明方式与授予流程,是后续进行权限裁剪、SELinux 策略配置和系统定制的前提。

一、四类权限的核心定义

Android 在 frameworks/base/core/res/AndroidManifest.xml 中通过 <permission> 标签定义权限,其中 protectionLevel 属性决定权限的保护级别。四类常见权限的本质区别在于“谁有资格获得”以及“用户是否需要参与授权”。

权限类型 protectionLevel 授予方式 典型场景
normal normal 安装时自动授予,无需用户确认 网络访问、蓝牙、修改音量
dangerous dangerous 运行时弹窗,由用户动态授权 位置、摄像头、麦克风、通讯录
signature signature 仅授予与声明者使用同一签名的应用 系统内部接口、厂商私有服务
privileged privileged(常与 signature 组合) 仅授予位于 /system/priv-app 的特权应用 车载系统服务、车辆属性访问

需要特别说明的是,privileged 并不是一个独立的 protectionLevel 取值,而是一种“安装位置 + 签名”的组合约束。实际定义中常见写法为 signature|privileged,表示该权限只授予系统签名且安装在 /system/priv-app 下的应用。

二、权限的声明与授予流程

权限的完整生命周期涉及三个角色:权限定义方(通常是 framework 或系统应用)、权限使用方(普通应用或系统应用)、以及权限管理服务(PackageManagerService)。下图展示了从权限定义到最终授予的关键路径。

Android 权限声明与授予流程 权限定义方 framework / 系统应用 AndroidManifest.xml 权限使用方 普通应用 / 特权应用 uses-permission 声明 PackageManagerService 解析权限 · 校验签名 判定 protectionLevel 定义权限 申请权限 normal 安装即授予 dangerous 运行时弹窗授权 signature 签名匹配授予 privileged priv-app + 签名 授予结果写入 packages.xml,运行时由 ActivityManagerService 与 AppOps 共同校验

流程可归纳为四个阶段:

  1. 定义阶段:系统在 framework 的 AndroidManifest.xml 中声明权限及其 protectionLevel。
  2. 声明阶段:应用在自己的 AndroidManifest.xml 中使用 <uses-permission> 申请权限。
  3. 解析与判定阶段:PMS 在安装或开机扫描时解析权限,根据 protectionLevel、应用签名、安装路径决定是否授予。
  4. 运行时校验阶段:应用调用受保护 API 时,系统通过 checkPermission、AppOps 等机制进行实时校验。

三、声明方式与代码示例

权限定义方在 framework 中声明一个 signature|privileged 权限,示例如下:

<permission
    android:name="android.car.permission.CAR_POWERTRAIN"
    android:protectionLevel="signature|privileged" />

应用侧申请权限时,只需在清单中声明:

<uses-permission
    android:name="android.car.permission.CAR_POWERTRAIN" />

对于 dangerous 权限,还必须在运行时动态请求:

if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
    requestPermissions(
        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
        REQUEST_CODE_LOCATION);
}
在 Android Automotive 中,许多车辆属性权限被定义为 signature|privileged,这意味着只有预置在 /system/priv-app 且使用平台签名的应用才能获得。普通第三方应用即使声明了这些权限,PMS 也不会授予。

四、典型场景与误区复盘

场景一:普通应用申请车辆速度权限。 某第三方导航应用声明了 android.car.permission.CAR_SPEED,但该权限为 signature|privileged。安装后 PMS 判定签名不匹配且不在 priv-app,权限被静默拒绝。应用调用相关 API 时抛出 SecurityException。正确做法是使用 CarPropertyManager 的公开接口,或由系统应用通过 Binder 代理转发数据。

场景二:dangerous 权限被用户拒绝后再次请求。 用户首次拒绝后,应用再次调用 requestPermissions,系统可能不再弹窗(取决于“不再询问”状态)。此时应引导用户进入设置页手动开启,而不是反复弹窗骚扰。

场景三:privileged 权限与 SELinux 的混淆。 有开发者认为只要把应用放进 /system/priv-app 就能获得所有权限。实际上 privileged 只解决 Android 权限层面的授予,SELinux 仍会独立校验进程对设备节点、socket、属性的访问。权限通过但 SELinux 拒绝,依然会失败。

常见误区:把 privileged 当作 protectionLevel 的独立取值直接写入。正确写法是 signature|privileged 或 privileged 与其他级别组合。单独写 privileged 在部分版本中虽可解析,但语义不完整,容易导致权限判定不符合预期。

复盘要点可以归纳为一张检查清单:

  • 权限是否已在 framework 的 AndroidManifest.xml 中定义,protectionLevel 是否正确。
  • 应用是否在清单中声明了 uses-permission,名称是否完全匹配。
  • dangerous 权限是否实现了运行时请求与拒绝后的降级逻辑。
  • signature 权限的应用签名是否与定义方一致。
  • privileged 权限的应用是否确实位于 /system/priv-app 且被正确扫描。
  • 权限通过后,SELinux 策略是否允许对应的底层访问。

掌握这四类权限的边界与授予路径,才能在 Android Automotive 的系统定制中准确判断“某个能力应该开放给谁、通过什么方式开放”,避免权限过宽带来的安全风险,也避免权限不足导致功能不可用。