Android 权限管理机制
一. 权限管理核心机制
Android 的权限管理建立在 Linux 的基础上,并加入了更高级别的抽象和控制。其核心机制可以概括为以下几点:
- Linux 内核层隔离 (UID/GID)
○ 基础: Android 继承并强化了 Linux 的 UID(用户ID)和 GID(组ID)机制
○ 沙箱: 每个 Android 应用在安装时都会被分配一个唯一的、永久的 UID。这意味着每个应用都在自己独立的 Linux 进程中运行,拥有自己的文件目录(/data/data/<package_name>
),其文件权限被设置为仅允许自身(即相同 UID)访问。这构成了应用沙箱的基础
○ 权限映射: 许多 Android 权限(特别是设备资源相关的,如android.permission.BLUETOOTH
)在底层实际上被映射到一个特定的 GID(例如net_bt
)。当应用被授予该权限时,它的进程就会被加入到对应的 GID 组中。在 Linux 中,组权限决定了能否访问某些设备驱动(如/dev/bluetooth
) - 权限级别 (Protection Level)
权限在AndroidManifest.xml
中定义时,会通过android:protectionLevel
属性来声明其危险程度和保护方式
○ normal: 低风险权限,系统在安装时自动授予
○ dangerous: 高风险权限,涉及用户隐私或设备控制。必须向用户显式请求并由用户批准
○ signature: 只有与定义该权限的应用使用相同证书签名的应用才能获得此权限。用于应用套件内部通信
○ signatureOrSystem / internal: 更高级别的系统权限,通常仅供 OEM 或系统应用使用 - 权限授予时机
○ 安装时授权 (Android 5.1及以前): 用户在安装应用前,必须同意应用请求的所有权限
○ 运行时授权 (Runtime Permissions, Android 6.0+): 对于dangerous
级别的权限,用户可以在应用运行时再决定是否授予。应用可以在需要时动态请求,用户也可以随时在设置中撤销授权。这给了用户更大的控制权 - 权限执行者 (Enforcement)
○ 在框架层 (Framework): 由ActivityManagerService
(AMS) 和PackageManagerService
(PMS) 负责。当应用调用一个受权限保护的 API(如getDeviceId()
)时,API 内部会通过Context.checkPermission()
等方法检查调用者是否拥有相应权限
○ 在原生层/Native (Binder IPC): 许多系统服务(如BluetoothService
,Vold
)运行在 Native 层。它们通过 Binder IPC 接收请求。它们会使用IPCThreadState::getCallingUid()
获取调用者的 UID,然后向 PMS 查询该 UID 对应的包是否拥有所需权限
○ 在内核层 (Kernel): 通过 SELinux (Security-Enhanced Linux) 和传统的文件系统权限(DAC)进行最终的控制。即使应用通过了框架层的检查,如果 SELinux 策略不允许该进程访问某个内核资源,访问也会被拒绝
二. 权限申请流程全链路分析
让我们以应用申请 android.permission.CAMERA
这个危险权限为例,追踪其完整流程
阶段 1:应用层 (Java) - 发起请求
- 声明权限: 在
AndroidManifest.xml
中声明<uses-permission android:name="android.permission.CAMERA" />
运行时检查与请求:
// 1. 检查是否已有权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED) {// 2. 如果没有,向用户展示解释(可选)if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA)) {// 告诉用户为什么需要这个权限}// 3. 发起正式的权限请求弹窗ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA},MY_CAMERA_PERMISSION_REQUEST_CODE); } else {// 4. 已经有权限,直接操作相机openCamera(); }
处理回调:
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {if (requestCode == MY_CAMERA_PERMISSION_REQUEST_CODE) {if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {openCamera(); // 用户同意,执行操作} else {// 用户拒绝}} }
阶段 2:框架层 (Framework) - 处理请求与持久化
requestPermissions()
调用路径:
○ Activity.requestPermissions() -> ActivityCompat.requestPermissions()
○ 最终通过 Binder IPC 调用到ActivityManagerService
的grantRuntimePermission
相关方法- 权限弹窗: AMS 会协调创建一个系统的权限请求对话框(UI 属于
Settings
应用或PermissionController
组件),以当前应用的上下文显示,但完全由系统控制,应用无法干预 - 用户决策: 用户点击“允许”或“拒绝”
- 持久化授权结果:
○ 用户的决定由PermissionController
通过PackageManagerService
的grantRuntimePermission()
或revokeRuntimePermission()
方法进行记录
○ PMS 是权限状态的最终权威。它将授权结果写入到/data/system/users/<user-id>/runtime-permissions.xml
文件中。这样即使设备重启,权限状态也会保留
○ PMS 还会更新其内存中的数据结构,以便快速查询
阶段 3:权限执行 (Enforcement) - 当应用使用相机时
- 应用调用 API: 应用调用
Camera.open()
- 框架层检查:
CameraService
(一个系统服务) 会收到打开摄像头的请求。在它的 Binder 接口方法中,它会执行安全检查:// 这是一个概念性代码,类似于CameraService内部的逻辑 public class CameraService extends ICameraService.Stub {@Overridepublic ICamera connect(ICameraClient client, ...) {// 获取调用者的 UID 和 PIDint callingPid = Binder.getCallingPid();int callingUid = Binder.getCallingUid();// 关键检查:调用者是否有Camera权限?if (mContext.checkPermission(android.Manifest.permission.CAMERA, callingPid, callingUid)!= PackageManager.PERMISSION_GRANTED) {// 如果没有权限,抛出SecurityException异常throw new SecurityException("Required CAMERA permission not granted for uid=" + callingUid);}// ... 继续打开相机的逻辑} }
○ PMS 查阅其内存中的权限数据库(从runtime-permissions.xml
加载而来),并返回结果 - Native 层检查 (可选): 有些服务(如
vold
存储服务)直接运行在 Native 层。它们的检查流程类似:
○IPCThreadState::getCallingUid()
获取调用者 UID
○ 通过 Binder 调用回 PMS 的 Native 接口IPackageManagerNative
或与上层 PMS 的 Java 服务通信,来检查权限
阶段 4:内核层 (Kernel) - 最终防线
即使应用通过了所有框架层的权限检查,当它尝试真正操作硬件设备(如 /dev/video0
摄像头设备节点)时,还会受到内核的最后一道关卡检查
- 文件系统权限 (DAC):
○ 设备节点/dev/video0
有其所属用户和组(如root:camera
)
○ 它的权限可能是crw-rw----
,意味着所有者(root)和组员(camera)有读写权限,其他用户无权限
○ 应用进程的 UID 是app_123
,它不属于camera
组,按理说无法访问。但为什么有权限的应用能访问?因为在安装时授予CAMERA
权限后,PMS 会将应用的 UID 加入到camera
组中(在/data/system/packages.list
中记录)。这样,应用进程就具备了访问设备节点的组权限 - SELinux (MAC):
○ DAC 的粒度较粗。SELinux 提供了更细粒度的强制访问控制
○ 每个进程都有一个domain
(域),每个文件/设备都有一个type
(类型)
○ SELinux 策略规则定义了哪个domain
可以对哪个type
执行哪些操作(如read
,write
,open
)
○ 示例策略:# 允许 untrusted_app 域(普通应用)对 camera_device 类型执行 open 操作。 allow untrusted_app camera_device:chr_file open;
○ 如果策略中没有明确允许的规则,即使 DAC 允许,访问也会被
avc: denied
并拒绝。这是 Android 安全的重要基石
总结
Android 的权限管理是一个贯穿整个系统栈的深度防御体系:
层级 | 角色 | 关键技术 |
---|---|---|
应用层 | 申请 | checkSelfPermission() , requestPermissions() |
框架层 | 管理、授权、执行 | PackageManagerService , ActivityManagerService , Binder IPC |
Native层 | 执行 | IPCThreadState::getCallingUid() , 与 PMS 通信 |
内核层 | 最终执行 | Linux UID/GID (DAC), SELinux (MAC) |