工作资料
本文承接 UMS 与 PMS、per-user 包状态 和 多用户安装,聚焦 Managed Profile(工作资料)这条特殊用户路径。文章不把 DPC 的全部企业策略当作 PMS 实现,而是只解释源码能直接证明的内容:用户类型约束、profile group、PMS 包初始化、默认跨资料过滤器和 quiet mode 可用性通知。
工作资料不是独立的完整用户。它是一个带 FLAG_PROFILE/FLAG_MANAGED_PROFILE 的子用户,必须有父用户,并通过 profileGroupId 与父用户关联;它拥有自己的 per-user package state、UID 和数据目录,但跨资料 Intent 是否转发由显式过滤器和用户限制共同决定。
1. 类型与关系
源码文件:frameworks/base/services/core/java/com/android/server/pm/UserTypeDetails.java,符号:字段与访问器
private final @NonNull String mName;
private final int mMaxAllowed;
private final int mMaxAllowedPerParent;
private final @UserInfoFlag int mBaseType;
private final @UserInfoFlag int mDefaultUserInfoPropertyFlags;
private final @Nullable Bundle mDefaultRestrictions;
private final @Nullable List<DefaultCrossProfileIntentFilter>
mDefaultCrossProfileIntentFilters;
private final boolean mProfileParentRequired;
public int getDefaultUserInfoFlags() {
return mDefaultUserInfoPropertyFlags | mBaseType;
}
public boolean isProfile() {
return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
}UserTypeDetails 是用户类型的策略容器:最大数量、每个父用户的数量上限、默认 flags、默认 restrictions、默认跨资料过滤器以及是否必须有 parent。PMS 只接收最终的 installable package 集合和 userId;类型策略由 UMS 在创建前后消费。
源码文件:frameworks/base/services/core/java/com/android/server/pm/UserTypeFactory.java,符号:getDefaultTypeProfileManaged
private static UserTypeDetails.Builder
getDefaultTypeProfileManaged() {
return new UserTypeDetails.Builder()
.setName(USER_TYPE_PROFILE_MANAGED)
.setBaseType(UserInfo.FLAG_PROFILE)
.setDefaultUserInfoPropertyFlags(
UserInfo.FLAG_MANAGED_PROFILE)
.setMaxAllowed(getMaxManagedProfiles())
.setMaxAllowedPerParent(
getMaxManagedProfiles())
.setProfileParentRequired(true)
.setDefaultRestrictions(
getDefaultProfileRestrictions())
.setDefaultCrossProfileIntentFilters(
getDefaultManagedCrossProfileIntentFilter());
}工作资料默认是 profile、带 managed flag、必须指定父用户,并有全局和 per-parent 数量上限。默认 filter 列表不是“所有 Intent 都能跨资料”,而是一个有限策略集合。
2. 创建入口
源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:createProfileForUserWithThrow、createUserInternal
public UserInfo createProfileForUserWithThrow(
@Nullable String name,
@NonNull String userType,
@UserInfoFlag int flags,
@UserIdInt int parentId,
@Nullable String[] disallowedPackages)
throws ServiceSpecificException {
checkCreateUsersPermission(flags);
try {
return createUserInternal(name, userType,
flags, parentId, disallowedPackages);
} catch (UserManager.CheckedUserOperationException e) {
throw e.toServiceSpecificException();
}
}DPC 或系统调用的是 UMS 的 profile 创建入口,而不是直接调用 PMS。UMS 负责权限、restriction、user type 和 parent 校验;通过后才进入通用 createUserInternal。
String restriction = UserManager.DISALLOW_ADD_USER;
if (UserManager.isUserTypeManagedProfile(userType)) {
restriction = UserManager.DISALLOW_ADD_MANAGED_PROFILE;
}
enforceUserRestriction(restriction,
UserHandle.getCallingUserId(),
"Cannot add user");
return createUserInternalUnchecked(name, userType,
flags, parentId, false /* preCreate */,
disallowedPackages, null /* token */);工作资料有专门的 DISALLOW_ADD_MANAGED_PROFILE 限制,不等同于普通 DISALLOW_ADD_USER。这使设备策略可以单独阻止创建工作资料,同时仍允许其他用户操作。
3. 父用户关系
源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:创建用户时的 profile group 设置
if (isProfile) {
if (parent.info.profileGroupId
== UserInfo.NO_PROFILE_GROUP_ID) {
parent.info.profileGroupId = parent.info.id;
writeUserLP(parent);
}
userInfo.profileGroupId =
parent.info.profileGroupId;
} else if (isRestricted) {
if (parent.info.restrictedProfileParentId
== UserInfo.NO_PROFILE_GROUP_ID) {
parent.info.restrictedProfileParentId = parent.info.id;
writeUserLP(parent);
}
userInfo.restrictedProfileParentId =
parent.info.restrictedProfileParentId;
}第一次创建 profile 时,父用户可能还没有 group id,UMS 会把父用户自己的 id 作为 group 根;新工作资料继承同一个 profileGroupId。因此 profileGroupId 不是简单的 parentId 字段,但它让同一组 profile 能被权限、可见性和跨 profile 查询识别。
源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:canAddMoreProfilesToUser
if (!userInfo.isProfile()) {
return false;
}
if (!canAddMoreProfilesToUser(
newUserType, userInfo.profileGroupId)) {
return false;
}profile 数量限制既可以是设备级 maxAllowed,也可以是父用户级 maxAllowedPerParent。达到上限时不是 PMS 安装失败,而是 UMS 在创建 profile 阶段拒绝新用户。
4. PMS 包初始化
源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:创建流程中的 PMS 回调
final Set<String> installablePackages =
mSystemPackageInstaller
.getInstallablePackagesForUserType(
userType);
mPm.createNewUser(userId, installablePackages,
disallowedPackages);UMS 根据用户类型计算初始可安装包集合,再交给 PMS。工作资料的包是否初始 installed 由 Settings.createNewUserLI 按 system package、allowlist、denylist 和 hidden-until-installed 状态决定;DPC 后续安装应用则走单独的 per-user 安装路径。
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,符号:onNewUserCreated
void onNewUserCreated(int userId,
boolean convertedFromPreCreated) {
if (!convertedFromPreCreated
|| !readPermissionStateForUser(userId)) {
mPermissionManager.onUserCreated(userId);
mLegacyPermissionManager
.grantDefaultPermissions(userId);
mPermissionManager
.setDefaultPermissionGrantFingerprint(
Build.FINGERPRINT, userId);
mDomainVerificationManager.clearUser(userId);
mInstallerService.onUserAdded(userId);
}
}PMS 在包状态完成后初始化工作资料的权限和 InstallerService 状态。profile owner 的设置不是这个方法完成的,而是 DPM 在用户创建成功后继续执行;不要把“UMS 创建 profile”与“DPC 已成为 profile owner”混为同一事务。
5. 默认限制
源码文件:frameworks/base/services/core/java/com/android/server/pm/UserTypeFactory.java,符号:profile default restrictions
private static Bundle getDefaultProfileRestrictions() {
Bundle restrictions = new Bundle();
restrictions.putBoolean(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
true);
restrictions.putBoolean(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
true);
restrictions.putBoolean(
UserManager.DISALLOW_BLUETOOTH_SHARING,
true);
return restrictions;
}这些是创建时的默认 user restrictions,不等于 DPC 后续下发的全部企业策略。UMS 将默认 restrictions 合并到用户状态,再由系统服务和 API 检查;PMS 只在安装和跨用户操作时消费其中影响包管理的部分。
6. 默认跨资料过滤器
源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:setDefaultCrossProfileIntentFilters
private void setDefaultCrossProfileIntentFilters(
int profileUserId, UserTypeDetails details,
Bundle profileRestrictions, int parentUserId) {
if (details == null || !details.isProfile()) {
return;
}
List<DefaultCrossProfileIntentFilter> filters =
details.getDefaultCrossProfileIntentFilters();
boolean disallowSharingIntoProfile =
profileRestrictions.getBoolean(
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
false);
for (DefaultCrossProfileIntentFilter filter : filters) {
if (disallowSharingIntoProfile
&& filter.letsPersonalDataIntoProfile) {
continue;
}
if (filter.direction
== DefaultCrossProfileIntentFilter.Direction.TO_PARENT) {
mPm.addCrossProfileIntentFilter(
mPm.snapshotComputer(), filter.filter,
mContext.getOpPackageName(),
profileUserId, parentUserId, filter.flags);
} else {
mPm.addCrossProfileIntentFilter(
mPm.snapshotComputer(), filter.filter,
mContext.getOpPackageName(),
parentUserId, profileUserId, filter.flags);
}
}
}默认过滤器在 UMS 创建 profile 后写入 PMS 的 cross-profile resolver。DISALLOW_SHARE_INTO_MANAGED_PROFILE 会跳过“把个人数据送入工作资料”的过滤器,但不会把所有跨资料方向全部清空。
源码文件:frameworks/base/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java,符号:managed profile defaults
private static final DefaultCrossProfileIntentFilter ACTION_SEND =
new DefaultCrossProfileIntentFilter.Builder(
DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
0)
.addAction(Intent.ACTION_SEND)
.addAction(Intent.ACTION_SEND_MULTIPLE)
.addCategory(Intent.CATEGORY_DEFAULT)
.build();
public static List<DefaultCrossProfileIntentFilter>
getDefaultManagedProfileFilters() {
List<DefaultCrossProfileIntentFilter> filters =
new ArrayList<>();
filters.add(ACTION_SEND);
filters.addAll(
getDefaultCrossProfileTelephonyIntentFilters(false));
return filters;
}默认策略是具体 Intent action 的集合,例如发送、电话、媒体选择等;它不是一个“工作资料可访问个人全部应用”的总开关。最终解析还要经过 PMS 的 cross-profile filter、组件可见性和用户状态。
7. Profile group
源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:isSameProfileGroup
private static boolean isSameProfileGroup(
@NonNull UserInfo user1,
@NonNull UserInfo user2) {
return user1.id == user2.id
|| (user1.profileGroupId
!= UserInfo.NO_PROFILE_GROUP_ID
&& user1.profileGroupId
== user2.profileGroupId);
}同一 profile group 是许多跨资料权限和可见性判断的前提,但不是自动共享数据的授权。它说明两个用户属于同一个人/工作关系,具体 Intent、包可见性、权限和 quiet mode 仍由对应服务判断。
8. Quiet mode
源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:broadcastProfileAvailabilityChanges
private String getAvailabilityIntentAction(
boolean enableQuietMode,
boolean useManagedActions) {
return useManagedActions
? (enableQuietMode
? Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE
: Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
: (enableQuietMode
? Intent.ACTION_PROFILE_UNAVAILABLE
: Intent.ACTION_PROFILE_AVAILABLE);
}工作资料进入 quiet mode 后,UMS 通过 managed profile 专用 action 通知父用户;恢复时发送 available。quiet mode 改变的是 profile 可用性和相关广播,不等于删除 PackageUserState 或 APK。
if (profileInfo.isManagedProfile()) {
var dpmi = getDevicePolicyManagerInternal();
dpmi.broadcastIntentToManifestReceivers(
availabilityIntent, parentHandle,
true /* requiresPermission */);
}
availabilityIntent.addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(
availabilityIntent, parentHandle,
null, options);Managed profile 的 manifest receivers 由 DPM internal 以权限要求方式转发;动态 receiver 使用 parent user 的广播。两条发送路径的权限与 receiver 范围不同,不能只抓一条 log 判断 quiet mode 是否生效。
9. Profile 广播
源码文件:frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java,符号:sendProfileAddedBroadcast、sendProfileRemovedBroadcast
private void sendProfileAddedBroadcast(
int parentUserId, int addedUserId) {
sendProfileBroadcast(
new Intent(Intent.ACTION_PROFILE_ADDED),
parentUserId, addedUserId);
}
private void sendProfileRemovedBroadcast(
int parentUserId, int removedUserId,
String userType) {
if (Objects.equals(userType,
UserManager.USER_TYPE_PROFILE_MANAGED)) {
sendManagedProfileRemovedBroadcast(
parentUserId, removedUserId);
}
sendProfileBroadcast(
new Intent(Intent.ACTION_PROFILE_REMOVED),
parentUserId, removedUserId);
}所有 profile 都有通用 PROFILE_ADDED/REMOVED 动态广播;Managed Profile 额外有 MANAGED_PROFILE_REMOVED,并允许 manifest receiver 接收。这是因为通用 profile action 与 managed-specific action 的 receiver 范围不同。
10. 包安装与工作资料
工作资料创建时,PMS 通过 installablePackages 决定初始 system package 状态;DPC 后续可以调用 per-user 安装接口为 profile 启用已有包。无论哪条路径,APK code path 通常全局共享,但 PackageUserState、应用数据、权限和 Intent 可见性以 profile userId 为边界。
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,符号:addCrossProfileIntentFilter
public void addCrossProfileIntentFilter(
@NonNull Computer snapshot,
@NonNull IntentFilter intentFilter,
@NonNull String ownerPackage,
int sourceUserId, int targetUserId,
int flags) {
mCrossProfileIntentFilterHelper
.addCrossProfileIntentFilter(
snapshot, intentFilter, ownerPackage,
sourceUserId, targetUserId, flags);
}PMS 只保存和解析跨 profile filter;它不决定 DPC 是否拥有 profile owner 身份,也不自动授予所有工作权限。Intent 转发最终还会经过 source/target user 的包状态和 resolver 规则。
11. 失败与边界
| 现象 | 应检查 |
|---|---|
| 无法创建工作资料 | DISALLOW_ADD_MANAGED_PROFILE、type enabled、max per parent |
| profile 没有正确归属父用户 | profileGroupId、parent user 是否存在 |
| 某些 Intent 不跨资料 | default filter、DISALLOW_SHARE_INTO_MANAGED_PROFILE、resolver flags |
| quiet mode 后仍显示工作应用 | availability broadcast、profile package state、UI consumer |
| 工作资料没有预期 system apps | installablePackages、denylist、Settings.createNewUserLI |
| 删除后仍有 profile 通知 | PROFILE_REMOVED 与 MANAGED_PROFILE_REMOVED 的 receiver 范围 |
工作资料隔离不是只由 DPC 或只由 PMS 完成:UMS 管理用户类型和生命周期,PMS 管理 per-user 包状态与 Intent filter,DPM 管理 profile owner 和策略,StorageManager/installd 管理数据目录。排查必须先确定故障落在哪个 owner。
12. 阅读检查
顺着源码复述:DPC 调 createProfileForUserWithThrow → UMS 校验 type/restriction/parent → PMS 初始化包状态 → UMS 应用 default restrictions → PMS 保存 cross-profile filters → DPC 设置 profile owner。然后回答:profileGroupId 是否等于每个 profile 的 parentId?默认 cross-profile filter 是否允许所有 Intent?quiet mode 是否删除 APK?正确答案分别是“不一定,它是组根关系”“不是,只允许源码列出的 action”“不会,只改变可用性通知和运行状态”。
