news 2026/8/22 16:44:04

第 31 章:多用户与用户配置文件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第 31 章:多用户与用户配置文件

Android 是多用户操作系统。设备开机那一刻,用户身份(user 0,系统用户)就处于激活状态,整个框架基于用户边界实现数据、进程、权限隔离。这套多用户能力不仅支撑设置中的 “用户” 页面,还支持工作配置文件、隐私空间、访客账户、受限配置文件、克隆配置文件。

本章基于真实 AOSP 源码梳理多用户架构,涵盖system_server中的UserManagerService、用户类型定义、生命周期管理、存储布局、配置文件隔离以及用户切换机制。

31.1 多用户架构

31.1.1 设计基础

Android 多用户模型构建在 Linux 基于 UID 的进程隔离之上:

概念Linux 底层基础Android 扩展实现
用户隔离UID 区间每个 Android 用户分配 100 000 大小的 UID 区间
进程隔离进程命名空间应用运行 uid = userId * 100000 + appId
文件隔离文件所有权用户私有数据目录:/data/user/<userId>/
存储加密dm‑crypt按用户划分 CE/DE(凭证加密 / 设备加密)存储

对于存在 user 0、user 10 的设备,appId 为 10045 的应用运行 UID 计算:

  • User 0:UID 10045
  • User 10:UID 1010045

该计算逻辑编码在UserHandle

public static final int PER_USER_RANGE = 100000; public static int getUid(int userId, int appId) { return userId * PER_USER_RANGE + (appId % PER_USER_RANGE); }

31.1.2 核心类

源码路径:

类名源码路径
UserManagerServiceframeworks/base/services/core/java/com/android/server/pm/UserManagerService.java
UserTypeFactoryframeworks/base/services/core/java/com/android/server/pm/UserTypeFactory.java
UserTypeDetailsframeworks/base/services/core/java/com/android/server/pm/UserTypeDetails.java
UserDataPreparerframeworks/base/services/core/java/com/android/server/pm/UserDataPreparer.java
UserVisibilityMediatorframeworks/base/services/core/java/com/android/server/pm/UserVisibilityMediator.java
UserSystemPackageInstallerframeworks/base/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java

31.1.3 UserManagerService 初始化

UserManagerService是所有用户操作的核心管理服务,实现IUserManager.Stub,多把锁保护关键状态:

// 摘自 UserManagerService.java public class UserManagerService extends IUserManager.Stub { // 锁顺序:先 mPackagesLock,后 mUsersLock private final Object mPackagesLock; private final Object mUsersLock = LockGuard.installNewLock(LockGuard.INDEX_USER); private final Object mRestrictionsLock = NamedLock.create("mRestrictionsLock"); private final Object mAppRestrictionsLock = NamedLock.create("mAppRestrictionsLock"); @GuardedBy("mUsersLock") private final SparseArray<UserData> mUsers; private final ArrayMap<String, UserTypeDetails> mUserTypes; }

类中记录锁调用命名约定:

方法命名约定:

  • 后缀LAr:调用时需要持有mAppRestrictionsLock
  • 后缀LP:调用时需要持有mPackagesLock
  • 后缀LR:调用时需要持有mRestrictionsLock
  • 后缀LU:调用时需要持有mUsersLock
Method naming convention: - Methods suffixed with "LAr" should be called within the `mAppRestrictionsLock` lock. - Methods suffixed with "LP" should be called within the `mPackagesLock` lock. - Methods suffixed with "LR" should be called within the `mRestrictionsLock` lock. - Methods suffixed with "LU" should be called within the `mUsersLock` lock.

31.1.4 持久化存储

用户元数据磁盘存放路径:/data/system/users/

/data/system/users/ userlist.xml # 全部用户列表与下一个序列号 user.list # 性能优化版用户列表 0/ # 系统用户 user 0 0.xml # user0的UserInfo photo.png # 用户头像 res_*.xml # 用户限制配置 10/ # 次要用户 user10 10.xml 11/ # 工作配置文件 user11 11.xml

userlist.xml示例:

<users nextSerialNumber="15" version="11"> <user /> <user /> <user /> </users>

单个用户 XML 文件(<id>.xml)内容示例:

<user serialNumber="12" flags="0x00000810" type="android.os.usertype.full.SECONDARY" created="1700000000000" lastLoggedIn="1700100000000" lastEnteredForeground="1700100000000" lastLoggedInFingerprint="google/pixel/..." > <name>John</name> <restrictions /> <userProperties /> </user>

文件属性直接对应UserManagerService常量:

// 摘自 UserManagerService.java private static final String ATTR_FLAGS = "flags"; private static final String ATTR_TYPE = "type"; private static final String ATTR_ID = "id"; private static final String ATTR_CREATION_TIME = "created"; private static final String ATTR_LAST_LOGGED_IN_TIME = "lastLoggedIn"; private static final String ATTR_LAST_ENTERED_FOREGROUND_TIME = "lastEnteredForeground"; private static final String ATTR_SERIAL_NO = "serialNumber"; private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber"; private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId"; private static final String ATTR_RESTRICTED_PROFILE_PARENT_ID = "restrictedProfileParentId";

31.1.5 用户 ID 分配

用户 ID 从MIN_USER_ID(10) 开始顺序分配:

// 摘自 UserManagerService.java @VisibleForTesting static final int MIN_USER_ID = UserHandle.MIN_SECONDARY_USER_ID; // 10 @VisibleForTesting static final int MAX_USER_ID = UserHandle.MAX_SECONDARY_USER_ID; // Integer.MAX_VALUE / 100000
  • user 0 永远是系统用户;ID 1‑9 保留预留。
  • 新建用户分配下一个可用 ID。
  • 序列号单调递增,即使用户被删除也不会复用,用于保证数据溯源。

用户 ID 与序列号相互独立: userID=10,serialNumber 可以是 12;user10 删除后,新用户可以复用 ID=10,但序列号会分配为 15。

系统维护最近已删除 ID 列表,避免刚删除的 ID 被立刻复用。

@VisibleForTesting static final int MAX_RECENTLY_REMOVED_IDS_SIZE = 100;

31.1.6 用户限制

用户限制由多来源叠加:

合并逻辑在updateUserRestrictionsInternalLR,输出生效用户限制,供系统服务执行。

服务将生效限制与基础限制分开缓存:

// 摘自 UserManagerService.java @GuardedBy("mRestrictionsLock") private final RestrictionsSet mBaseUserRestrictions = new RestrictionsSet(); @GuardedBy("mRestrictionsLock") private final RestrictionsSet mCachedEffectiveUserRestrictions = new RestrictionsSet();

重要:修改限制时,总是新建 Bundle 对象,不能直接修改已有 Bundle 实例,因为 Bundle 会在基础限制集合与缓存生效集合之间共享。

31.2 用户类型

31.2.1 UserTypeDetails 机制

Android 使用基于类型的体系定义用户分类;每种类型由UserTypeDetails定义,通过UserTypeFactory注册。

源码:frameworks/base/services/core/java/com/android/server/pm/UserTypeFactory.java

// 摘自 UserTypeFactory.java public static ArrayMap<String, UserTypeDetails> getUserTypes() { final ArrayMap<String, UserTypeDetails.Builder> builders = getDefaultBuilders(); // OEM可通过 config_user_types XML自定义 try (XmlResourceParser parser = Resources.getSystem().getXml(R.xml.config_user_types)) { customizeBuilders(builders, parser); } // 构建全部用户类型 final ArrayMap<String, UserTypeDetails> types = new ArrayMap<>(builders.size()); for (int i = 0; i < builders.size(); i++) { types.put(builders.keyAt(i), builders.valueAt(i).createUserTypeDetails()); } return types; }

OEM 厂商可提供res/xml/config_user_types.xml覆盖默认配置,修改最大允许数量、默认限制、属性等。

31.2.2 AOSP 内置用户类型清单

工厂注册的默认类型:

 

源码详细类型规格:

 

类型常量基础标志最大数量是否需要父用户关键属性
USER_TYPE_FULL_SYSTEMFLAG_SYSTEM | FLAG_FULL1主用户、管理员、主账号
USER_TYPE_FULL_SECONDARYFLAG_FULL配置值标准次要用户
USER_TYPE_FULL_GUESTFLAG_FULL1可临时(销毁式)
USER_TYPE_FULL_DEMOFLAG_FULL3演示 /kiosk 模式
USER_TYPE_FULL_RESTRICTEDFLAG_FULL配置值受限用户
USER_TYPE_PROFILE_MANAGEDFLAG_PROFILE配置值工作配置文件
USER_TYPE_PROFILE_CLONEFLAG_PROFILE每个父用户最多 1应用克隆
USER_TYPE_PROFILE_PRIVATEFLAG_PROFILE1隐私空间
USER_TYPE_PROFILE_COMMUNALFLAG_PROFILE1共享模式
USER_TYPE_PROFILE_SUPERVISINGFLAG_PROFILE1受监管用户
USER_TYPE_PROFILE_TESTFLAG_PROFILE4(每个父用户最多 2)仅调试,用于自动化测试
USER_TYPE_SYSTEM_HEADLESSFLAG_SYSTEM1无界面系统用户模式

USER_TYPE_PROFILE_TEST仅在Build.IS_DEBUGGABLE调试版本注册,复用 managed profile 角标,只用于自动化测试。上表为getDefaultBuilders()注册的默认配置。

31.2.3 完整系统用户

系统用户(user 0)具备特殊性,传统非 headless 模式:

// 摘自 UserTypeFactory.java private static UserTypeDetails.Builder getDefaultTypeFullSystem() { return new UserTypeDetails.Builder() .setName(USER_TYPE_FULL_SYSTEM) .setBaseType(FLAG_SYSTEM | FLAG_FULL) .setDefaultUserInfoPropertyFlags(FLAG_PRIMARY | FLAG_ADMIN | FLAG_MAIN) .setMaxAllowed(1) .setDefaultRestrictions(getDefaultSystemUserRestrictions()); }
  • ID 固定为 0,永久存在,不可删除;开机第一个启动。
  • Headless System User 模式(HSUM,车载设备使用):user0 后台运行,对用户不可见,实际操作用户在之上启动。

31.2.4 次要用户

标准次要用户是可切换完整用户:

private static UserTypeDetails.Builder getDefaultTypeFullSecondary() { return new UserTypeDetails.Builder() .setName(USER_TYPE_FULL_SECONDARY) .setBaseType(FLAG_FULL) .setMaxAllowed(getDefaultMaxAllowedForSwitchableTypes()) .setDefaultRestrictions(getDefaultSecondaryUserRestrictions()); }

getDefaultMaxAllowedForSwitchableTypes()返回getMaxSwitchableUsers()‑1,该上限绑定整机可切换用户配额;克隆、受限配置文件复用该工具函数。

次要用户默认限制:

  • DISALLOW_OUTGOING_CALLS = true禁止外呼
  • DISALLOW_SMS = true禁止短信 管理员用户可以解除以上限制。

31.2.5 访客用户

访客用户用于设备临时共享场景:

private static UserTypeDetails.Builder getDefaultTypeFullGuest() { final boolean ephemeralGuests = Resources.getSystem() .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral); final int flags = FLAG_GUEST | (ephemeralGuests ? FLAG_EPHEMERAL : 0); return new UserTypeDetails.Builder() .setName(USER_TYPE_FULL_GUEST) .setBaseType(FLAG_FULL) .setDefaultUserInfoPropertyFlags(flags) .setEnabled(getMaxSwitchableUsers() > 1 ? 1 : 0) .setMaxAllowed(1) .setDefaultRestrictions(getDefaultGuestUserRestrictions()); }

访客关键特性:

  1. 同一时刻只允许一个访客
  2. 可设置为临时销毁:退出时清空数据,由config_guestUserEphemeral控制
  3. 继承次要用户全部限制,额外增加:DISALLOW_INSTALL_UNKNOWN_SOURCESDISALLOW_CONFIG_CREDENTIALS;根据android.multiuser.Flags.userRestrictionConfigWifiSharedPrivate()增加DISALLOW_CONFIG_WIFI_SHAREDDISALLOW_CONFIG_WIFI
  4. 单用户设备下自动禁用(setEnabled(getMaxSwitchableUsers() > 1 ? 1 : 0)

31.2.6 托管配置文件(工作配置文件 Work Profile)

工作配置文件是使用最广泛的配置文件类型,由 DPC 设备策略控制器管理:

private static UserTypeDetails.Builder getDefaultTypeProfileManaged() { return new UserTypeDetails.Builder() .setName(USER_TYPE_PROFILE_MANAGED) .setBaseType(FLAG_PROFILE) .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE) .setMaxAllowed(getMaxManagedProfiles()) .setMaxAllowedPerParent(getMaxManagedProfiles()) .setProfileParentRequired(true) // …角标、标签、颜色配置 .setDefaultUserProperties(new UserProperties.Builder() .setStartWithParent(true) .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE) .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE) .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED) .setShowInSharingSurfaces( UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE) .setCredentialShareableWithParent(true)); }

属性释义:

  • startWithParent=true:父用户启动时,配置文件自动启动
  • SHOW_IN_LAUNCHER_SEPARATE:工作应用在启动器带独立角标显示
  • SHOW_IN_QUIET_MODE_PAUSED:静默模式开启,应用显示为暂停状态
  • credentialShareableWithParent=true:可与父用户共用锁屏凭证

31.2.7 克隆配置文件(Clone Profile)

克隆配置文件允许同一应用运行第二个实例,多用于社交 / 消息类 App:

private static UserTypeDetails.Builder getDefaultTypeProfileClone() { return new UserTypeDetails.Builder() .setName(USER_TYPE_PROFILE_CLONE) .setBaseType(FLAG_PROFILE) .setMaxAllowedPerParent(1) .setProfileParentRequired(true) .setDefaultUserProperties(new UserProperties.Builder() .setStartWithParent(true) .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT) .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_WITH_PARENT) .setInheritDevicePolicy( UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT) .setUseParentsContacts(true) .setMediaSharedWithParent(true) .setCredentialShareableWithParent(true) .setDeleteAppWithParent(true)); }

克隆配置文件特有属性:

  • SHOW_IN_LAUNCHER_WITH_PARENT:和父用户应用并列展示,不使用独立分页
  • useParentsContacts=true:复用父用户联系人数据库
  • mediaSharedWithParent=true:媒体存储与父用户共享
  • deleteAppWithParent=true:父用户卸载应用时克隆版本同步卸载

31.2.8 受限配置文件(Restricted Profiles)

多用于平板设备,和父用户共享部分数据集,但访问权限受限:

private static UserTypeDetails.Builder getDefaultTypeFullRestricted() { return new UserTypeDetails.Builder() .setName(USER_TYPE_FULL_RESTRICTED) .setBaseType(FLAG_FULL) .setDefaultUserInfoPropertyFlags(FLAG_RESTRICTED)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/22 16:34:39

Zsh 命令还没回车就能看出对错:语法高亮的 5 分钟上手

Zsh 命令还没回车就能看出对错&#xff1a;语法高亮的 5 分钟上手 【免费下载链接】zsh-syntax-highlighting Fish shell like syntax highlighting for Zsh. 项目地址: https://gitcode.com/gh_mirrors/zs/zsh-syntax-highlighting 敲完 rm -rf build/ 准备回车&#x…

作者头像 李华
网站建设 2026/8/22 16:28:34

MemExplorer:面向智能体推理NPU的异构内存设计空间探索框架

1. 项目概述&#xff1a;当智能体遇上异构内存&#xff0c;一场效率革命正在发生最近和几个做NPU&#xff08;神经网络处理器&#xff09;芯片设计的朋友聊天&#xff0c;大家不约而同都在吐槽同一个问题&#xff1a;模型是越做越聪明&#xff0c;参数是越堆越多&#xff0c;但…

作者头像 李华
网站建设 2026/8/22 16:26:39

2. 第一现场:VS Code 运行时上下文

“这个函数为什么报错&#xff1f;”是 IDE 里最自然的一句话&#xff0c;也是最考验上下文的一句话。 人类同事看到你把光标放在 src/auth/token.ts:42&#xff0c;通常能立刻理解“这个函数”。模型没有眼睛&#xff0c;必须有人把现场翻译给它。 2.1 现场快照包含什么 src/e…

作者头像 李华
网站建设 2026/8/22 16:25:38

Calibre电子书格式转换与管理:从批量处理到自动化集成

如果你在本地电脑上管理着成百上千本电子书&#xff0c;一定会遇到格式不兼容的烦恼——Kindle 只认 MOBI/AZW3&#xff0c;手机阅读器偏爱 EPUB&#xff0c;而 PDF 又常常排版错乱。手动转换不仅效率低下&#xff0c;还常常丢失目录、封面甚至章节信息。 今天要介绍的 Calib…

作者头像 李华
网站建设 2026/8/22 16:24:57

学 Simulink—— 爪极发电机在汽车怠速工况下的稳压控制仿真

目录 手把手教你学 Simulink —— 爪极发电机在汽车怠速工况下的稳压控制仿真(落地版) 一、为什么这一版要"重做" 二、系统结构(推荐搭建顺序) 三、Step ① 怠速转速模型(必须可复现) 四、Step ② 爪极发电机本体(MATLAB Function) 4.1 参数 4.2 反电…

作者头像 李华