系列目录 :第一篇:功能演进与系统架构总览 | 第二篇:核心数据结构深度剖析 | 第三篇:用户创建与删除流程全链路分析 | 第四篇:用户切换机制源码剖析 | 第五篇:应用安装与管理隔离机制 | 第六篇:数据隔离与存储机制详解 | 第七篇:系统服务的多用户感知处理 | [第八篇:广播分发与 Intent 路由的用户隔离](#第八篇:广播分发与 Intent 路由的用户隔离) | 第九篇:权限系统与多用户的交互机制 | [第十篇:Work Profile 企业模式与开发实战指南](#第十篇:Work Profile 企业模式与开发实战指南)
一、为什么要解析 AOSP 7 的多用户源码
做 Android 定制或企业级开发时,多用户问题往往绕不开:
- 为什么同一个 App 在主用户和设备第二个用户下数据完全隔离,但
/data/data下看到的目录却不一样? - 为什么
UserManager创建的访客用户,退出后数据会被清空,而普通用户不会? - 为什么跨用户启动 Activity 必须显式传
UserHandle,不传就落到调用者自己的用户空间?
AOSP 7(Nougat,代号 N)是多用户功能成熟到稳定的关键版本:手机端的完整多用户、Work Profile(企业模式)、Guest 模式都在这之前成型。本系列以 AOSP 7 源码为基准,逐层拆解多用户的数据模型、生命周期、隔离机制与系统服务协同。本篇先建立全局认知。
二、多用户功能演进历程
| 版本 | 里程碑 |
|---|---|
| Android 4.2(Jelly Bean MR1) | 平板端确立多用户框架,UserManagerService 与访客模式成型 |
| Android 5.0(Lollipop) | 手机端支持多用户(OEM 普遍未开启,由 config_multiuser 控制) |
| Android 6.0(Marshmallow) | 运行时权限按用户维度授予,跨用户调用安全收紧 |
| Android 7.0(Nougat) | Work Profile 完善、多用户稳定性成熟,API 定型 |
关键设计 :AOSP 7 中多用户是否启用由资源
config_multiuser(MU_ENABLED)控制,UserHandle中硬编码MU_ENABLED = true;OEM 通过修改资源决定是否真正启用整套机制。这解释了为什么同样基于 AOSP 7,有的设备能创建第二个用户,有的不能。
三、多用户的四种用户类型
AOSP 7 中用户通过 UserInfo.flags 位掩码区分类型,实际共四类:主用户、普通用户、访客用户、工作配置(Work Profile)。
源码路径 :frameworks/base/core/java/android/content/pm/UserInfo.java
java
public class UserInfo implements Parcelable {
public static final int FLAG_MASK_USER_TYPE = 0x0000FFFF;
public static final int FLAG_PRIMARY = 0x00000001; // 主用户
public static final int FLAG_ADMIN = 0x00000002; // 管理员
public static final int FLAG_GUEST = 0x00000004; // 访客用户
public static final int FLAG_RESTRICTED = 0x00000008; // 受限模式(儿童锁场景)
public static final int FLAG_INITIALIZED = 0x00000010;
public static final int FLAG_MANAGED_PROFILE = 0x00000020; // 工作配置
public static final int FLAG_DISABLED = 0x00000040;
public static final int FLAG_QUIET_MODE = 0x00000080;
public static final int FLAG_EPHEMERAL = 0x00000100; // 临时用户
public static final int FLAG_DEMO = 0x00000200;
public int id;
public String name;
public int flags; // 用户类型由该字段的位掩码决定
// ...
public boolean isGuest() {
return (flags & FLAG_GUEST) == FLAG_GUEST;
}
}
关键设计 :用户类型不是独立枚举,而是
UserInfo.flags的位组合------同一个用户可同时是"普通用户 + 受限模式"。类型判断都是位运算(isGuest()等),新增类型只需加一个位,不破坏已有持久化数据。
各类型语义:
| 类型 | 标志 | 语义 |
|---|---|---|
| 主用户(Primary) | FLAG_PRIMARY |
ID 恒为 0,设备首启创建,不可删除 |
| 普通用户(Secondary) | 无特殊标志 | 由主用户创建,独立应用与数据 |
| 访客用户(Guest) | FLAG_GUEST |
临时用户,退出后数据清除 |
| 工作配置(Work Profile) | FLAG_MANAGED_PROFILE |
企业场景,与宿主用户共享部分资源但数据隔离 |
注意 :原系列初稿把
FLAG_MANAGED_PROFILE的值写成0x00000008,AOSP 7 实际是0x00000020;且这些常量定义在UserInfo而非UserManager。
四、系统架构总览
4.1 核心组件关系
应用进程
│ UserManager(客户端 API,framework.jar)
▼
system_server
├── UserManagerService 用户生命周期(创建/删除/切换/属性)
│ ├── PackageManagerService 应用安装与按用户启用
│ └── ActivityManagerService 运行时进程按 userId 划分
│ └── UserController 用户切换的运行时状态机
└── StorageManagerService 按用户挂载外部存储
多用户涉及多个系统服务协同:UserManagerService 管理用户生命周期,PackageManagerService 管理应用在各用户下的安装与启用,ActivityManagerService 通过 UserController 完成运行时切换,StorageManagerService 负责外部存储按用户挂载。
4.2 关键源码文件清单
| 职责 | 源码路径 |
|---|---|
| 用户标识 | frameworks/base/core/java/android/os/UserHandle.java |
| 用户管理客户端 API | frameworks/base/core/java/android/os/UserManager.java |
| 用户管理服务 | frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java |
| 用户信息数据类 | frameworks/base/core/java/android/content/pm/UserInfo.java |
| 运行时切换状态机 | frameworks/base/services/core/java/com/android/server/am/UserController.java |
| 包管理(多用户感知) | frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java |
| 存储挂载 | frameworks/base/services/core/java/com/android/server/StorageManagerService.java |
关键设计 :
UserController位于com.android.server.am包(AM 侧),而非 PM 侧------切换用户是运行时行为,涉及进程、窗口、广播的批量处置,归 AM 管理合理;而用户的"出生与死亡"归UserManagerService。
4.3 文件系统目录结构
/data/system/users/
├── list 用户 ID 列表
├── 0.xml 主用户配置
├── 10.xml 用户 10 配置
└── 10.preferredapps 用户 10 的默认应用选择
/data/user/
├── 0/ 主用户应用数据
├── 0_ce/ 主用户加密数据(CE)
├── 10/ 用户 10 应用数据
└── 10_de/ 用户 10 不可加密数据(DE)
关键设计 :AOSP 7 引入了 FBE(文件级加密)的 CE/DE 分离------
/data/user/<id>_ce仅在用户解锁后可访问,_de设备开机即可访问。这是 AOSP 7 相对 6.0 的重要变化,也决定了后续版本"用户解锁后 App 才能启动"的默认行为。
4.4 用户 ID 与 UID 映射
源码路径 :frameworks/base/core/java/android/os/UserHandle.java
java
public class UserHandle implements Parcelable {
public static final int PER_USER_RANGE = 100000;
public static final boolean MU_ENABLED = true;
public static @UserIdInt int getUserId(int uid) {
if (MU_ENABLED) {
return uid / PER_USER_RANGE;
} else {
return UserHandle.USER_SYSTEM;
}
}
public static @AppIdInt int getAppId(int uid) {
return uid % PER_USER_RANGE;
}
public static int getUid(@UserIdInt int userId, @AppIdInt int appId) {
if (MU_ENABLED) {
return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
} else {
return appId;
}
}
}
关键设计 :
UID = userId * 100000 + appId,每个用户独占 10 万 UID 区间。Linux 内核层面 UID 唯一,uid = 112345天然属于用户 1 的 appId 12345 应用------文件系统权限、进程归属、IPC 检查都能直接由 UID 推导出用户,不需要任何额外表查询。
五、源码阅读路线图
| 阶段 | 内容 | 对应篇目 |
|---|---|---|
| 数据模型 | UserInfo、UserHandle、/data/system/users/*.xml |
第二篇 |
| 生命周期 | createUser / removeUser / switchUser 全链路 |
第三、四篇 |
| 隔离机制 | PMS 安装隔离、/data/user/<id> 存储隔离 |
第五、六篇 |
| 服务协同 | AM 用户感知、广播按用户分发、权限跨用户检查 | 第七、八、九篇 |
| 高级特性 | Work Profile、DevicePolicyManager |
第十篇 |
六、本篇总结
本篇建立了三个基础认知:
- 四种用户类型 :主用户、普通用户、访客、工作配置,由
UserInfo.flags位掩码区分; - 组件分工 :
UserManagerService管生命周期,UserController(AM 侧)管运行时切换,PackageManagerService管应用按用户安装; - UID 映射 :
userId * 100000 + appId是整套隔离机制的底层基石。
下一篇进入核心数据结构,逐字段剖析 UserInfo、UserHandle 与用户配置的 XML 持久化格式。