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

发布时间:2026/8/22 16:44:22
第 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 10045User 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.javaUserTypeFactoryframeworks/base/services/core/java/com/android/server/pm/UserTypeFactory.javaUserTypeDetailsframeworks/base/services/core/java/com/android/server/pm/UserTypeDetails.javaUserDataPreparerframeworks/base/services/core/java/com/android/server/pm/UserDataPreparer.javaUserVisibilityMediatorframeworks/base/services/core/java/com/android/server/pm/UserVisibilityMediator.javaUserSystemPackageInstallerframeworks/base/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java31.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 SparseArrayUserData mUsers; private final ArrayMapString, 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.xmluserlist.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/..." nameJohn/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 / 100000user 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 ArrayMapString, UserTypeDetails getUserTypes() { final ArrayMapString, UserTypeDetails.Builder builders = getDefaultBuilders(); // OEM可通过 config_user_types XML自定义 try (XmlResourceParser parser = Resources.getSystem().getXml(R.xml.config_user_types)) { customizeBuilders(builders, parser); } // 构建全部用户类型 final ArrayMapString, 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()); }访客关键特性:同一时刻只允许一个访客可设置为临时销毁:退出时清空数据,由config_guestUserEphemeral控制继承次要用户全部限制,额外增加:DISALLOW_INSTALL_UNKNOWN_SOURCES、DISALLOW_CONFIG_CREDENTIALS;根据android.multiuser.Flags.userRestrictionConfigWifiSharedPrivate()增加DISALLOW_CONFIG_WIFI_SHARED或DISALLOW_CONFIG_WIFI单用户设备下自动禁用(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)