Skip to content

工作资料

追踪 Android 17 Managed Profile 的类型约束、父子关系、包初始化、跨资料 Intent 和可用性广播。

基于android-17.0.0_r1
AndroidPMSUserManagerServiceMultiUserManagedProfile

工作资料 ​

本文承接 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,符号:字段与访问器

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

java
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

java
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。

java
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 设置

java
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

java
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 回调

java
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

java
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

java
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

java
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

java
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

java
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

java
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。

java
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

java
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

java
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 appsinstallablePackages、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”“不会,只改变可用性通知和运行状态”。