访客用户
本文承接 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
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
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
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,符号:创建收尾
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
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 与初始化
@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
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
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
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 处理
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
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,符号:安装限制检查
if (isUserRestricted(userId,
UserManager.DISALLOW_INSTALL_APPS)) {
return Pair.create(
PackageManager.INSTALL_FAILED_USER_RESTRICTED,
intentSender);
}因此“Guest 不能安装应用”是否成立,取决于当前 effective restriction,而不是仅由 user.isGuest() 判断。系统资源、默认 Guest restrictions 和运行时管理员更新都可能改变结果。
9. 访客与普通用户
| 维度 | Guest | Secondary | Restricted |
|---|---|---|---|
| 基础类型 | FLAG_FULL | FLAG_FULL | FLAG_FULL |
| 默认数量 | 1 | 设备配置上限 | 设备配置上限 |
| guest flag | FLAG_GUEST | 无 | FLAG_RESTRICTED |
| ephemeral | 资源配置决定 | 通常持久 | 通常持久 |
| 动态默认限制 | mGuestRestrictions | type 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”。
