Skip to content

访客用户

追踪 Android 17 访客用户的类型配置、ephemeral 标记、默认限制、延迟删除和 PMS 清理边界。

基于android-17.0.0_r1
AndroidPMSUserManagerServiceMultiUserGuest

访客用户 ​

本文承接 UMS 与 PMS、受限用户 和 用户空间隔离,专门解释 Android 17 的 Guest user。重点不是“访客界面长什么样”,而是源码如何区分 Guest、ephemeral user 和 guest-to-remove 状态,以及这些状态何时让 PMS 清理包状态和数据。

访客是 USER_TYPE_FULL_GUEST,基础类型是 FLAG_FULL,不是 profile。它最多存在一个;是否默认 ephemeral 由资源配置决定。访客的限制来自 UserTypeFactory 的默认限制和 UserManagerService 的运行时 guest restrictions,两者不能混写。

1. Guest 类型 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/UserTypeFactory.java,符号:getDefaultTypeFullGuest

java
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());
}

Guest 是否 ephemeral 是类型默认 flag,不是所有 Guest API 调用都强制覆写的布尔参数;系统配置还决定设备是否启用 Guest 类型以及最多一个 Guest 的数量限制。

源码文件:frameworks/base/services/core/java/com/android/server/pm/UserTypeFactory.java,符号:getDefaultGuestUserRestrictions

java
private static Bundle getDefaultGuestUserRestrictions() {
    Bundle restrictions =
            getDefaultSecondaryUserRestrictions();
    restrictions.putBoolean(
            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
            true);
    restrictions.putBoolean(
            UserManager.DISALLOW_CONFIG_CREDENTIALS,
            true);
    restrictions.putBoolean(
            UserManager.DISALLOW_WALLPAPER, true);
    restrictions.putBoolean(
            UserManager.DISALLOW_BLUETOOTH_SHARING, true);
    return restrictions;
}

Guest 继承 secondary user 的基础限制,再增加未知来源、凭据配置、壁纸和蓝牙共享等限制。这里是类型默认值;运行时 mGuestRestrictions 还可以由管理接口覆盖或追加。

2. Guest 创建 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:createUserInternalUncheckedNoTracing

java
final boolean isGuest =
        UserManager.isUserTypeGuest(userType);
final UserTypeDetails typeDetails =
        mUserTypes.get(userType);
if (typeDetails == null) {
    throwCheckedUserOperationException(
            "Cannot create user of invalid user type: "
                    + userType,
            USER_OPERATION_ERROR_UNKNOWN);
}

flags |= typeDetails.getDefaultUserInfoFlags();
if (!canAddMoreUsersOfType(typeDetails)) {
    throwCheckedUserOperationException(
            "Cannot add more users of type " + userType,
            USER_OPERATION_ERROR_MAX_USERS);
}

创建时 UMS 将类型默认 flag 合并到 UserInfo,并执行最多一个 Guest 的数量检查。Guest 没有 profileGroupId 约束;它是完整用户,数据目录和应用状态独立。

源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:创建收尾

java
if (isGuest) {
    synchronized (mGuestRestrictions) {
        restrictions.putAll(mGuestRestrictions);
    }
} else {
    typeDetails.addDefaultRestrictionsTo(restrictions);
}

synchronized (mRestrictionsLock) {
    mBaseUserRestrictions.updateRestrictions(
            userId, restrictions);
}

mPm.createNewUser(userId, installablePackages,
        disallowedPackages);

Guest 创建时使用单独的 mGuestRestrictions;其他用户类型通常从 UserTypeDetails 追加默认 restrictions。这是 Guest 动态默认限制与其他类型静态默认限制的实现差异。

3. Guest 状态查询 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:getGuestUsers

java
public List<UserInfo> getGuestUsers() {
    checkManageUsersPermission("getGuestUsers");
    ArrayList<UserInfo> guests = new ArrayList<>();
    synchronized (mUsersLock) {
        for (int i = 0; i < mUsers.size(); i++) {
            UserInfo user = mUsers.valueAt(i).info;
            if (user.isGuest()
                    && !user.guestToRemove
                    && !user.preCreated
                    && !mRemovingUserIds.get(user.id)) {
                guests.add(user);
            }
        }
    }
    return guests;
}

guestToRemove 的 Guest 不再出现在正常 Guest 列表,pre-created Guest 也不会被当作可供用户切换的 Guest。这个过滤影响 Settings/系统 UI 选择 Guest,也影响“是否已经存在 Guest”的判断。

4. Guest 默认限制 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:mGuestRestrictions 与初始化

java
@GuardedBy("mGuestRestrictions")
private final Bundle mGuestRestrictions =
        new Bundle();

@VisibleForTesting
void initDefaultGuestRestrictions() {
    synchronized (mGuestRestrictions) {
        if (mGuestRestrictions.isEmpty()) {
            UserTypeDetails guestType =
                    mUserTypes.get(
                            UserManager.USER_TYPE_FULL_GUEST);
            if (guestType != null) {
                guestType.addDefaultRestrictionsTo(
                        mGuestRestrictions);
            }
        }
    }
}

初始化只在当前没有持久化 Guest restrictions 时从类型定义加载;已有自定义限制不会被启动时默认值覆盖。

源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:setDefaultGuestRestrictions

java
public void setDefaultGuestRestrictions(
        Bundle restrictions) {
    checkManageUsersPermission(
            "setDefaultGuestRestrictions");
    synchronized (mGuestRestrictions) {
        mGuestRestrictions.clear();
        mGuestRestrictions.putAll(restrictions);
    }
    synchronized (mPackagesLock) {
        writeUserListLP();
    }

    for (UserInfo guest : getGuestUsers()) {
        synchronized (mRestrictionsLock) {
            Bundle current = mBaseUserRestrictions
                    .getRestrictions(guest.id);
            current.putAll(restrictions);
            updateUserRestrictionsInternalLR(
                    current, guest.id);
        }
    }
}

更新默认 Guest restrictions 会同步影响现有 Guest,不只是影响下一个新 Guest。调用者必须有 MANAGE_USERS;限制更新最终还会通过 updateUserRestrictionsInternalLR 应用到系统服务。

5. Guest 延迟删除 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:markGuestForDeletion

java
public boolean markGuestForDeletion(int userId) {
    checkManageUsersPermission(
            "Only the system can remove users");
    if (getUserRestrictions(
            UserHandle.getCallingUserId())
            .getBoolean(UserManager.DISALLOW_REMOVE_USER,
                    false)) {
        return false;
    }

    synchronized (mPackagesLock) {
        synchronized (mUsersLock) {
            UserData data = mUsers.get(userId);
            if (userId == 0 || data == null
                    || mRemovingUserIds.get(userId)) {
                return false;
            }
            if (!data.info.isGuest()) {
                return false;
            }
        }
        data.info.guestToRemove = true;
        addUserInfoFlags(data.info,
                UserInfo.FLAG_DISABLED);
        writeUserLP(data);
    }
    return true;
}

标记 Guest 删除不是立即销毁:它允许系统先创建/切换到另一个 Guest,再等待旧 Guest 停止后执行真正 remove。标记状态会持久化,若系统重启时仍未完成,启动清理逻辑会继续处理。

6. Ephemeral Guest ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:ephemeral flags

java
if (parent != null && parent.info.isEphemeral()) {
    flags |= UserInfo.FLAG_EPHEMERAL;
}

if (preCreate) {
    flags &= ~UserInfo.FLAG_EPHEMERAL;
}

if ((flags & UserInfo.FLAG_EPHEMERAL) != 0) {
    flags |= UserInfo.FLAG_EPHEMERAL_ON_CREATE;
}

FLAG_EPHEMERAL 是用户生命周期 flag,FLAG_EPHEMERAL_ON_CREATE 用于记录创建时的 ephemeral 来源。pre-created user 暂时清掉 ephemeral,避免 storage key 尚未持久化;转换为普通用户时再恢复需要的 flag。

源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:用户停止时的 Guest 处理

java
if (userInfo.isGuest()) {
    // The guest will be deleted after it stops.
    userInfo.guestToRemove = true;
}

在 ephemeral/切换场景中,Guest 可能在停止后自动标记删除。真正数据销毁仍要经过 UMS 的 stop callback、广播屏障、PMS cleanUpUser 和 UserDataPreparer.destroyUserData,不是设置 flag 的瞬间完成。

7. PMS 清理边界 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,符号:cleanUpUser

java
void cleanUpUser(UserManagerService userManager,
        int userId) {
    synchronized (mLock) {
        mDeletePackageHelper
                .removeUnusedPackagesLPw(userManager, userId);
        mSettings.removeUserLPw(userId);
        mAppsFilter.onUserDeleted(
                snapshotComputer(), userId);
        mPermissionManager.onUserRemoved(userId);
        mInstallerService.onUserRemoved(userId);
    }
    mInstantAppRegistry.onUserRemoved(userId);
    mPackageMonitorCallbackHelper.onUserRemoved(userId);
}

PMS 清理 Guest 的 per-user package state、权限、AppsFilter、InstallerService 和 instant app registry;用户目录和 storage key 由 UMS/StorageManager/installd 清理。Guest 的 ephemeral 语义不会让 PMS 单独删除全局 APK,除非该包已经对所有用户无用。

8. 限制与包操作 ​

Guest 的默认限制可能包含 DISALLOW_INSTALL_UNKNOWN_SOURCES、DISALLOW_CONFIG_CREDENTIALS、DISALLOW_OUTGOING_CALLS、DISALLOW_SMS 等。PMS 只消费包管理相关限制,例如 DISALLOW_INSTALL_APPS;电话、短信、位置、凭据配置由其他系统服务检查。

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,符号:安装限制检查

java
if (isUserRestricted(userId,
        UserManager.DISALLOW_INSTALL_APPS)) {
    return Pair.create(
            PackageManager.INSTALL_FAILED_USER_RESTRICTED,
            intentSender);
}

因此“Guest 不能安装应用”是否成立,取决于当前 effective restriction,而不是仅由 user.isGuest() 判断。系统资源、默认 Guest restrictions 和运行时管理员更新都可能改变结果。

9. 访客与普通用户 ​

维度GuestSecondaryRestricted
基础类型FLAG_FULLFLAG_FULLFLAG_FULL
默认数量1设备配置上限设备配置上限
guest flagFLAG_GUEST无FLAG_RESTRICTED
ephemeral资源配置决定通常持久通常持久
动态默认限制mGuestRestrictionstype defaults创建方法追加/类型策略
删除策略可延迟标记、停止后删除普通删除普通删除

Guest 的特殊性不在于它是一种 profile,而在于它是一个可选 ephemeral 的完整用户,并有专门的“标记待删”流程。

10. 失败定位 ​

现象检查点
无法创建 Guest类型 enabled、maxAllowed=1、DISALLOW_ADD_USER、磁盘空间
新 Guest 仍有旧限制mGuestRestrictions 初始化/持久化和 base restrictions 合并
第二个 Guest 无法创建旧 Guest 是否只是 guestToRemove,是否已被排除/完成删除
Guest 切换后数据仍在ephemeral flag、停止回调、cleanUpUser、destroyUserData
Guest 能安装不应安装的包DISALLOW_INSTALL_APPS effective 值、PMS 安装入口
Guest 列表仍显示待删用户getGuestUsers 的 guestToRemove、preCreated、removing 过滤

11. 阅读检查 ​

复述这条路径:UserTypeFactory 生成 Guest 类型 → UMS 创建并合并 mGuestRestrictions → PMS 初始化 per-user package state → Guest 使用期间可被 markGuestForDeletion 标记 → 停止/广播后 UMS 调 PMS cleanUpUser → 销毁 Guest 数据。

然后回答:Guest 一定 ephemeral 吗?guestToRemove=true 是否立即删除目录?Guest 默认限制是否只影响新建 Guest?答案分别是“取决于资源配置”“不是,需等待完整移除流程”“不是,setDefaultGuestRestrictions 会同步现有 Guest”。