第 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 核心类

源码路径:

类名 源码路径
UserManagerService frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java
UserTypeFactory frameworks/base/services/core/java/com/android/server/pm/UserTypeFactory.java
UserTypeDetails frameworks/base/services/core/java/com/android/server/pm/UserTypeDetails.java
UserDataPreparer frameworks/base/services/core/java/com/android/server/pm/UserDataPreparer.java
UserVisibilityMediator frameworks/base/services/core/java/com/android/server/pm/UserVisibilityMediator.java
UserSystemPackageInstaller frameworks/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 id="0" />
    <user id="10" />
    <user id="11" />
</users>

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

<user
    id="10"
    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_SYSTEM FLAG_SYSTEM | FLAG_FULL 1 主用户、管理员、主账号
USER_TYPE_FULL_SECONDARY FLAG_FULL 配置值 标准次要用户
USER_TYPE_FULL_GUEST FLAG_FULL 1 可临时(销毁式)
USER_TYPE_FULL_DEMO FLAG_FULL 3 演示 /kiosk 模式
USER_TYPE_FULL_RESTRICTED FLAG_FULL 配置值 受限用户
USER_TYPE_PROFILE_MANAGED FLAG_PROFILE 配置值 工作配置文件
USER_TYPE_PROFILE_CLONE FLAG_PROFILE 每个父用户最多 1 应用克隆
USER_TYPE_PROFILE_PRIVATE FLAG_PROFILE 1 隐私空间
USER_TYPE_PROFILE_COMMUNAL FLAG_PROFILE 1 共享模式
USER_TYPE_PROFILE_SUPERVISING FLAG_PROFILE 1 受监管用户
USER_TYPE_PROFILE_TEST FLAG_PROFILE 4(每个父用户最多 2) 仅调试,用于自动化测试
USER_TYPE_SYSTEM_HEADLESS FLAG_SYSTEM 1 无界面系统用户模式

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)
代码转载自:https://pan.quark.cn/s/133311188eb6 ### C# DllImport功能说明及路径选取问题分析 #### 一、DllImport核心原理 `DllImport`是.NET Framework内的一种技术,用于执行平台调用服务(Platform Invoke, 简称P/Invoke),该机制使得.NET应用程序能够调用非托管代码中的函数,例如Windows API或其他非托管库中的函数。这对于增强.NET应用程序的功能性非常关键,因为许多高级系统级操作(例如文件操作、进程控制等)通常由非托管库负责实现。 `DllImport`特性包含在`System.Runtime.InteropServices`命名空间中,它的主要功能是向CLR(Common Language Runtime)指示如何定位并调用非托管库中的特定函数。 #### 二、DllImport特性包含的主要元素 `DllImport`特性所包含的主要元素有: - **DllName**:必需的字符串参数,用于表明需要导入的非托管库的名称。 - **CallingConvention**:可选参数,用于设定调用协议。在默认情况下,其值为`CallingConvention.Cdecl`。 - **CharSet**:可选参数,用于定义字符集的类型。在默认情况下,其值为`CharSet.Auto`,即根据函数的签名自动决定字符集。 - **EntryPoint**:可选参数,用于指定非托管库中的函数名称。若未提供,则默认使用应用程序的方法名称作为函数名称。 - **ExactSpelling**:可选布尔值,用于确定函数名称是否必须非托管库中的完全一致。...
代码下载链接: https://pan.quark.cn/s/8df2b016201b 555 芯片的引脚布局、功能特性、引脚示意图以及引脚说明是关键信息。555 芯片作为一种集成电路,具有多样化的功能特性,在定时器、定时延时控制、调光、调温、调压、调速等多种控制及计量检测领域有着广泛的应用。接下来将展示 555 芯片的引脚示意图和引脚说明: 1. 555 芯片引脚示意图:555 芯片包含 8 个引脚,具体如下: * 1 脚:地线端 * 2 脚:触发输入端 * 3 脚:输出端 * 4 脚:复位端 * 5 脚:控制端 * 6 脚:阈值端 * 7 脚:放电端 * 8 脚:电源端 2. 555 芯片引脚说明: * 1 脚:地线端,用于连接电路的负极部分。 * 2 脚:触发输入端,用于接收外部信号的输入,进而控制输出端的状态。 * 3 脚:输出端,输出高电平或低电平信号,其状态受触发器控制。 * 4 脚:复位端,当输入低电平时,输出端会输出低电平信号。 * 5 脚:控制端,用于调节输出端的状态,能够改变上下触发电平的数值。 * 6 脚:阈值端,作为上比较器的输入端,当输入高电平时,输出端会输出低电平信号。 * 7 脚:放电端,是内部放电管的输出端,其输出电平状态受触发器控制。 * 8 脚:电源端,用于连接电源的正极部分。 3. 555 芯片工作原理:555 芯片的工作原理是通过上比较器和下比较器来控制输出端的状态。上比较器的输入端位于 6 脚,而下比较器的输入端位于 2 脚。根据输入端的电平状态,输出端会输出高电平或低电平信号。 4. 555 芯片应用领域:555 芯片在各种电子产品中有着广泛的应用,例如在定时器、定时延时控制、调光、调温、调压、调速等领域。它还可以用于...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值