第 61 章:设备策略与 Android 企业版

Android 企业版是一套 API、基础设施组件与管理模式的统称,用于让企业组织大规模管理 Android 设备。其核心是设备策略框架 ,该框架是运行在 system‑server 内部的子系统,以DevicePolicyManagerService(DPMS)为核心,将高层企业管控意图("要求 6 位 PIN 码"、"在工作资料空间禁用摄像头")转化为 Android 全栈中具体可强制执行的变更。本章沿着 AOSP 真实源码梳理每一条关键执行路径:从声明管理组件的 XML 元数据,到两万五千行的 DPMS 实现,再到策略引擎解析层,最终抵达各个子系统执行器,使每一条策略真正落地生效。

61.1 企业版架构

61.1.1 问题场景

企业移动管理(EMM)必须平衡两组相互矛盾的诉求:

  • 企业管控:组织需要强制执行安全策略、部署应用、推送配置、设备遗失时擦除数据、审计设备行为。
  • 用户隐私:员工不希望雇主查看个人照片、读取私人消息、非工作时段追踪位置。

Android 企业版通过用户空间隔离(工作资料空间)、权限等级划分(设备所有者与资料所有者)、细粒度策略 API(现代 AOSP 中超过 250 项可独立管控的策略)共同化解上述矛盾。

61.1.2 管理模式

Android 定义了四种主要管理模式,每种模式在 IT 管控权限与用户自由度之间做出不同取舍。

管理模式 设备所有者 资料所有者 典型场景
完全托管模式 IT 组织 企业配发设备
工作资料空间(BYOD 自带设备办公) 员工 IT 组织 员工个人设备
COPE 企业所有、允许个人使用 IT 组织 IT 组织 企业设备,允许个人使用
传统设备管理器 员工 兼容 Android5.0 之前版本

61.1.3 设备所有者(DO)

设备所有者是拥有整台设备完整管理权限的设备策略客户端(DPC)应用。在设备初始化设置向导阶段完成配置(开发调试可通过 adb 完成)。 源码中设备所有者信息由以下类维护:

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
class Owners {
    @GuardedBy("mData")
    private final OwnersData mData;
    // mData.mDeviceOwner 保存设备所有者的OwnerInfo
    // mData.mDeviceOwnerUserId 标记设备所有者运行所在的用户id
}

关键特性:

  1. 单例:一台设备只允许存在一个设备所有者。
  2. 配置部署:设备开箱初始化(OOBE)阶段,可通过 NFC 碰传、二维码、零接触注册、adb shell dpm set‑device‑owner完成设置。
  3. 管控范围:可设置全局策略(Wi‑Fi、时区、系统更新策略、恢复出厂保护)以及按用户维度的策略。
  4. 不可随意移除:一旦设置,仅可通过恢复出厂设置,或者由设备所有者自身调用clearDeviceOwnerApp()进行移除。

DPMS 通过统计日志常量记录管理模式:

复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java (约244‑250行)
import static com.android.server.devicepolicy.DevicePolicyStatsLog
    .DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__DEVICE_OWNER;
import static com.android.server.devicepolicy.DevicePolicyStatsLog
    .DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__COPE;
import static com.android.server.devicepolicy.DevicePolicyStatsLog
    .DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__PROFILE_OWNER;

61.1.4 资料所有者(PO)

资料所有者 管理某一个 Android 用户(通常是托管工作资料空间)。和设备所有者不同,一台设备可以存在多个资料所有者,一个用户对应一个。 Owners类使用SparseArray存储资料所有者:

java 复制代码
// Owners.java 内部 OwnersData
// mData.mProfileOwners 是 SparseArray<OwnerInfo>,以userId作为key

Owners从磁盘加载配置时,会将所有者信息推送到多个子系统:

java 复制代码
// Owners.java, load()
void load() {
    synchronized (mData) {
        int[] usersIds =
            mUserManager.getAliveUsers().stream().mapToInt(u -> u.id).toArray();
        mData.load(usersIds);
        // ... 推送到 DeviceStateCache、ActivityTaskManager、PackageManager
        notifyChangeLocked();
        pushDeviceOwnerUidToActivityTaskManagerLocked();
        pushProfileOwnerUidsToActivityTaskManagerLocked();
    }
}

61.1.5 COPE(企业所有,支持个人使用)

COPE 是 Android11 引入的混合模式:设备归企业所有(存在设备所有者),同时用户拥有个人资料空间;工作资料内运行资料所有者,设备所有者可以对个人侧施加部分限制。

COPE 的属性编码在部署参数中:

java 复制代码
// frameworks/base/core/java/android/app/admin/ManagedProfileProvisioningParams.java
public final class ManagedProfileProvisioningParams implements Parcelable {
    private final boolean mOrganizationOwnedProvisioning;
    // 该字段为true时,资料所有者获得针对个人资料的提升权限
    // 例如可以挂起个人应用
}

UserManagerInternal内部的所有者类型标识区分三种场景:

复制代码
// DevicePolicyManagerService.java 引用
import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_DEVICE_OWNER;
import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER;
import static com.android.server.pm.UserManagerInternal
    .OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;

61.1.6 BYOD(自带设备办公)

BYOD 模式下设备属于员工,仅创建工作资料空间,资料所有者仅管理该工作资料。IT 管理员无法管控个人侧,这是对用户隐私保护程度最高的管理模式。

61.1.7 管理模式决策流程

61.1.8 详解管理模式:策略作用域矩阵

下表展示各类 DPM API 在不同管理模式下是否可用。开发 DPC 时,理解这些作用域至关重要。

表格

API 分类 设备所有者 DO BYOD 资料所有者 PO COPE 资料所有者 PO 传统设备管理器
密码安全等级 支持 仅工作资料 工作资料 + 设备全局 支持
禁用摄像头 支持 仅工作资料 工作资料 + 设备全局 支持
禁用截屏 支持 仅工作资料 仅工作资料 不支持
Wi‑Fi 配置 支持 不支持 不支持 不支持
系统更新策略 支持 不支持 不支持 不支持
恢复出厂 支持 仅资料擦除 仅资料擦除 支持
立即锁屏 支持 锁定工作资料 两者均可锁定 支持
安装 CA 证书 支持 仅工作资料 仅工作资料 不支持
安全日志 支持 不支持 支持 不支持
网络日志 支持 不支持 支持 不支持
挂起个人应用 不支持 支持 不支持
始终在线 VPN 支持 仅工作资料 仅工作资料 不支持
跨资料策略 支持 支持 不支持
任务锁定模式 支持 支持 支持 不支持
应用限制 支持 仅工作资料 仅工作资料 不支持
USB 数据信令控制 支持 不支持 不支持 不支持

61.1.9 Android 企业版功能演进

Android 各版本企业版能力变化:

Android 版本 关键企业功能
5.0(Lollipop) 工作资料空间、资料所有者、设备所有者
6.0(M) COSU 企业专用单设备模式、始终在线 VPN
7.0(Nougat) 网络日志、安全日志、DPC 所有者转移
8.0(Oreo) 临时用户、强制备份、配套 DPC
9.0(Pie) 合规校验、二维码部署增强
10 COPE 企业所有托管资料模式
11 个人应用挂起、COPE 能力增强
12 合规确认、隐私仪表盘
13 基于角色的管理、细粒度权限
14 设备策略引擎、多管理员策略冲突解析
15 增强 MTE 内存标签扩展、审计日志、设备防盗 API
16 内容保护策略、系统权限管理员
17 高级保护模式共存、多用户托管设备部署流程

Android16 新增三态内容保护策略setContentProtectionPolicy,以及system:权限前缀,允许受信任平台服务作为策略引擎管理员。Android17 在此基础上扩展:全新高级保护模式(AAPM)服务以系统管理员身份驱动多项设备策略(MTE、USB、安装未知来源应用),无头设备场景提供完整多用户托管设备部署流程。以上内容将在 61.8 节详细说明。

61.1.10 无头系统用户模式

现代 Android 支持无头系统用户模式,在车载、多用户设备场景尤为重要。DeviceAdminInfo定义三种无头模式:

复制代码
// frameworks/base/core/java/android/app/admin/DeviceAdminInfo.java
public static final int HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED = 0;
public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1;
public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2;
  • 关联模式:设备所有者运行在系统用户,其余所有用户自动添加资料所有者。
  • 单用户模式:设备所有者部署到第一个次级用户,禁止创建更多次级用户。

61.2 DevicePolicyManagerService

61.2.1 概述与类继承结构

DevicePolicyManagerService是 AOSP 中体量最大的系统服务之一,Android17 源码树约 24000 行代码,路径:frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java。 它实现IDevicePolicyManagerAIDL 接口,运行在 system‑server 进程内。

源码文件位置:

组件 文件路径
AIDL 接口 frameworks/base/core/java/android/app/admin/IDevicePolicyManager.aidl
客户端 API frameworks/base/core/java/android/app/admin/DevicePolicyManager.java
服务实现 frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
策略引擎 frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
所有者信息管理 frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
管理员状态 frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
单用户数据 frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java

61.2.2 DPMS 内部架构

梳理启动流程前,先了解 DPMS 内部组件及其依赖关系。

这套组件拆分是多年重构的结果。最初 DPMS 是单一巨型类,引擎、监控器、辅助类被逐步抽离,提升可维护性与可测试性。

61.2.3 服务注册与启动流程

SystemServer将 DPMS 注册为系统服务,启动生命周期遵循标准SystemService阶段。

进入PHASE_BOOT_COMPLETED阶段后,服务处理延迟的恢复出厂动作,并确保设备所有者所属用户已经启动:

cpp 复制代码
// DevicePolicyManagerService.java, onBootPhase()
case SystemService.PHASE_BOOT_COMPLETED:
    // 理论上可以更早执行,但依赖RecoverySystem,过早阶段会发生阻塞
    factoryResetIfDelayedEarlier();
    ensureDeviceOwnerUserStarted();
    break;

61.2.4 管理员组件模型

管理员组件是继承DeviceAdminReceiverBroadcastReceiver子类。系统通过 Manifest 清单识别该组件。

XML 复制代码
<!-- DPC应用 AndroidManifest.xml 示例 -->
<receiver
    android:name=".MyDeviceAdminReceiver"
    android:permission="android.permission.BIND_DEVICE_ADMIN">
    <meta‑data
        android:name="android.app.device_admin"
        android:resource="@xml/device_admin" />
    <intent‑filter>
        <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
    </intent‑filter>
</receiver>

引用的 XML 资源声明该管理员需要使用哪些策略能力:

XML 复制代码
<!-- res/xml/device_admin.xml -->
<device‑admin xmlns:android="http://schemas.android.com/apk/res/android">
    <uses‑policies>
        <limit‑password />
        <watch‑login />
        <reset‑password />
        <force‑lock />
        <wipe‑data />
        <encrypted‑storage />
        <disable‑camera />
        <disable‑keyguard‑features />
    </uses‑policies>
</device‑admin>

策略标签直接对应DeviceAdminInfo内常量:

java 复制代码
// frameworks/base/core/java/android/app/admin/DeviceAdminInfo.java
public static final int USES_POLICY_LIMIT_PASSWORD = 0;
public static final int USES_POLICY_WATCH_LOGIN = 1;
public static final int USES_POLICY_RESET_PASSWORD = 2;
public static final int USES_POLICY_FORCE_LOCK = 3;
public static final int USES_POLICY_WIPE_DATA = 4;
public static final int USES_POLICY_SETS_GLOBAL_PROXY = 5;
public static final int USES_POLICY_EXPIRE_PASSWORD = 6;
public static final int USES_ENCRYPTED_STORAGE = 7;
public static final int USES_POLICY_DISABLE_CAMERA = 8;
public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9;

61.2.5 ActiveAdmin:单管理员状态

当管理员组件被激活(普通设备管理员、资料所有者、设备所有者),DPMS 会创建ActiveAdmin对象,保存该管理员完整的策略状态。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
class ActiveAdmin {
    DeviceAdminInfo info;
    PasswordPolicy passwordPolicy;
    boolean disableCamera;
    boolean disableScreenCapture;
    boolean disableCallerIdAccess;
    boolean disableContactsSearch;
    boolean disableBluetoothContactSharing;
    int disabledKeyguardFeatures;
    long maximumTimeToUnlock;
    int maximumFailedPasswordsForWipe;
    boolean encryptionRequested;
    boolean testOnlyAdmin;
    // ... 数十个其他策略字段
}

ActiveAdmin内部定义序列化标签,用于将全部策略持久化写入 XML:

java 复制代码
// ActiveAdmin.java
private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable‑keyguard‑features";
private static final String TAG_DISABLE_CAMERA = "disable‑camera";
private static final String TAG_DISABLE_CALLER_ID = "disable‑caller‑id";
private static final String TAG_DISABLE_CONTACTS_SEARCH = "disable‑contacts‑search";
private static final String TAG_DISABLE_BLUETOOTH_CONTACT_SHARING =
    "disable‑bt‑contacts‑sharing";
private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable‑screen‑capture";
private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable‑account‑management";
private static final String TAG_ENCRYPTION_REQUESTED = "encryption‑requested";
private static final String TAG_MAX_FAILED_PASSWORD_WIPE = "max‑failed‑password‑wipe";
private static final String TAG_MAX_TIME_TO_UNLOCK = "max‑time‑to‑unlock";
private static final String TAG_PASSWORD_QUALITY = "password‑quality";
private static final String TAG_MIN_PASSWORD_LENGTH = "min‑password‑length";
// ...更多标签

61.2.6 DevicePolicyData:按用户维度状态

除每个管理员自身状态,DPMS 通过DevicePolicyData维护每个用户独立数据:

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
class DevicePolicyData {
    private static final String TAG_ACCEPTED_CA_CERTIFICATES = "accepted‑ca‑certificate";
    private static final String TAG_LOCK_TASK_COMPONENTS = "lock‑task‑component";
    private static final String TAG_LOCK_TASK_FEATURES = "lock‑task‑features";
    private static final String TAG_STATUS_BAR = "statusbar";
    private static final String TAG_APPS_SUSPENDED = "apps‑suspended";
    private static final String TAG_SECONDARY_LOCK_SCREEN = "secondary‑lock‑screen";
    private static final String TAG_AFFILIATION_ID = "affiliation‑id";
    private static final String TAG_LAST_SECURITY_LOG_RETRIEVAL = "last‑security‑log‑retrieval";
    private static final String TAG_LAST_BUG_REPORT_REQUEST = "last‑bug‑report‑request";
    private static final String TAG_LAST_NETWORK_LOG_RETRIEVAL = "last‑network‑log‑retrieval";
    // ...
}

按用户保存的数据包含:

  • 任务锁定模式配置(允许的包名、特性开关)
  • 管理员安装的 CA 证书
  • 关联 ID,用于判断用户之间是否归属同一企业
  • 恢复出厂标记(待执行标记、原因)
  • 密码托管令牌句柄

数据持久化到每个用户系统目录下 XML 文件:

复制代码
/data/system/users/<userId>/device_policies.xml
/data/system/device_owner_2.xml

61.2.7 DevicePolicyEngine:多管理员策略冲突解析

Android14 起 AOSP 引入DevicePolicyEngine,处理多个管理管理员同时设置冲突策略的场景。这对于设备所有者、资料所有者、基于角色管理员共存至关重要。

引擎把策略分为两种作用域存储:

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
final class DevicePolicyEngine {
    // Map<userId, Map<policyKey, policyState>>
    @GuardedBy("mLock")
    private final Map<Integer, Map<PolicyKey, PolicyState<?>>> mLocalPolicies;

    // Map<policyKey, policyState>
    @GuardedBy("mLock")
    private final Map<PolicyKey, PolicyState<?>> mGlobalPolicies;
}

61.2.8 冲突解析机制

每一条策略定义都会声明解析机制,用于解决多个管理员给出冲突取值时如何合并结果。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
private static final MostRestrictive<Boolean> FALSE_MORE_RESTRICTIVE =
    new MostRestrictive<>(
        List.of(new BooleanPolicyValue(false), new BooleanPolicyValue(true)));

private static final MostRestrictive<Boolean> TRUE_MORE_RESTRICTIVE =
    new MostRestrictive<>(
        List.of(new BooleanPolicyValue(true), new BooleanPolicyValue(false)));

所有解析机制都继承抽象类ResolutionMechanism<V>。Android17 内置 7 种具体实现,其中LeastRecentListUnion为该版本新增。

解析机制 源码文件 说明 示例策略
MostRestrictive<V> MostRestrictive.java 按预定义顺序,取限制程度最高的值 禁用摄像头、禁用截屏
TopPriority<V> TopPriority.java 按管理员权限优先级列表,取优先级最高管理员的值 任务锁定模式、持久首选 Activity
MostRecent<V> MostRecent.java 取最近一次设置的值 无严格顺序的单管理员配置项
LeastRecent<V> LeastRecent.java(Android17 新增) 取最早设置的值,先写入优先 先写优先类策略
PackageSetUnion PackageSetUnion.java 合并全部管理员的包集合取并集 禁止用户操作的应用包
ListUnion<T> ListUnion.java(Android17 新增) 合并全部管理员列表并集 允许输入法列表类策略
FlagUnion FlagUnion.java 全部管理员整型标记做按位或运算 锁屏特性禁用标记

示例:安全日志使用TRUE_MORE_RESTRICTIVE,只要任意管理员开启安全日志,最终就保持开启状态。

java 复制代码
// PolicyDefinition.java
static PolicyDefinition<Boolean> SECURITY_LOGGING = new PolicyDefinition<>(
    new NoArgsPolicyKey(DevicePolicyIdentifiers.SECURITY_LOGGING_POLICY),
    TRUE_MORE_RESTRICTIVE,
    POLICY_FLAG_GLOBAL_ONLY_POLICY,
    PolicyEnforcerCallbacks::enforceSecurityLogging,
    new BooleanPolicySerializer());

61.2.9 策略标记

每条PolicyDefinition携带标记位,控制策略作用域与行为。

java 复制代码
// PolicyDefinition.java
private static final int POLICY_FLAG_NONE = 0;
private static final int POLICY_FLAG_GLOBAL_ONLY_POLICY = 1;
private static final int POLICY_FLAG_LOCAL_ONLY_POLICY = 1 << 1;
private static final int POLICY_FLAG_INHERITABLE = 1 << 2;
private static final int POLICY_FLAG_NON_COEXISTABLE_POLICY = 1 << 3;
static final int POLICY_FLAG_USER_RESTRICTION_POLICY = 1 << 4;
static final int POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED = 1 << 5;
private static final int POLICY_FLAG_PACKAGE_POLICY = 1 << 6;
  • GLOBAL_ONLY:策略作用域为整台设备(如自动时区)
  • LOCAL_ONLY:策略仅作用于单个用户(如权限授予)
  • INHERITABLE:子资料空间继承父用户策略
  • NON_COEXISTABLE_POLICY:各管理员的值独立保存,不参与合并(例如应用限制)
  • USER_RESTRICTION_POLICY:标记该策略属于用户限制,走特殊处理逻辑
  • SKIP_ENFORCEMENT_IF_UNCHANGED:解析结果无变化时跳过执行回调,避免多余下层调用
  • PACKAGE_POLICY:标记策略以包名为 key,应用卸载时引擎自动清理对应策略

61.2.10 EnforcingAdmin:策略引擎内管理员身份

EnforcingAdmin类在策略引擎内部抽象管理员身份。Android17 识别四类权限主体,使用常量字符串 / 前缀标识。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
public final class EnforcingAdmin {
    public static final String ROLE_AUTHORITY_PREFIX = "role:";
    public static final String SYSTEM_AUTHORITY_PREFIX = "system:";
    public static final String DPC_AUTHORITY = "enterprise";
    public static final String DEVICE_ADMIN_AUTHORITY = "device_admin";

    private final ComponentName mComponentName;
    private final AdminKey mAdminKey;
    private Set<String> mAuthorities;
    private final boolean mIsRoleAuthority;
}

早期DevicePolicyEngine使用<packageName, userId>定位管理员。Android16/17 重构为封闭层级AdminKeyAdminKey.PackageAdminKey.LegacyAdminKey.System),即使包名相同,企业管理员、传统设备管理员、系统管理员之间也不会发生 key 冲突。静态工厂方法为每类权限主体生成对应身份。

java 复制代码
static EnforcingAdmin createEnterpriseEnforcingAdmin(
        ComponentName componentName, int userId) {
    return new EnforcingAdmin(
        new AdminKey.Package(userId, componentName.getPackageName()),
        componentName, /* isRoleAuthority */ false,
        new HashSet<>(Set.of(DPC_AUTHORITY)));
}

static EnforcingAdmin createDeviceAdminEnforcingAdmin(
        ComponentName componentName, int userId) {
    return new EnforcingAdmin(
        new AdminKey.Legacy(userId, componentName),
        componentName, /* isRoleAuthority */ false,
        new HashSet<>(Set.of(DEVICE_ADMIN_AUTHORITY)));
}

static EnforcingAdmin createSystemEnforcingAdmin(String systemEntity) {
    return new EnforcingAdmin(
        new AdminKey.System(systemEntity),
        /* componentName */ null, /* isRoleAuthority */ false,
        getSystemAuthority(systemEntity));
}

第四个工厂createRoleEnforcingAdmin仅接收包名与用户 ID(不需要ComponentName),解析该应用持有的角色,生成一组role:<roleName>权限标识。 SYSTEM_AUTHORITY_PREFIXsystem:)与createSystemEnforcingAdmin这套机制是重要新增能力:受信任平台服务(典型如高级保护模式 AAPM)可以和 DPC 共用同一套策略引擎设置设备策略,本身不需要是 DPC 应用。 持久化权限字符串解析为android.app.admin.Authority对象的实现在getParcelableAuthorityenterprise映射为DpcAuthoritydevice_admin映射为DeviceAdminAuthorityrole:前缀映射为RoleAuthoritysystem:前缀映射为SystemAuthority

61.2.11 策略 API 权限模型

Android13 起,大量 DPM API 不再只依赖管理员ComponentName身份,改用细粒度权限。DPMS 导入大量MANAGE_DEVICE_POLICY_*权限。

java 复制代码
// DevicePolicyManagerService.java (19‑49行)
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CERTIFICATES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_INPUT_METHODS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_KEYGUARD;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_TASK;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SCREEN_CAPTURE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SECURITY_LOGGING;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIPE_DATA;
// ...更多权限

该机制允许非 DPC 应用(例如角色持有者)管控特定策略,而不需要完整设备所有者 / 资料所有者权限。

61.2.12 权限委托:共享管理员能力

设备所有者或资料所有者可以将部分管理能力委托给其他应用,而不需要授予完整管理员身份。

java 复制代码
// DevicePolicyManager.java
public static final String DELEGATION_APP_RESTRICTIONS = "delegation‑app‑restrictions";
public static final String DELEGATION_BLOCK_UNINSTALL = "delegation‑block‑uninstall";
public static final String DELEGATION_CERT_INSTALL = "delegation‑cert‑install";
public static final String DELEGATION_CERT_SELECTION = "delegation‑cert‑selection";
public static final String DELEGATION_ENABLE_SYSTEM_APP = "delegation‑enable‑system‑app";
public static final String DELEGATION_INSTALL_EXISTING_PACKAGE =
    "delegation‑install‑existing‑package";
public static final String DELEGATION_KEEP_UNINSTALLED_PACKAGES =
    "delegation‑keep‑uninstalled‑packages";
public static final String DELEGATION_NETWORK_LOGGING = "delegation‑network‑logging";
public static final String DELEGATION_PACKAGE_ACCESS = "delegation‑package‑access";
public static final String DELEGATION_PERMISSION_GRANT = "delegation‑permission‑grant";
public static final String DELEGATION_SECURITY_LOGGING = "delegation‑security‑logging";

被委托的应用接收DelegatedAdminReceiver回调:

java 复制代码
// frameworks/base/core/java/android/service/restrictions/DelegatedAdminReceiver.java
public class DelegatedAdminReceiver extends BroadcastReceiver {
    // 接收委托操作回调,例如网络日志、安全日志、证书选择
}

61.2.13 策略持久化与 XML 格式

DPMS 把全部策略状态持久化为 XML。理解 XML 格式对于调试、解析dumpsys device_policy输出非常关键。

设备所有者文件/data/system/device_owner_2.xml

XML 复制代码
<?xml version='1.0' encoding='utf‑8' standalone='yes' ?>
<root>
    <device‑owner
        package="com.example.dpc"
        name="Enterprise DPC"
        component="com.example.dpc/.MyDeviceAdminReceiver"
        userRestrictionsMigrated="true" />
    <device‑owner‑context userId="0" />
</root>

单用户策略文件/data/system/users/<userId>/device_policies.xml

XML 复制代码
<?xml version='1.0' encoding='utf‑8' standalone='yes' ?>
<policies setup‑complete="true" provisioning‑state="3"
    permission‑policy="0" device‑paired="true"
    new‑user‑disclaimer="not_needed">
    <admin name="com.example.dpc/.MyDeviceAdminReceiver">
        <policies flags="255" />
        <password‑quality value="327680" />
        <min‑password‑length value="6" />
        <password‑history‑length value="3" />
        <max‑time‑to‑unlock value="300000" />
        <max‑failed‑password‑wipe value="10" />
        <disable‑camera value="true" />
        <disable‑keyguard‑features value="56" />
        <disable‑screen‑capture value="true" />
        <encryption‑requested value="true" />
    </admin>
    <lock‑task‑component value="com.example.kiosk" />
    <lock‑task‑features value="16" />
    <affiliation‑id id="enterprise‑corp‑123" />
</policies>

XML 标签说明:

XML 标签 说明
password‑quality 密码安全等级(十六进制常量)
min‑password‑length 密码最小长度
max‑time‑to‑unlock 锁屏前最大空闲时长,单位毫秒
max‑failed‑password‑wipe 密码错误 N 次执行擦除
disable‑camera 禁用摄像头布尔标记
disable‑keyguard‑features 锁屏禁用特性位掩码
lock‑task‑component 任务锁定模式允许应用包名
affiliation‑id 企业关联标识

61.2.14 调用方身份与权限校验

每一次 DPM API 调用都会执行严格的调用者身份校验。CallerIdentity类保存调用上下文。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
// 记录:调用UID、PID、包名、用户ID
// 用于校验调用方是否是激活管理员、DO、PO,或是持有对应MANAGE_DEVICE_POLICY_*权限

典型权限校验流程:

61.2.15 线程安全与锁机制

DPMS 使用全局锁对象做同步。

java 复制代码
// DevicePolicyManagerService.java
// getLockObject() 返回主同步锁对象
// 大量方法使用该锁做synchronized保证数据一致性

Owners类使用自身mData锁保护所有者数据;DevicePolicyEngine使用mLock保护策略状态。同时获取多把锁时必须小心避免死锁。

61.2.16 策略执行完整流程

管理员调用 DPM API,请求依次经过多层组件:

61.2.17 Binder 缓存

DPMS 使用IpcDataCache,对高频查询策略状态避免重复 Binder 调用。DevicePolicyCacheImpl提供进程内缓存,缓存项包括:截屏是否禁用、摄像头是否禁用、密码复杂度等。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/
//   DevicePolicyCacheImpl.java
// Caches: screen-capture-disallowed users, password quality, permission
// policy, launcher shortcut overrides, sensors-permission grant, and
// content protection policy

当所有者信息发生变更,缓存主动失效:

java 复制代码
// Owners.java
private void pushToDevicePolicyManager() {
    DevicePolicyManagerService.invalidateBinderCaches();
}

61.3 工作资料空间

61.3.1 概念模型

工作资料空间是 Android 的托管资料:和个人资料运行在同一台设备上,拥有完全独立:

  • 应用实例(独立副本)
  • 数据目录 /data/user/<profileUserId>/
  • 联系人数据库
  • 日历存储
  • 通知栏分组
  • 独立 FBE 基于文件加密密钥

在桌面 Launcher 中用户看到 "工作" 标签,工作应用图标带有公文包角标。

61.3.2 创建托管工作资料

工作资料通过DevicePolicyManagerAPI 创建,核心入口:createAndProvisionManagedProfile()

java 复制代码
// DevicePolicyManager.java
// @SystemApi
public UserHandle createAndProvisionManagedProfile(
    @NonNull ManagedProfileProvisioningParams provisioningParams)
    throws ProvisioningException { ... }

部署参数控制资料初始化配置:

java 复制代码
// ManagedProfileProvisioningParams.java
public final class ManagedProfileProvisioningParams implements Parcelable {
    @NonNull private final ComponentName mProfileAdminComponentName;
    @NonNull private final String mOwnerName;
    @Nullable private final String mProfileName;
    @Nullable private final Account mAccountToMigrate;
    private final boolean mLeaveAllSystemAppsEnabled;
    private final boolean mOrganizationOwnedProvisioning;
    private final boolean mKeepAccountOnMigration;
    @NonNull private final PersistableBundle mAdminExtras;
}

服务端 DPMS 协调整套创建流程:

DevicePolicyManagerService实现会在创建前做大量前置条件校验:

java 复制代码
// DevicePolicyManagerService.java, createAndProvisionManagedProfile()
@Override
public UserHandle createAndProvisionManagedProfile(
        @NonNull ManagedProfileProvisioningParams provisioningParams,
        @NonNull String callerPackage) {
    Objects.requireNonNull(provisioningParams, "provisioningParams is null");
    Objects.requireNonNull(callerPackage, "callerPackage is null");
    // ...权限校验、前置条件校验、创建资料
}

61.3.3 托管资料部署前置条件

DPMS 执行大量前置检查才允许创建托管资料,状态码可以定位失败原因。

java 复制代码
// DevicePolicyManager.java
public static final int STATUS_OK = 0;
public static final int STATUS_ACCOUNTS_NOT_EMPTY = 3;
public static final int STATUS_CANNOT_ADD_MANAGED_PROFILE = 7;
public static final int STATUS_HAS_DEVICE_OWNER = 1;
public static final int STATUS_USER_HAS_PROFILE_OWNER = 2;
public static final int STATUS_USER_SETUP_COMPLETED = 4;
public static final int STATUS_MANAGED_USERS_NOT_SUPPORTED = 8;
public static final int STATUS_NOT_SYSTEM_USER = 9;
// ...更多状态码

61.3.4 跨资料 Intent 过滤器

跨资料 Intent 过滤器控制哪些 Intent 可以跨越工作‑个人边界。DPC 通过下面 API 配置。

java 复制代码
// DevicePolicyManager.java
public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 0x0001;
public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002;

@RequiresPermission(value = MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
                    conditional = true)
public void addCrossProfileIntentFilter(
    @Nullable ComponentName admin, IntentFilter filter, int flags) { ... }

当个人应用发出匹配跨资料过滤器的 Intent,系统会把请求解析到工作资料(或反向,取决于 flag)。

跨资料小组件提供者对应策略定义在PolicyDefinition.java,由ActiveAdmin读取存储。

java 复制代码
// PolicyDefinition.java (引用自ActiveAdmin)
private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS =
    "cross‑profile‑widget‑providers";

61.3.5 工作模式开关

用户可以手动打开、关闭工作资料。工作资料暂停时:全部工作应用挂起,通知隐藏,工作数据不可访问。

资料状态变更系统会发出特定广播:

java 复制代码
// DevicePolicyManagerService.java
import static android.content.Intent.ACTION_MANAGED_PROFILE_AVAILABLE;
import static android.content.Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE;

DPMS 跟踪资料状态;COPE 模式下,如果工作资料关闭超过设定时长,可以触发挂起个人应用。

java 复制代码
// ActiveAdmin.java
private static final String TAG_SUSPEND_PERSONAL_APPS = "suspend‑personal‑apps";
private static final String TAG_PROFILE_MAXIMUM_TIME_OFF = "profile‑max‑time‑off";
private static final String TAG_PROFILE_OFF_DEADLINE = "profile‑off‑deadline";

61.3.6 COPE 模式:个人应用挂起

企业归属设备场景,当工作资料关闭超过配置的超时时间,资料所有者可以挂起个人应用。PersonalAppsSuspensionHelper负责计算哪些个人应用需要被挂起。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
public final class PersonalAppsSuspensionHelper {
    // 判断待挂起个人应用,以下应用排除:
    // ‑输入法
    // ‑无障碍服务
    // ‑默认短信应用
    // ‑必需系统应用
}

挂起状态常量定义在DevicePolicyManager

java 复制代码
// DevicePolicyManager.java
public static final int PERSONAL_APPS_NOT_SUSPENDED = 0;
public static final int PERSONAL_APPS_SUSPENDED_EXPLICITLY = 1;
public static final int PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT = 2;

61.3.7 工作资料数据隔离

工作资料通过多层机制实现数据隔离。

隔离分层

各边界独立强制执行:

  • 文件系统:每个用户拥有独立/data/user/<uid>目录;FBE 模式下每个用户 CE 存储拥有独立密钥。工作资料锁定时 CE 密钥被回收,工作数据不可访问。
  • ContentProvider:框架把查询路由到对应用户实例;个人应用查询联系人默认只能看到个人联系人,除非显式开启跨资料访问。
  • 包可见性:默认一个资料中的应用看不到另一个资料的应用;PackageManager基于调用用户过滤返回结果。
  • 网络:工作资料可以拥有独立 VPN、代理、网络偏好;DPMS 通过ConnectivityManager完成配置。
java 复制代码
// DevicePolicyManagerService.java
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
import static android.net.ConnectivityManager
    .PROFILE_NETWORK_PREFERENCE_ENTERPRISE_BLOCKING;
import static android.net.ConnectivityManager
    .PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;

61.3.8 工作资料托管 eSIM 卡订阅

支持 eSIM 的设备,工作资料可以拥有托管通信订阅:

java 复制代码
// frameworks/base/core/java/android/app/admin/ManagedSubscriptionsPolicy.java
public final class ManagedSubscriptionsPolicy implements Parcelable {
    // 控制工作资料内托管eSIM订阅处理逻辑
}

61.3.9 保持资料后台运行

较新特性:用户把工作资料切到 "静默暂停" 状态,进程依然可以保持运行。

java 复制代码
// DevicePolicyData.java
private static final String TAG_KEEP_PROFILES_RUNNING = "keep‑profiles‑running";

该特性用于:即使用户关闭工作开关,工作应用仍可以接收推送、同步数据。

61.3.10 工作资料电话业务

工作资料暂停时,DPMS 可以展示未接工作来电通知。 引用字符串资源包含标题、正文、开启工作资料按钮文本。

java 复制代码
// DevicePolicyManagerService.java (referenced string resources)
import static android.app.admin.DevicePolicyResources.Strings.Core
    .WORK_PROFILE_TELEPHONY_PAUSED_BODY;
import static android.app.admin.DevicePolicyResources.Strings.Core
    .WORK_PROFILE_TELEPHONY_PAUSED_TITLE;
import static android.app.admin.DevicePolicyResources.Strings.Core
    .WORK_PROFILE_TELEPHONY_PAUSED_TURN_ON_BUTTON;

61.3.11 删除工作资料

当工作资料被删除(用户操作、管理员触发、策略违规),DPMS 发送对应通知。资源字符串包含:密码多次错误删除资料、通用删除、企业设备删除、通知标题。

java 复制代码
// Referenced string resources
import static android.app.admin.DevicePolicyResources.Strings.Core
    .WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.Core
    .WORK_PROFILE_DELETED_GENERIC_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.Core
    .WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.Core
    .WORK_PROFILE_DELETED_TITLE;

61.4 设备管理员

61.4.1 DeviceAdminReceiver:管理员回调接口

DeviceAdminReceiver是所有管理员组件的基类,继承BroadcastReceiver,提供各类策略事件回调。

java 复制代码
// frameworks/base/core/java/android/app/admin/DeviceAdminReceiver.java
public class DeviceAdminReceiver extends BroadcastReceiver {
    // 生命周期广播
    public static final String ACTION_DEVICE_ADMIN_ENABLED
        = "android.app.action.DEVICE_ADMIN_ENABLED";
    public static final String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED
        = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
    public static final String ACTION_DEVICE_ADMIN_DISABLED
        = "android.app.action.DEVICE_ADMIN_DISABLED";

    // 密码相关广播
    public static final String ACTION_PASSWORD_CHANGED
        = "android.app.action.ACTION_PASSWORD_CHANGED";
    public static final String ACTION_PASSWORD_FAILED
        = "android.app.action.ACTION_PASSWORD_FAILED";
    public static final String ACTION_PASSWORD_SUCCEEDED
        = "android.app.action.ACTION_PASSWORD_SUCCEEDED";
    public static final String ACTION_PASSWORD_EXPIRING
        = "android.app.action.ACTION_PASSWORD_EXPIRING";
}

61.4.2 管理员生命周期

管理员生命周期遵循一套固定执行顺序:

61.4.3 密码策略

密码策略是最常用管理员能力。Android 提供两套方案。

传统等级模式(已废弃,但兼容保留)

java 复制代码
// DevicePolicyManager.java
public static final int PASSWORD_QUALITY_UNSPECIFIED = 0;
public static final int PASSWORD_QUALITY_BIOMETRIC_WEAK = 0x8000;
public static final int PASSWORD_QUALITY_SOMETHING = 0x10000;
public static final int PASSWORD_QUALITY_NUMERIC = 0x20000;
public static final int PASSWORD_QUALITY_NUMERIC_COMPLEX = 0x30000;
public static final int PASSWORD_QUALITY_ALPHABETIC = 0x40000;
public static final int PASSWORD_QUALITY_ALPHANUMERIC = 0x50000;
public static final int PASSWORD_QUALITY_COMPLEX = 0x60000;

现代复杂度模式(推荐)

java 复制代码
// DevicePolicyManager.java
public static final int PASSWORD_COMPLEXITY_NONE = 0;
public static final int PASSWORD_COMPLEXITY_LOW = 0x10000;
public static final int PASSWORD_COMPLEXITY_MEDIUM = 0x30000;
public static final int PASSWORD_COMPLEXITY_HIGH = 0x50000;

复杂度对应要求:

等级 PIN 码 图案 密码
LOW 4 位以上数字 任意 4 字符以上
MEDIUM 4 位以上,禁止重复 / 连续序列 任意 4 字符以上
HIGH 8 位以上,禁止重复 / 连续序列 不可用 6 字符以上,字母 + 数字

ActiveAdmin内部使用独立对象存储密码全部配置:

java 复制代码
// ActiveAdmin.java
PasswordPolicy passwordPolicy = new PasswordPolicy();
// 字段包含:quality、length、大小写字母、数字、符号、非字母字符、历史记录长度

61.4.4 密码过期

管理员强制定期修改密码。

java 复制代码
// ActiveAdmin.java
private static final String TAG_PASSWORD_EXPIRATION_DATE = "password‑expiration‑date";
private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password‑expiration‑timeout";

密码即将过期时,向管理员发送ACTION_PASSWORD_EXPIRING广播,管理员提示用户修改密码。

61.4.5 密码最大错误尝试次数

管理员配置密码多次输错自动擦除数据。

java 复制代码
// ActiveAdmin.java
private static final String TAG_MAX_FAILED_PASSWORD_WIPE = "max‑failed‑password‑wipe";

阈值触发后:资料所有者只会擦除工作资料;设备所有者执行整机恢复出厂。

61.4.6 设备锁定

USES_POLICY_FORCE_LOCK策略允许管理员立即锁定设备,或是设置设备自动锁定前的最大空闲时长。

java 复制代码
// DeviceAdminInfo.java
public static final int USES_POLICY_FORCE_LOCK = 3;
java 复制代码
// ActiveAdmin.java
private static final String TAG_MAX_TIME_TO_UNLOCK = "max-time-to-unlock";
private static final String TAG_STRONG_AUTH_UNLOCK_TIMEOUT = "strong-auth-unlock-timeout";

调用lockNow()时,设备策略管理服务(DPMS)会触发强身份认证。

java 复制代码
// DevicePolicyManagerService.java(引用常量)
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
        .STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;

61.4.7 加密策略

管理员可以要求开启存储加密。

java 复制代码
// DeviceAdminInfo.java
public static final int USES_ENCRYPTED_STORAGE = 7;
java 复制代码
// ActiveAdmin.java
private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";

设备策略管理服务通过DevicePolicyManager提供的常量查询加密状态。

java 复制代码
// DevicePolicyManager.java
public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5;
// 表示基于文件的加密已激活

61.4.8 相机禁用策略

相机禁用策略为布尔型策略,支持按用户配置或者全局配置。

java 复制代码
// DeviceAdminInfo.java
public static final int USES_POLICY_DISABLE_CAMERA = 8;
java 复制代码
// ActiveAdmin.java
private static final String TAG_DISABLE_CAMERA = "disable-camera";

在策略引擎内部,相机禁用采用**取最严格值(TRUE_MORE_RESTRICTIVE)**的解析规则:只要任意一个管理员禁用相机,相机就保持禁用状态。

61.4.9 截屏禁用

与相机禁用类似,截屏功能可以按用户维度禁用。

java 复制代码
// ActiveAdmin.java
private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";

61.4.10 锁屏界面功能禁用

管理员可以选择性禁用锁屏界面的各项功能。

java 复制代码
// DeviceAdminInfo.java
public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9;
java 复制代码
// DevicePolicyManager.java(锁定任务相关特性)
public static final int LOCK_TASK_FEATURE_KEYGUARD = ...;
public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = ...;
public static final int LOCK_TASK_FEATURE_OVERVIEW = ...;
public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = ...;
public static final int LOCK_TASK_FEATURE_HOME = ...;

KEYGUARD_DISABLED_FEATURES策略在策略引擎中有特殊处理逻辑。

java 复制代码
// PolicyDefinition.java(引用)
static final PolicyDefinition KEYGUARD_DISABLED_FEATURES = ...;

61.4.11 恢复出厂设置(数据擦除)

管理员权限中最高风险的操作就是擦除设备数据。

java 复制代码
// DeviceAdminInfo.java
public static final int USES_POLICY_WIPE_DATA = 4;
java 复制代码
// DevicePolicyManager.java(擦除标记位)
public static final int WIPE_EXTERNAL_STORAGE = 0x0001;
public static final int WIPE_RESET_PROTECTION_DATA = 0x0002;
public static final int WIPE_EUICC = 0x0004;
public static final int WIPE_SILENTLY = 0x0008;

设备策略管理服务内部实现会委托给FactoryResetter执行。

java 复制代码
// DevicePolicyManagerService.java(内部注入器类)
.build().factoryReset();

如果系统尚未完全启动,恢复出厂设置操作可以延后执行。

java 复制代码
// DevicePolicyData.java
public static final int FACTORY_RESET_FLAG_ON_BOOT = 1;
public static final int FACTORY_RESET_FLAG_WIPE_EXTERNAL_STORAGE = 2;
public static final int FACTORY_RESET_FLAG_WIPE_EUICC = 4;
public static final int FACTORY_RESET_FLAG_WIPE_FACTORY_RESET_PROTECTION = 8;

61.4.12 恢复出厂设置保护(FRP)

恢复出厂设置保护用于阻止未授权的恢复出厂操作。管理员可以配置恢复出厂之后,哪些账号可以解锁设备。

java 复制代码
// frameworks/base/core/java/android/app/admin/FactoryResetProtectionPolicy.java
public final class FactoryResetProtectionPolicy implements Parcelable {
    // 包含允许的账号列表,以及FRP功能是否开启
}

61.4.13 账号管理

管理员可以管控哪些账号类型允许新增、删除。

java 复制代码
// ActiveAdmin.java
private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
private static final String TAG_ACCOUNT_TYPE = "account-type";

61.4.14 VPN 策略

管理员可以强制开启带锁定模式的始终在线 VPN。

开启锁定模式之后:

  • 在 VPN 连接成功前,全部网络流量都会被拦截。
  • VPN 一旦断开,流量会再次被拦截。
  • 部分系统流量(如网络门户检测流量)可以配置豁免。

61.4.15 受控服务管控

管理员可以限制允许运行的无障碍服务与输入法。

java 复制代码
// ActiveAdmin.java
private static final String TAG_PERMITTED_ACCESSIBILITY_SERVICES =
        "permitted‑accessiblity‑services"; // 保留源码中原有的拼写错误
private static final String TAG_PERMITTED_IMES = "permitted‑imes";
private static final String TAG_PERMITTED_NOTIFICATION_LISTENERS =
        "permitted‑notification‑listeners";

该机制保证受管理环境下,只运行经过审批的无障碍服务与键盘输入法,防止恶意输入法、无障碍服务造成数据泄露。

61.4.16 按流量计费的数据管控

管理员可以阻止指定应用使用按流量计费的网络(蜂窝移动数据)。

java 复制代码
// ActiveAdmin.java
private static final String TAG_METERED_DATA_DISABLED_PACKAGES =
        "metered_data_disabled_packages";

61.4.17 可信代理管理

管理员可以管控可信代理(智能解锁相关功能)。

java 复制代码
// ActiveAdmin.java
private static final String TAG_MANAGE_TRUST_AGENT_FEATURES =
        "manage‑trust‑agent‑features";
private static final String TAG_TRUST_AGENT_COMPONENT_OPTIONS =
        "trust‑agent‑component‑options";
private static final String TAG_TRUST_AGENT_COMPONENT = "component";

61.4.18 就近投屏 / 推送策略

管理员可以管控通知、应用的就近推送功能。

java 复制代码
// ActiveAdmin.java
private static final String TAG_NEARBY_NOTIFICATION_STREAMING_POLICY =
        "nearby‑notification‑streaming‑policy";
private static final String TAG_NEARBY_APP_STREAMING_POLICY =
        "nearby‑app‑streaming‑policy";

61.4.19 组织标识

管理员可以设置组织名称与主题颜色,用于系统界面标识。

java 复制代码
// ActiveAdmin.java
private static final String TAG_ORGANIZATION_COLOR = "organization‑color";
private static final String TAG_ORGANIZATION_NAME = "organization‑name";

组织名称会显示在设置界面,以及与受管理工作资料相关的通知当中。

61.4.20 支持提示消息

管理员可以设置向用户展示的简短、详细两类支持提示消息。

java 复制代码
// ActiveAdmin.java
private static final String TAG_SHORT_SUPPORT_MESSAGE = "short‑support‑message";
private static final String TAG_LONG_SUPPORT_MESSAGE = "long‑support‑message";

短消息展示在设置页面管理员条目旁;长消息展示策略相关的完整说明信息。

61.4.21 会话提示消息(多用户)

针对多用户设备(例如共享平板),管理员可以设置会话开始、结束提示消息。

java 复制代码
// ActiveAdmin.java
private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";

61.4.22 用户限制

除了专门的策略 API 之外,设备所有者与工作资料所有者可以设置用户限制,用于限制设备各项功能。

java 复制代码
// ActiveAdmin.java
private static final String TAG_USER_RESTRICTIONS = "user‑restrictions";
private static final String TAG_DEFAULT_ENABLED_USER_RESTRICTIONS =
        "default‑enabled‑user‑restrictions";
private static final String TAG_RESTRICTION = "restriction";

常用用户限制:

  • UserManager.DISALLOW_INSTALL_APPS 禁止安装应用
  • UserManager.DISALLOW_UNINSTALL_APPS 禁止卸载应用
  • UserManager.DISALLOW_CONFIG_WIFI 禁止配置 Wi‑Fi
  • UserManager.DISALLOW_SHARE_LOCATION 禁止位置共享
  • UserManager.DISALLOW_MODIFY_ACCOUNTS 禁止修改账号
  • UserManager.DISALLOW_CONFIG_BLUETOOTH 禁止配置蓝牙
  • UserManager.DISALLOW_USB_FILE_TRANSFER 禁止 USB 文件传输
  • UserManager.DISALLOW_DEBUGGING_FEATURES 禁止调试功能
  • UserManager.DISALLOW_CONFIG_VPN 禁止配置 VPN
  • UserManager.DISALLOW_FACTORY_RESET 禁止恢复出厂设置
  • UserManager.DISALLOW_REMOVE_MANAGED_PROFILE 禁止删除受管理工作资料
  • UserManager.DISALLOW_ADD_USER 禁止新增用户
  • UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA 禁止挂载物理存储介质
  • UserManager.DISALLOW_OUTGOING_CALLS 禁止拨出电话
  • UserManager.DISALLOW_SMS 禁止短信
  • UserManager.DISALLOW_CELLULAR_2G 禁止 2G 蜂窝网络

策略引擎对用户限制做特殊处理:

java 复制代码
// PolicyDefinition.java
private static final int POLICY_FLAG_USER_RESTRICTION_POLICY = 1 << 4;
/*
 * 将该标记添加到用户限制类策略上。
 * 存在专门API处理用户限制策略,依靠该标记识别这类策略。
 */

61.4.23 锁定任务模式

锁定任务模式将设备限定运行指定应用,适用于自助终端、专用设备场景。

java 复制代码
// DevicePolicyManager.java
public static final int LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK = ...;
public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = ...;
public static final int LOCK_TASK_FEATURE_HOME = ...;
public static final int LOCK_TASK_FEATURE_KEYGUARD = ...;
public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = ...;
public static final int LOCK_TASK_FEATURE_OVERVIEW = ...;
public static final int LOCK_TASK_FEATURE_QUICK_SETTINGS = ...;
public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = ...;

该策略定义使用**最高优先级(TopPriority)**解析规则。

java 复制代码
// PolicyDefinition.java
static PolicyDefinition LOCK_TASK = new PolicyDefinition<>(
        new NoArgsPolicyKey(DevicePolicyIdentifiers.LOCK_TASK_POLICY),
        new TopPriority<>(List.of(
                EnforcingAdmin.getRoleAuthorityOf(ROLE_SYSTEM_FINANCED_DEVICE_CONTROLLER),
                EnforcingAdmin.DPC_AUTHORITY)),
        POLICY_FLAG_LOCAL_ONLY_POLICY,
        (LockTaskPolicy value, Context context, Integer userId, PolicyKey policyKey) ->
                PolicyEnforcerCallbacks.setLockTask(value, context, userId),
        new LockTaskPolicySerializer());

61.5 受管理配置

61.5.1 应用限制框架

受管理配置(又称为应用限制)允许管理员向受管理应用下发键‑值形式配置。这是无需用户交互即可配置工作应用的核心机制。

61.5.2 限制项类型

配置限制以Bundle形式传递,内部存放带类型的键值对。应用在 XML 资源文件中声明自身支持的限制项。

XML 复制代码
<restriction
    android:key="server_url"
    android:restrictionType="string"
    android:title="@string/server_url_title"
    android:description="@string/server_url_description"
    android:defaultValue="https://mail.example.com/" />

<restriction
    android:key="allow_personal_use"
    android:restrictionType="bool"
    android:title="@string/personal_use_title"
    android:defaultValue="false" />

<restriction
    android:key="max_attachment_size"
    android:restrictionType="integer"
    android:title="@string/max_attachment_title"
    android:defaultValue="10" />

支持的限制类型:

XML 类型 Java 类型
bool boolean
string String
integer int
multi‑select String\[\]
choice String
bundle Bundle(嵌套)
bundle_array Parcelable\[\]

61.5.3 应用限制权限委托

app‑restrictions能力支持权限委托。

java 复制代码
// DevicePolicyManager.java
public static final String DELEGATION_APP_RESTRICTIONS = "delegation‑app‑restrictions";

EMM 代理可以将配置管理权限委托给专门的配置应用。

61.5.4 RestrictionsManager

应用通过RestrictionsManager读取受管理配置。

java 复制代码
// android.content.RestrictionsManager
public Bundle getApplicationRestrictions() { ... }
public List getManifestRestrictions(String packageName) { ... }

RestrictionsReceiver让应用可以异步接收限制配置变更。

java 复制代码
// frameworks/base/core/java/android/service/restrictions/RestrictionsReceiver.java
// DevicePolicyManager.java导入该类
import android.service.restrictions.RestrictionsReceiver;

61.5.5 策略引擎处理逻辑

应用限制使用NON_COEXISTABLE_POLICY标记,代表各个管理员的限制独立存储,不会做合并。

java 复制代码
// PolicyDefinition.java
// POLICY_FLAG_NON_COEXISTABLE_POLICY = 1 << 3
/*
 * 标记含义:各个管理员策略互相独立,不执行合并解析。
 * 例如不同管理员针对同一个包设置的应用限制,不做合并,各自保存,查询时互相独立。
 */

61.5.6 受管理配置架构

61.5.7 常用场景受管理配置示例

VPN 配置:

XML 复制代码
<restriction android:key="vpn_protocol" android:restrictionType="choice"
    android:entries="@array/vpn_protocols"
    android:entryValues="@array/vpn_protocol_values" />

邮件配置:

XML 复制代码
<restriction android:key="email_server" android:restrictionType="string" />
<restriction android:key="email_port" android:restrictionType="integer" />
<restriction android:key="use_ssl" android:restrictionType="bool" />

Wi‑Fi 配置(由 DPC 完成配置):

XML 复制代码
<restriction android:key="wifi_ssid" android:restrictionType="string" />
<restriction android:key="wifi_security_type" android:restrictionType="choice" />

61.6 COPE 模式与完全托管设备

61.6.1 完全托管设备配置

完全托管设备在初始化设置阶段通过provisionFullyManagedDevice()完成配置。

java 复制代码
// frameworks/base/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
public final class FullyManagedDeviceProvisioningParams implements Parcelable {
    @NonNull private final ComponentName mDeviceAdminComponentName;
    @NonNull private final String mOwnerName;
    private final boolean mLeaveAllSystemAppsEnabled;
    @Nullable private final String mTimeZone;
    private final long mLocalTime;
    @Nullable private final Locale mLocale;
    private final boolean mDeviceOwnerCanGrantSensorsPermissions;
    @NonNull private final PersistableBundle mAdminExtras;
    private final boolean mDemoDevice;
}

61.6.2 设备配置入口

Android 支持多种设备配置入口:

dpm set‑device‑owner 这条 shell 命令不会走 provisionFullyManagedDevice() ;它在 DevicePolicyManagerServiceShellCommand.runSetDeviceOwner() 中的处理逻辑是:直接先后调用 setActiveAdmin()setDeviceOwner()。 而 provisionFullyManagedDevice() 是系统应用 ManagedProvisioning 使用的入口函数。

设备配置相关 Intent:

java 复制代码
// DevicePolicyManager.java
public static final String ACTION_PROVISION_MANAGED_DEVICE
        = "android.app.action.PROVISION_MANAGED_DEVICE";
public static final String ACTION_PROVISION_MANAGED_PROFILE
        = "android.app.action.PROVISION_MANAGED_PROFILE";
public static final String ACTION_PROVISION_MANAGED_USER
        = "android.app.action.PROVISION_MANAGED_USER";

61.6.3 设备所有者能力

设备所有者拥有最全面管控权限。

分类 能力
网络 设置全局代理、配置 Wi‑Fi、配置 VPN、配置私有 DNS
安全 开启安全日志、开启网络日志、生成证明密钥
应用 静默安装 / 卸载应用、隐藏应用、冻结应用、阻止卸载
系统 设置系统更新策略、重启设备、设置时间与时区
用户 创建 / 删除用户、切换用户、设置关联 ID
硬件 禁用相机、禁用截屏、禁用 USB 数据
电话 配置 APN、管理 SIM 卡订阅
标识 设置组织名称、设置设备所有者锁屏信息

61.6.4 COPE 架构

COPE 模式:个人侧具备设备所有者权限,工作资料内具备资料所有者权限。关键标记为mOrganizationOwnedProvisioning

java 复制代码
// ManagedProfileProvisioningParams.java
private final boolean mOrganizationOwnedProvisioning;

标记开启时,工作资料所有者对个人资料获得额外管控能力:

  • 工作资料长时间未使用时,可以冻结个人应用。
  • 在设备级别锁屏强制密码策略。
  • 管控整机网络日志。
  • 读取设备硬件标识(IMEI、序列号)。

61.6.5 COPE 模式与完全托管设备对比

特性 完全托管 COPE
是否存在设备所有者 否(资料所有者,拥有提升权限)
是否允许个人应用 由 IT 决定 是(核心设计目标)
IT 是否可见个人应用 IT 可以查看 IT 无法查看
是否存在工作资料 可选 必须存在
用户能否删除工作资料 不能 不能(组织所属模式)
恢复出厂控制 完全控制 通过 FRP 保护
个人应用冻结 不适用 支持(工作长时间未使用)

61.6.6 分期设备模式

Android 支持分期设备模式,用于分期付款设备。

java 复制代码
// DevicePolicyManager.java
public static final int DEVICE_OWNER_TYPE_DEFAULT = 0;
public static final int DEVICE_OWNER_TYPE_FINANCED = 1;

分期设备控制器使用ROLE_SYSTEM_FINANCED_DEVICE_CONTROLLER角色,在策略解析中拥有高优先级。

java 复制代码
// PolicyDefinition.java(锁定任务示例)
new TopPriority<>(List.of(
        EnforcingAdmin.getRoleAuthorityOf(ROLE_SYSTEM_FINANCED_DEVICE_CONTROLLER),
        EnforcingAdmin.DPC_AUTHORITY))

61.6.7 系统更新策略

设备所有者可以控制系统更新执行方式。

java 复制代码
// Owners.java引用
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.SystemUpdateInfo;

四种更新策略:

  1. 自动:更新可用就立即安装
  2. 时间窗口:在配置好的维护窗口内安装
  3. 延后更新:最多延后 30 天
  4. 冻结时段:指定日期区间完全阻止更新
java 复制代码
// DevicePolicyManager.java
// FreezePeriod用于阻止更新,例如大促节假日
import android.app.admin.FreezePeriod;

61.6.8 始终在线 VPN

设备所有者、资料所有者可以强制启用始终在线 VPN。

java 复制代码
// ActiveAdmin.java
private static final String TAG_ALWAYS_ON_VPN_PACKAGE = "vpn‑package";

配置始终在线 VPN 之后,VPN 未建立前全部网络流量拦截(锁定模式)。

61.7 跨资料通信

61.7.1 跨资料边界

工作‑个人资料边界是 Android 企业版最重要安全特性。默认情况下,一个资料内应用不能访问另一个资料的应用与数据。跨资料通信必须显式开启。

61.7.2 跨资料 Intent 过滤器

DPC 管控可以跨资料传递的 Intent。

java 复制代码
// DevicePolicyManager.java
public void addCrossProfileIntentFilter(
        @Nullable ComponentName admin,
        IntentFilter filter,
        int flags) { ... }

public void clearCrossProfileIntentFilters(
        @Nullable ComponentName admin) { ... }

标记控制跳转方向:

java 复制代码
// DevicePolicyManager.java
public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 0x0001;
// 个人应用可以解析Intent跳转至工作应用

public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002;
// 工作应用可以解析Intent跳转至个人应用

61.7.3 默认跨资料 Intent 过滤器

DPC 未配置任何规则前,系统自带默认跨资料 Intent 过滤器,保障基础功能。典型场景:

  • 电话拨号:个人拨号器展示工作联系人
  • Web 链接:跨资料跳转打开网页
  • 设置:任意资料访问系统设置

61.7.4 CrossProfileApps API

CrossProfileApps提供高层 API 用于跨资料交互。

java 复制代码
// frameworks/base/core/java/android/content/pm/CrossProfileApps.java
public class CrossProfileApps {

    public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED =
            "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";

    // 在另一个资料启动Activity
    public void startMainActivity(
            ComponentName component, UserHandle targetUser) { ... }

    // 获取支持跨资料交互的目标用户
    public List getTargetUserProfiles() { ... }

    // 判断是否允许跨资料交互
    public boolean canInteractAcrossProfiles() { ... }
    public boolean canRequestInteractAcrossProfiles() { ... }
}

61.7.5 应用清单声明跨资料能力

想要跨资料交互的应用,需要在 AndroidManifest 声明对应能力。

XML 复制代码
<manifest>
    <application android:crossProfile="true">
        <!-- App can receive CAN_INTERACT_ACROSS_PROFILES_CHANGED
             in manifest receivers -->
    </application>
</manifest>

61.7.6 个人应用读取工作联系人

个人拨号界面展示工作联系人是最常见跨资料能力,受多条策略管控。

java 复制代码
// ActiveAdmin.java
private static final String TAG_DISABLE_CALLER_ID = "disable‑caller‑id";
private static final String TAG_DISABLE_CONTACTS_SEARCH = "disable‑contacts‑search";
private static final String TAG_DISABLE_BLUETOOTH_CONTACT_SHARING =
        "disable‑bt‑contacts‑sharing";

管理员可以独立管控:

  • 跨资料来电号码识别(来电显示工作联系人名称)
  • 跨资料联系人搜索(个人应用检索工作联系人)
  • 蓝牙联系人同步(车载套件蓝牙同步工作联系人)

61.7.7 跨资料日历

管理员可以允许个人日历应用读取工作日历事件。

java 复制代码
// ActiveAdmin.java
private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES =
        "cross‑profile‑calendar‑packages";
private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL =
        "cross‑profile‑calendar‑packages‑null";

61.7.8 跨资料桌面小组件

允许工作资料小组件在个人桌面显示。

java 复制代码
// ActiveAdmin.java
private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS =
        "cross‑profile‑widget‑providers";
private static final String TAG_PROVIDER = "provider";

策略引擎中定义对应策略。

java 复制代码
// PolicyDefinition.java(引用)
static final PolicyDefinition<...> CROSS_PROFILE_WIDGET_PROVIDER = ...;

61.7.9 跨资料应用包白名单

管理员配置允许跨资料通信的应用包集合。

java 复制代码
// ActiveAdmin.java
private static final String TAG_CROSS_PROFILE_PACKAGES = "cross‑profile‑packages";

61.7.10 打通个人‑工作应用

该属性支持应用同时在两个用户配置文件之间协同工作:

XML 复制代码
<!-- 应用清单文件 -->
<application android:crossProfile="true">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

当应用声明 crossProfile="true" 后,将获得多项能力:

  1. 可以通过清单静态注册的广播接收器接收 CAN_INTERACT_ACROSS_PROFILES_CHANGED 广播(不局限于运行时动态注册)。
  2. 系统可弹窗提示用户,为该应用授予跨配置文件交互权限。
  3. 应用可调用 CrossProfileApps.canInteractAcrossProfiles() 查询自身是否已拥有该权限。

61.7.11 跨资料数据共享模式

跨用户配置文件之间共享数据存在多种实现方案:

模式 管控方 使用场景
Intent 跳转模式 管理员配置过滤器 打开链接、内容分享
ContentProvider 模式 管理员 + 系统管控 联系人、日历查询
直接启动模式 应用 + 用户 + 管理员授权 在个人 / 工作应用实例间切换
剪贴板模式 管理员管控开关 跨资料复制粘贴

61.7.12 跨资料 ContentProvider 访问

系统提供特殊 URI 用于跨资料 ContentProvider 访问;联系人场景使用ContactsContract.Directory

java 复制代码
// DevicePolicyManager.java导入
import android.provider.ContactsContract.Directory;

带有ENTERPRISE标记的 Directory 代表可以对个人资料开放的工作联系人;系统根据管理员的来电显示、联系人搜索策略做访问控制。

61.7.13 跨资料交互完整流程

61.8 合规与安全

61.8.1 安全日志

安全日志采集设备上安全相关事件。SecurityLogMonitor管理日志缓冲区。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/
// SecurityLogMonitor.java
class SecurityLogMonitor implements Runnable {
    /*
     * 该类管理安全日志访问。维护待设备所有者拉取的日志缓冲区。
     * 通过JNI接口从logd读取日志,暂存直到设备所有者拉取,避免日志丢失。
     * 设备所有者读取接口做访问频率限制。
     * 每两小时或者缓冲区达到阈值,通知DPC读取日志。
     */
}

安全日志事件示例:

  • ADB 连接断开与建立
  • 应用进程启动
  • 锁屏解锁、锁屏上锁
  • 存储介质挂载卸载
  • 系统开关机
  • 密码修改、密码错误
  • 证书安装
  • 密钥生成事件

61.8.2 审计日志

除传统安全日志之外,Android 还支持审计日志。

java 复制代码
// PolicyDefinition.java
static PolicyDefinition AUDIT_LOGGING = new PolicyDefinition<>(
        new NoArgsPolicyKey(DevicePolicyIdentifiers.AUDIT_LOGGING_POLICY),
        TRUE_MORE_RESTRICTIVE,
        POLICY_FLAG_GLOBAL_ONLY_POLICY,
        PolicyEnforcerCallbacks::enforceAuditLogging,
        new BooleanPolicySerializer());

客户端DevicePolicyManager提供接口:setAuditLogEnabled(boolean)isAuditLogEnabled(),通过setAuditLogEventCallback()注册流式回调,底层基于IAuditLogEventsCallback AIDL。

java 复制代码
// frameworks/base/core/java/android/app/admin/DevicePolicyManager.java
public void setAuditLogEnabled(boolean enabled) { ... }
public boolean isAuditLogEnabled() { ... }
public void setAuditLogEventCallback(Executor executor,
        Consumer<List> callback) {
    // 将Consumer包装为IAuditLogEventsCallback.Stub
}

传统安全日志为批量缓冲,缓冲区满才通知管理员;审计日志事件产生就推送到回调,适合做近实时合规监控。 服务端对应接口:IDevicePolicyManagersetAuditLogEnabled(String callerPackage, boolean enabled)isAuditLogEnabled(String callerPackage)

61.8.3 网络日志

网络日志采集 DNS 查询、TCP 连接事件。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/
// NetworkLogger.java
final class NetworkLogger {
    private final DevicePolicyManagerService mDpm;
    private final PackageManagerInternal mPm;
    private final AtomicBoolean mIsLoggingEnabled = new AtomicBoolean(false);
    private final int mTargetUserId;
}

NetworkLogger注册INetdEventCallback拦截网络事件。

java 复制代码
// NetworkLogger.java
private final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
    @Override
    public void onDnsEvent(int netId, int eventType, int returnCode,
            String hostname, String[] ipAddresses, int ipAddressesCount,
            long timestamp, int uid) {
        if (!mIsLoggingEnabled.get()) return;
        if (!shouldLogNetworkEvent(uid)) return;
        DnsEvent dnsEvent = new DnsEvent(hostname, ipAddresses,
                ipAddressesCount, mPm.getNameForUid(uid), timestamp);
        sendNetworkEvent(dnsEvent);
    }

    @Override
    public void onConnectEvent(String ipAddr, int port,
            long timestamp, int uid) {
        if (!mIsLoggingEnabled.get()) return;
        // 过滤并组装事件,逻辑同上
    }
};

NetworkLoggingHandler做事件批处理。

61.8.4 设备证明

DPC 借助硬件安全密钥向远端服务器证明设备身份。

java 复制代码
// DevicePolicyManager.java
public static final int ID_TYPE_BASE_INFO = 1; // 厂商基础信息
public static final int ID_TYPE_SERIAL = 2; // 序列号
public static final int ID_TYPE_IMEI = 4; // IMEI
public static final int ID_TYPE_MEID = 8; // MEID
public static final int ID_TYPE_INDIVIDUAL_ATTESTATION = 16; // 设备唯一密钥

设备证明流程:

密钥对附带完整证书链,可校验 Google 根 CA;证书扩展包含 OS 版本、安全补丁版本、启动状态、设备硬件标识。

61.8.5 设备证明证书链结构

证明证书链拥有一套固定结构,由远端服务端完成校验:

证书扩展( OID:1.3.6.1.4.1.11129.2.1.17)包含:

字段 说明
attestationVersion 证明格式版本
attestationSecurityLevel TEE / StrongBox
keymasterVersion KeyMaster/KeyMint 版本
keymasterSecurityLevel 执行安全环境
attestationChallenge 服务器下发随机挑战串
uniqueId 设备唯一 ID(按需开启)
softwareEnforced 软件层面密钥属性
teeEnforced 硬件强制密钥属性

teeEnforced内服务器可校验内容:

  • osVersion Android 系统版本
  • osPatchLevel 安全补丁版本
  • rootOfTrust 校验启动状态、公钥、设备锁状态
  • attestationApplicationId 请求应用签名证书 SHA‑256

61.8.6 证书管理

DPC 可以安装 CA 根证书、客户端证书。

java 复制代码
// DevicePolicyManager.java(委托权限)
public static final String DELEGATION_CERT_INSTALL = "delegation‑cert‑install";
public static final String DELEGATION_CERT_SELECTION = "delegation‑cert‑selection";

CertificateMonitor跟踪管理员安装证书。

复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/
// CertificateMonitor.java
// 监控管理员安装的CA证书

61.8.7 密码合规检查

DPC 可以校验当前密码是否满足策略要求。

java 复制代码
// DevicePolicyManager.java
// isActivePasswordSufficient() ------ 校验是否满足管理员密码策略
// getPasswordComplexity() ------ 返回当前密码复杂度等级

@RequiresPermission(REQUEST_PASSWORD_COMPLEXITY)
public static final String EXTRA_PASSWORD_COMPLEXITY =
        "android.app.extra.PASSWORD_COMPLEXITY";

合规检查 Intent:

java 复制代码
// DevicePolicyManager.java
public static final String ACTION_CHECK_POLICY_COMPLIANCE
        = "android.app.action.CHECK_POLICY_COMPLIANCE";

61.8.8 合规确认

Android12 起,系统要求 DPC 确认设备合规状态。

复制代码
// DeviceAdminReceiver.java
public static final String ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED
        = "android.app.action.COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED";

61.8.9 安全补丁校验

DPC 可以读取设备安全补丁版本,并强制最低补丁等级。DPMS 导入系统更新查询权限。

java 复制代码
// DevicePolicyManagerService.java
import static android.Manifest.permission
        .MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES;

61.8.10 USB 数据信号管控

高安全环境下可以禁用 USB 数据功能。

java 复制代码
// DevicePolicyManagerService.java
import static android.Manifest.permission
        .MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING;

61.8.11 内存标记扩展 MTE

硬件支持的设备,DPC 可以开启 MTE 增强内存安全。

java 复制代码
// DevicePolicyManagerService.java
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MTE;

// PolicyDefinition.java
import static android.app.admin.DevicePolicyIdentifiers.MEMORY_TAGGING_POLICY;

61.8.12 内容保护

DPC 使用三态策略管控内容保护。

java 复制代码
// frameworks/base/core/java/android/app/admin/DevicePolicyManager.java
public static final int CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY = 0;
public static final int CONTENT_PROTECTION_DISABLED = 1;
public static final int CONTENT_PROTECTION_ENABLED = 2;

setContentProtectionPolicygetContentProtectionPolicy走策略引擎,对应标识DevicePolicyIdentifiers.CONTENT_PROTECTION_POLICY;客户端 API 受权限MANAGE_DEVICE_POLICY_CONTENT_PROTECTION管控。 默认值CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY代表策略不干预系统原有设置,区分 "不表态" 与显式关闭。

61.8.13 设备被盗状态

Android 提供设备被盗状态查询 API。

java 复制代码
// DevicePolicyManager.java(标记)
import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;

// DevicePolicyManagerService.java
import static android.Manifest.permission.QUERY_DEVICE_STOLEN_STATE;

61.8.14 设备策略完整状态

DPC 可以读取设备全部策略快照。

java 复制代码
// frameworks/base/core/java/android/app/admin/DevicePolicyState.java
public final class DevicePolicyState implements Parcelable {
    // 设备上全部生效策略完整快照
}

61.8.15 企业专属设备 ID

为保护隐私,Android 生成企业专属稳定设备标识,避免直接暴露硬件序列号。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/
// EnterpriseSpecificIdCalculator.java
// 生成每个企业独立稳定设备ID,不暴露硬件标识符

61.8.16 远程 Bugreport

DPC 可以远程请求抓取设备 bugreport 日志。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/
// RemoteBugreportManager.java
// 处理管理员发起的远程bugreport请求

// DevicePolicyData.java
private static final String TAG_LAST_BUG_REPORT_REQUEST = "last‑bug‑report‑request";

61.8.17 Wi‑Fi SSID 策略

管理员管控设备允许连接的 Wi‑Fi 网络。

java 复制代码
// ActiveAdmin.java引用WifiSsidPolicy
import android.app.admin.WifiSsidPolicy;

// 两种模式
import static android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST;
import static android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST;
  • 白名单模式:仅允许列表内 SSID 连接
  • 黑名单模式:阻止列表内 SSID 连接

61.8.18 私有 DNS 策略

管理员可以配置私有 DNS(DNS‑over‑TLS)。

java 复制代码
// DevicePolicyManager.java
public static final int PRIVATE_DNS_MODE_OFF = 1;
public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2;
public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3;
public static final int PRIVATE_DNS_MODE_UNKNOWN = 0;

public static final int PRIVATE_DNS_SET_NO_ERROR = 0;
public static final int PRIVATE_DNS_SET_ERROR_FAILURE_SETTING = 1;

61.8.19 优先网络服务

面向企业场景实现专属网络路径。

java 复制代码
// ActiveAdmin引用
import android.app.admin.PreferentialNetworkServiceConfig;

管理员配置企业网络偏好,让工作流量使用指定网络切片、企业 APN。

61.8.20 APN 配置(电话模块)

设备所有者管理 APN 接入点。

java 复制代码
// DevicePolicyManagerService.java
import static android.provider.Telephony.Carriers.DPC_URI;
import static android.provider.Telephony.Carriers.ENFORCE_KEY;
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
import static android.provider.Telephony.Carriers.INVALID_APN_ID;

61.8.21 应用包策略

管理员针对特定能力做应用包黑白名单,例如跨资料 Intent 处理。

java 复制代码
// DevicePolicyManager.java引用
import android.app.admin.PackagePolicy;
// PackagePolicy用于针对能力配置包白/黑名单(如跨资料Intent处理)

61.8.22 临时用户

设备所有者可以强制创建临时用户;用户退出登录全部数据自动删除。

java 复制代码
// ActiveAdmin.java
private static final String TAG_FORCE_EPHEMERAL_USERS = "force_ephemeral_users";

适合教育、零售共享设备。

61.8.23 受保护应用包

管理员保护指定应用包,阻止用户卸载、篡改。

java 复制代码
// DevicePolicyData.java
private static final String TAG_PROTECTED_PACKAGES = "protected‑packages";

61.8.24 绕过角色资格校验

企业场景,管理员可以给不满足常规条件的应用授予系统角色。

复制代码
// DevicePolicyData.java
private static final String TAG_BYPASS_ROLE_QUALIFICATIONS =
        "bypass‑role‑qualifications";

61.8.25 第二道锁屏

管理员开启第二层锁屏界面。

java 复制代码
// DevicePolicyData.java
private static final String TAG_SECONDARY_LOCK_SCREEN = "secondary‑lock‑screen";

DPC 可以实现额外合规校验锁屏,在系统锁屏之前或之后展示。

61.8.26 应用豁免

管理员将指定应用豁免部分系统限制。

java 复制代码
// DevicePolicyManager.java
public static final int EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION = ...;
public static final int EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS = ...;
public static final int EXEMPT_FROM_HIBERNATION = ...;
public static final int EXEMPT_FROM_POWER_RESTRICTIONS = ...;
public static final int EXEMPT_FROM_SUSPENSION = ...;

保证 VPN 客户端、管理代理这类关键企业应用,不受电池优化、应用冻结策略影响。

61.8.27 完整合规架构

61.8.28 高级保护模式作为系统管理员

Android16 引入,Android17 扩展高级保护模式 AAPM。设置界面一个总开关,一键开启整套加固安全配置。

架构关键点:AAPM 不属于 DPMS,也不是 DPC 应用;它是独立系统服务,以系统管理员身份复用 61.2 介绍的策略引擎驱动设备策略。服务与客户端 API 不在 devicepolicy 包下。

java 复制代码
// frameworks/base/services/core/java/com/android/server/security/advancedprotection/
// AdvancedProtectionService.java 系统服务

// frameworks/base/core/java/android/security/advancedprotection/
// AdvancedProtectionManager.java @SystemService客户端

public static final String ADVANCED_PROTECTION_SYSTEM_ENTITY =
        "android.security.advancedprotection";

每一项加固能力是services/core/java/com/android/server/security/advancedprotection/features/目录下的 FeatureHook,ID 在AdvancedProtectionManager定义。

java 复制代码
// AdvancedProtectionManager.java
public static final int FEATURE_ID_DISALLOW_CELLULAR_2G = 0;
public static final int FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = 1;
public static final int FEATURE_ID_DISALLOW_USB = 2;
public static final int FEATURE_ID_DISALLOW_WEP = 3;
public static final int FEATURE_ID_ENABLE_MTE = 4;
public static final int FEATURE_ID_DISALLOW_INSECURE_WIFI_AUTOJOIN = 5;
public static final int FEATURE_ID_RESTRICT_NON_TOOL_A11Y_SERVICES = 6;

与设备策略模块桥接示例(MTE):

java 复制代码
// MemoryTaggingExtensionHook.java
mDevicePolicyManager.setMtePolicy(ADVANCED_PROTECTION_SYSTEM_ENTITY, mtePolicy);

调用的是系统重载接口setMtePolicy(String systemEntity, int policy),底层调用IDevicePolicyManager.setMtePolicyBySystem(systemEntity, policy);DPMS 内部通过createSystemEnforcingAdmin(ADVANCED_PROTECTION_SYSTEM_ENTITY)记录该系统管理员的策略。 AAPM 的策略和 DPC 策略进入同一策略引擎,按正常规则做解析,不会互相强制覆盖。

Android17 内置 4 个 Hook 实现,均继承AdvancedProtectionHookMemoryTaggingExtensionHookDisallowInstallUnknownSourcesAdvancedProtectionHookDisallowCellular2GAdvancedProtectionHookUsbDataAdvancedProtectionHook

前三者走 DPMS,两种调用路径:

  1. MTE 调用setMtePolicy(ADVANCED_PROTECTION_SYSTEM_ENTITY, ...)
  2. 禁止未知来源、禁止 2G,调用addUserRestrictionGlobally(systemEntity, ...)写入 DPMS,作为系统管理员的用户限制,和 DPC 策略共存。

USB 处理特殊:不使用用户限制 。开启时调用setUsbDataSignalIfPossible(...),受锁屏状态控制;设备锁屏时关闭 USB 数据通路,解锁恢复,底层调用IUsbManagerInternal.enableUsbDataSignal;不存在DISALLOW_USB这个 UserManager 常量,FEATURE_ID_DISALLOW_USB仅作为 Hook 标识。

重要规则:DPC 不能静默撤销 AAPM 开启的保护。用户主动开启高级保护模式后,部分特性使用系统管理员写入的严格值;企业管理员配置 "允许" 无法覆盖用户主动开启的加固项。

61.8.29 多用户托管设备配置

无头系统用户模式,配置流程不能假设设备所有者运行在人机交互用户。Android17 新增多用户托管设备配置路径专门解决该场景。

DevicePolicyManager新增客户端接口:

java 复制代码
// frameworks/base/core/java/android/app/admin/DevicePolicyManager.java
public static final String ACTION_PROVISION_MULTIUSER_MANAGED_DEVICE =
        "android.app.admin.action.PROVISION_MULTIUSER_MANAGED_DEVICE";
public static final String ACTION_PROVISION_MULTIUSER_MANAGED_USER =
        "android.app.admin.action.PROVISION_MULTIUSER_MANAGED_USER";

// 新配置返回码
public static final int RESULT_MULTIUSER_MANAGED_DEVICE_PROVISIONED = 124;

// 启动配置状态机(系统API)
public void startMultiuserManagedDeviceProvisioning();

参数 Parcelable:MultiuserManagedDeviceProvisioningParamsMultiuserManagedUserProvisioningParams,对应 AIDL 序列化类;结果回调MultiuserDeviceProvisioningCompletion.java。旧版MultiUserDeviceProvisioningParams标记废弃。

概念上做分离:"配置整机设备" 和 "在设备上配置托管用户";设备所有者绑定无头 system user,再创建各个托管用户,和HEADLESS_DEVICE_OWNER_MODE_SINGLE_USERHEADLESS_DEVICE_OWNER_MODE_AFFILIATED配套使用。

61.8.30 策略版本与迁移

策略引擎会把解析完成状态持久化磁盘,因此磁盘 XML 做版本管理,OTA 升级时自动迁移旧版本策略。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/
// DevicePolicyManagerService.java
static final int DPMS_VERSION = 6;

// 开机阶段
upgrader.upgradePolicy(DPMS_VERSION);

PolicyVersionUpgrader.upgradePolicy(int)按版本号逐次升级,从磁盘版本迭代到DPMS_VERSION,改写策略字段,更新存储版本。例如版本 5 升级到 6 会修改布尔策略默认值,加载旧文件使用旧默认,保存升级后版本。

单独PolicyMigrator.migrateV1PoliciesToDevicePolicyEngineLocked()完成 Android13 及更早版本旧策略一次性迁移,把旧版输入法管控、账号禁用等策略转换为新引擎格式。

迁移逻辑运行 DPMS 锁内,在系统开机阶段执行;大版本 OTA 第一次开机时会执行策略数据迁移,之后设备管理 API 才完全就绪。

61.8.31 企业 RCS 消息归档

Android17 新增,企业管控接收 RCS 归档消息的应用。RcsArchivalAppTracker跟踪每个用户唯一归档包名。

java 复制代码
// frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/
// RcsArchivalAppTracker.java
/* 跟踪企业RCS归档应用 */
// KEY_ARCHIVAL_PACKAGE = "messages_archival"
// 映射:userId → 当前被授予归档权限的包名

托管设备环境,可以把 RCS 归档能力授权给指定应用用于合规留存;更换应用时可以干净回收权限,避免权限泛滥。

61.9 动手实践

本节提供实操练习,基于 AOSP 源码与 Android 开发工具探索设备策略框架。

61.9.1 练习 1:查看 DPMS 源码

查看设备策略管理器服务的代码规模:

复制代码
# 统计主服务文件行数
wc -l frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
# 预期:Android 17代码树中约24000行

# 统计devicepolicy包下所有Java文件数量
find frameworks/base/services/devicepolicy/ -name "*.java" | wc -l

# 列出全部策略定义常量
grep -n "static PolicyDefinition" \
    frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java

# 统计DPMS引入的MANAGE_DEVICE_POLICY_*权限数量
grep "MANAGE_DEVICE_POLICY_" \
    frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java \
    | wc -l

61.9.2 练习 2:探索 DPM 客户端 API

复制代码
# 统计DevicePolicyManager中的公开API方法数量
grep -c "public.*(" \
    frameworks/base/core/java/android/app/admin/DevicePolicyManager.java

# 列出所有委托作用域
grep "DELEGATION_" \
    frameworks/base/core/java/android/app/admin/DevicePolicyManager.java \
    | grep "public static final"

# 查找所有PASSWORD_COMPLEXITY相关常量
grep "PASSWORD_COMPLEXITY_" \
    frameworks/base/core/java/android/app/admin/DevicePolicyManager.java

# 列出所有与设备预置相关的Action
grep "ACTION_PROVISION" \
    frameworks/base/core/java/android/app/admin/DevicePolicyManager.java

61.9.3 练习 3:构建极简设备管理应用

创建一个极简设备管理应用,理解管理组件的生命周期。

步骤 1:创建管理接收器

java 复制代码
// MyDeviceAdminReceiver.java
package com.example.myadmin;

import android.app.admin.DeviceAdminReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.util.Log;

public class MyDeviceAdminReceiver extends DeviceAdminReceiver {
    private static final String TAG = "MyDeviceAdmin";

    @Override
    public void onEnabled(Context context, Intent intent) {
        Log.i(TAG, "Device admin enabled");
    }

    @Override
    public void onDisabled(Context context, Intent intent) {
        Log.i(TAG, "Device admin disabled");
    }

    @Override
    public void onPasswordChanged(Context context, Intent intent,
            UserHandle user) {
        Log.i(TAG, "Password changed for user: " + user);
    }

    @Override
    public void onPasswordFailed(Context context, Intent intent,
            UserHandle user) {
        Log.i(TAG, "Password failed for user: " + user);
    }

    @Override
    public void onPasswordSucceeded(Context context, Intent intent,
            UserHandle user) {
        Log.i(TAG, "Password succeeded for user: " + user);
    }
}

步骤 2:创建管理策略 XML

XML 复制代码
<!-- res/xml/device_admin.xml -->
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-policies>
        <limit-password />
        <watch-login />
        <force-lock />
        <wipe-data />
        <disable-camera />
    </uses-policies>
</device-admin>

步骤 3:在清单文件中声明

XML 复制代码
<receiver
    android:name=".MyDeviceAdminReceiver"
    android:exported="true"
    android:permission="android.permission.BIND_DEVICE_ADMIN">
    <meta-data
        android:name="android.app.device_admin"
        android:resource="@xml/device_admin" />
    <intent-filter>
        <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
    </intent-filter>
</receiver>

步骤 4:创建管理 Activity

java 复制代码
// AdminActivity.java
package com.example.myadmin;

import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;

public class AdminActivity extends Activity {
    private static final String TAG = "MyDeviceAdmin";
    private DevicePolicyManager mDPM;
    private ComponentName mAdminComponent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mDPM = (DevicePolicyManager)
            getSystemService(Context.DEVICE_POLICY_SERVICE);
        mAdminComponent = new ComponentName(this,
            MyDeviceAdminReceiver.class);

        if (mDPM.isAdminActive(mAdminComponent)) {
            Log.i(TAG, "Admin is active");
            logPolicyState();
        } else {
            Log.i(TAG, "Admin is NOT active");
        }
    }

    private void logPolicyState() {
        Log.i(TAG, "Password sufficient: "
            + mDPM.isActivePasswordSufficient());
        Log.i(TAG, "Encryption status: "
            + mDPM.getStorageEncryptionStatus());
        Log.i(TAG, "Camera disabled: "
            + mDPM.getCameraDisabled(mAdminComponent));
    }
}

61.9.4 练习 4:配置测试设备所有者

在模拟器上使用 ADB 体验设备所有者功能:

复制代码
# 启动全新模拟器(恢复出厂状态)
emulator -avd Pixel_8_API_35 -wipe-data

# 完成初始化设置后,设置设备所有者
# (必须在用户完成设置流程之前执行)
adb shell dpm set-device-owner com.example.myadmin/.MyDeviceAdminReceiver

# 验证设备所有者已设置
adb shell dumpsys device_policy

# 查看设备策略XML文件
adb shell cat /data/system/device_owner_2.xml

# 查看每个用户的策略数据
adb shell cat /data/system/users/0/device_policies.xml

61.9.5 练习 5:创建并查看工作资料

复制代码
# 在已安装DPC测试应用的模拟器上执行:

# 列出当前所有用户
adb shell pm list users

# 创建受管理资料(使用TestDPC或同类应用)
# 创建完成后,再次列出用户
adb shell pm list users
# 预期输出:UserInfo{10:Work profile:...}

# 查看工作资料的策略数据
adb shell cat /data/system/users/10/device_policies.xml

# 列出工作资料内安装的应用包
adb shell pm list packages --user 10

# 查看跨资料Intent过滤器校验信息
adb shell dumpsys package intent-filter-verifications

# 切换工作模式(使受管理资料不可用)
adb shell am broadcast -a android.intent.action.MANAGED_PROFILE_UNAVAILABLE \
    --user 0

61.9.6 练习 6:探索托管配置

复制代码
# 为工作资料内的应用设置应用限制
adb shell content call \
    --uri content://com.android.providers.settings \
    --method GET_system \
    --arg device_provisioned

# 导出设备策略状态
adb shell dumpsys device_policy | grep -A 20 "Active Admins"

# 在策略导出结果中查找应用限制
adb shell dumpsys device_policy | grep -A 10 "application-restrictions"

61.9.7 练习 7:分析策略引擎的解析逻辑

研究策略引擎如何处理冲突策略:

复制代码
# 在源码中查找所有策略解析机制
grep -r "class.*Resolution\|MostRestrictive\|TopPriority\|PackageSetUnion\|MostRecent" \
    frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/ \
    --include="*.java" -l

# 列出全部策略定义
grep "static.*PolicyDefinition" \
    frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java

# 查找策略执行器回调中的CompletableFuture
grep "static.*CompletableFuture" \
    frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java \
    | head -20

61.9.8 练习 8:安全与网络日志

复制代码
# 开启安全日志(需要设备所有者权限)
adb shell dpm set-device-owner com.example.myadmin/.MyDeviceAdminReceiver
# 代码中调用:
# dpm.setSecurityLoggingEnabled(admin, true);

# 查看安全日志状态
adb shell dumpsys device_policy | grep -A 5 "Security Log"

# 查看网络日志状态
adb shell dumpsys device_policy | grep -A 5 "Network Log"

# 查看SecurityLogMonitor实现代码行数
wc -l frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java

# 查看NetworkLogger实现代码行数
wc -l frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java

61.9.9 练习 9:跟踪一次策略调用的完整调用栈

跟踪setCameraDisabled()调用,从客户端 API 一路到策略执行:

复制代码
# 1. 定位客户端方法
grep -n "setCameraDisabled" \
    frameworks/base/core/java/android/app/admin/DevicePolicyManager.java | head -5

# 2. 定位AIDL接口方法
grep -n "setCameraDisabled" \
    frameworks/base/core/java/android/app/admin/IDevicePolicyManager.aidl

# 3. 定位服务端实现
grep -n "setCameraDisabled" \
    frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java | head -5

# 4. 查找该策略的解析定义
grep -n "CAMERA\|camera.*disable" \
    frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java

# 5. 查找执行器回调
grep -n "camera\|Camera" \
    frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java | head -5

# 6. 查找CameraService读取该策略的位置
grep -rn "DevicePolicyCache\|isCameraDisabled\|CAMERA_DISABLED" \
    frameworks/base/services/core/ --include="*.java" | head -10

61.9.10 练习 10:构建具备托管配置的完整 DPC

开发一个演示托管配置功能的 DPC。

步骤 1:被托管应用的限制规则 schema

XML 复制代码
<!-- managed-app/res/xml/app_restrictions.xml -->
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
    <restriction
        android:key="server_url"
        android:restrictionType="string"
        android:title="Server URL"
        android:description="The server URL for syncing data"
        android:defaultValue="https://default.example.com" />
    <restriction
        android:key="auto_sync"
        android:restrictionType="bool"
        android:title="Auto Sync"
        android:description="Enable automatic data synchronization"
        android:defaultValue="true" />
    <restriction
        android:key="sync_interval_minutes"
        android:restrictionType="integer"
        android:title="Sync Interval"
        android:description="Minutes between automatic syncs"
        android:defaultValue="30" />
    <restriction
        android:key="allowed_file_types"
        android:restrictionType="multi-select"
        android:title="Allowed File Types"
        android:description=""
        android:entries="@array/file_types"
        android:entryValues="@array/file_type_values" />
</restrictions>

步骤 2:DPC 推送配置

java 复制代码
// DPC:向受托管应用推送限制配置
public void configureApp(ComponentName admin) {
    DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);

    Bundle restrictions = new Bundle();
    restrictions.putString("server_url", "https://corp.example.com");
    restrictions.putBoolean("auto_sync", true);
    restrictions.putInt("sync_interval_minutes", 15);
    restrictions.putStringArray("allowed_file_types",
        new String[]{"pdf", "docx", "xlsx"});

    dpm.setApplicationRestrictions(admin,
        "com.example.managedapp", restrictions);
}

步骤 3:受托管应用读取限制配置

java 复制代码
// 受托管应用:读取限制配置
public void loadConfig() {
    RestrictionsManager rm = getSystemService(RestrictionsManager.class);
    Bundle restrictions = rm.getApplicationRestrictions();

    String serverUrl = restrictions.getString("server_url",
        "https://default.example.com");
    boolean autoSync = restrictions.getBoolean("auto_sync", true);
    int syncInterval = restrictions.getInt("sync_interval_minutes", 30);

    Log.i(TAG, "Server: " + serverUrl);
    Log.i(TAG, "Auto sync: " + autoSync);
    Log.i(TAG, "Interval: " + syncInterval + " min");
}

// 注册监听配置变更
private void registerForChanges() {
    IntentFilter filter = new IntentFilter(
        Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
    registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            loadConfig(); // 重新加载限制配置
        }
    }, filter);
}

61.9.11 练习 11:探索所有权转移 API

设备所有者与资料所有者权限可以在不同 DPC 应用之间转移。

java 复制代码
// DevicePolicyManager API
public void transferOwnership(ComponentName admin,
    ComponentName target, PersistableBundle bundle) { ... }
bash 复制代码
# 查找transferOwnership的服务端实现
grep -n "transferOwnership" \
    frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java \
    | head -5

# 查看转移元数据管理器
cat frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java \
    | head -30

TransferOwnershipMetadataManager在转移过程中跟踪管理员类型:

java 复制代码
// TransferOwnershipMetadataManager.java
static final String ADMIN_TYPE_DEVICE_OWNER = "device-owner";
static final String ADMIN_TYPE_PROFILE_OWNER = "profile-owner";

61.9.12 练习 12:ADB 设备策略命令

DPMS 提供 Shell 命令接口:

复制代码
# 查看全部dpm支持命令
adb shell dpm help

# 核心命令:
adb shell dpm set-device-owner <component>
adb shell dpm set-profile-owner <component>
adb shell dpm remove-active-admin <component>
adb shell dpm set-active-admin <component>

# DevicePolicyManagerService同时支持dumpsys
adb shell dumpsys device_policy

# dumpsys输出关键模块:
# - Device Owner(设备所有者)
# - Profile Owner (per user)(各用户的资料所有者)
# - Active Admins (per user)(各用户的活跃管理员)
# - Policy states(策略状态)
# - Affiliation IDs(关联ID)
# - Security/Network logging status(安全/网络日志状态)

Shell 命令处理实现文件:

frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java

61.9.13 练习 13:跨资料通信

测试跨资料 Intent 解析:

复制代码
# 确认工作资料已存在
adb shell pm list users

# 查看当前跨资料Intent过滤器
adb shell dumpsys device_policy | grep -A 30 "cross-profile"

# 测试跨资料Intent解析
# 在个人资料中打开网页链接
adb shell am start -a android.intent.action.VIEW \
    -d "https://example.com" --user 0

# 查看Intent是否在工作资料中被解析处理
adb shell dumpsys activity activities | grep -B 2 -A 5 "example.com"

代码配置跨资料 Intent 过滤器:

java 复制代码
// 在DPC中,为网页浏览添加跨资料过滤器
DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
ComponentName admin = new ComponentName(this,
    MyDeviceAdminReceiver.class);

IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_VIEW);
filter.addCategory(Intent.CATEGORY_BROWSABLE);
filter.addDataScheme("https");

// 允许个人应用在工作浏览器中打开链接
dpm.addCrossProfileIntentFilter(admin, filter,
    DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);

// 允许工作应用在个人浏览器中打开链接
dpm.addCrossProfileIntentFilter(admin, filter,
    DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);

61.9.14 练习 14:实现密码复杂度强制策略

java 复制代码
// DPC:强制密码复杂度
public void enforcePasswordPolicy(ComponentName admin) {
    DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);

    // 现代方式:使用密码复杂度
    // (要求targetSdk >= 31)
    dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);

    // 检查当前密码是否满足要求
    boolean sufficient = dpm.isActivePasswordSufficient();
    Log.i(TAG, "Password sufficient: " + sufficient);

    if (!sufficient) {
        // 拉起修改密码界面
        Intent intent = new Intent(
            DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
        intent.putExtra(DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY,
            PASSWORD_COMPLEXITY_HIGH);
        startActivity(intent);
    }

    // 设置密码错误多少次后自动擦除数据
    dpm.setMaximumFailedPasswordsForWipe(admin, 10);

    // 设置无操作自动锁屏超时(5分钟)
    dpm.setMaximumTimeToLock(admin, 5 * 60 * 1000);
}

61.9.15 练习 15:设备认证校验

java 复制代码
// DPC:生成带认证信息的密钥对
public void performDeviceAttestation(ComponentName admin) {
    DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);

    try {
        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
                "attestation-key",
                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
            .setDigests(KeyProperties.DIGEST_SHA256)
            .setAttestationChallenge(
                generateServerChallenge()) // 来自服务端的nonce
            .build();

        AttestedKeyPair keyPair = dpm.generateKeyPair(admin, "EC", spec,
            DevicePolicyManager.ID_TYPE_SERIAL
                | DevicePolicyManager.ID_TYPE_IMEI);

        if (keyPair != null) {
            List<Certificate> chain = keyPair.getAttestationRecord();
            Log.i(TAG, "Attestation chain length: " + chain.size());

            // 将证书链发送给服务端完成校验
            sendAttestationToServer(chain);
        }
    } catch (Exception e) {
        Log.e(TAG, "Attestation failed", e);
    }
}

private byte[] generateServerChallenge() {
    // 生产环境中该值由EMM服务端下发
    byte[] challenge = new byte[32];
    new java.security.SecureRandom().nextBytes(challenge);
    return challenge;
}

61.9.16 练习 16:端到端工作资料 + 托管配置

本练习完整串联资料创建、应用安装、托管配置全流程。

java 复制代码
// 步骤1:创建工作资料
public void setupWorkProfile() {
    DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);

    ManagedProfileProvisioningParams params =
        new ManagedProfileProvisioningParams.Builder(
            new ComponentName("com.example.dpc",
                "com.example.dpc.MyDeviceAdminReceiver"),
            "Corporate IT")
        .setProfileName("Work")
        .setOrganizationOwnedProvisioning(false) // BYOD模式
        .build();

    try {
        UserHandle workProfile =
            dpm.createAndProvisionManagedProfile(params);
        Log.i(TAG, "Work profile created: " + workProfile);
        configureWorkProfile(workProfile);
    } catch (ProvisioningException e) {
        Log.e(TAG, "Provisioning failed", e);
    }
}

// 步骤2:配置工作资料
private void configureWorkProfile(UserHandle workProfile) {
    DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
    ComponentName admin = new ComponentName("com.example.dpc",
        "com.example.dpc.MyDeviceAdminReceiver");

    // 设置工作资料密码策略
    dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM);

    // 配置跨资料联系人
    // (允许个人电话应用读取工作联系人)
    // dpm.setCrossProfileContactsSearchDisabled(admin, false);

    // 向工作邮箱应用推送托管配置
    Bundle emailConfig = new Bundle();
    emailConfig.putString("server", "mail.corp.example.com");
    emailConfig.putInt("port", 993);
    emailConfig.putBoolean("use_ssl", true);
    dpm.setApplicationRestrictions(admin,
        "com.example.workmail", emailConfig);

    // 设置组织名称
    dpm.setOrganizationName(admin, "Example Corp");
}

61.9.17 核心源码文件参考

如需进一步研究,下面是关键源码文件:

文件 用途
frameworks/base/core/java/android/app/admin/DevicePolicyManager.java 客户端 API(18700 + 行)
frameworks/base/core/java/android/app/admin/DeviceAdminReceiver.java 管理接收器回调接口
frameworks/base/core/java/android/app/admin/DeviceAdminInfo.java 管理元数据解析
frameworks/base/core/java/android/app/admin/IDevicePolicyManager.aidl Binder 跨进程接口
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java 服务实现(25000 + 行)
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java 多管理员策略解析引擎
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java 策略定义与解析机制
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java 每个管理员的策略状态
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java 设备所有者 / 资料所有者管理
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java 按用户存储的策略持久化数据
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java 策略引擎中的管理员身份对象
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java 安全事件日志
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java 网络事件日志
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java CA 证书监控
frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java COPE 模式个人应用冻结
frameworks/base/core/java/android/app/admin/ManagedProfileProvisioningParams.java 工作资料预置参数
frameworks/base/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java 全设备管理预置参数
frameworks/base/core/java/android/content/pm/CrossProfileApps.java 跨资料交互 API
frameworks/base/core/java/android/app/admin/FactoryResetProtectionPolicy.java FRP 恢复出厂保护策略

小结

Android 企业级框架是 AOSP 内部最复杂子系统之一,仅核心服务与客户端 API 代码量就超过 40000 行。下面是关键架构要点:

管理模式(完全托管设备、工作资料 / BYOD、COPE)提供从 IT 完全管控到最大限度保护用户隐私的不同管控能力。模式在设备预置阶段就确定,直接决定可以执行哪些策略。

DevicePolicyManagerService 是策略的核心调度中心。代码约 24000 行,属于 AOSP 体量最大系统服务之一。它校验调用方权限,交由策略引擎完成策略解析,将版本化状态持久化存储为 XML(Android17 中 DPMS_VERSION=6),并通知其他子系统策略发生变更。

Android14 引入 DevicePolicyEngine,正式支持多管理员策略解析。Android17 内置 7 种解析策略:MostRestrictiveTopPriorityMostRecentLeastRecentPackageSetUnionListUnionFlagUnion;同时定义 4 种管理员权限类别:企业管理员、传统设备管理员、role 角色管理员、新增的 system 系统管理员。这套机制让 DPC 管理应用、基于角色的管理员、旧版设备管理员、高级保护模式这类可信系统服务可以共存并协同处理同一套策略。

工作资料依托 Android 多用户机制,为工作数据提供一套密码学隔离的容器。跨资料通信严格受控,由 Intent 过滤器、ContentProvider 访问控制、CrossProfileApps API 共同管控。

安全基础设施包含安全日志(来自 logd 事件)、网络日志(通过 netd 采集 DNS、TCP 事件)、硬件级设备认证、证书管理、合规校验;以上全部能力用于向企业提供可核验的设备状态证明。

权限模型已经演进:从需要指定管理员 ComponentName,过渡到细粒度MANAGE_DEVICE_POLICY_*系列权限,支持非 DPC 应用通过 Role 角色、委托机制参与设备管理。

相关推荐
2501_915106321 小时前
Flutter iOS混淆打包详细教程与步骤
android·flutter·ios·小程序·uni-app·iphone·webview
三少爷的鞋1 小时前
Android 架构演进:从生命周期事件到结构化任务
android
一笑的小酒馆9 小时前
AndroidKMP之网络请求
android
千里马-horse11 小时前
第 60 章 模拟器架构
架构·aosp
aqi0012 小时前
鸿蒙版本的JSBridge兼容与安卓配套的H5啦
android·华为·harmonyos·鸿蒙·移动应用
事圆则缓13 小时前
MVVM 的理解与设计
android·android jetpack
码农阿豪14 小时前
我把 OpenClaw 装进旧安卓手机后,又折腾了 3 天:飞书、ADB、SSH 全部打通
android·智能手机·飞书
有什么事15 小时前
把安卓搬上云端:云手机系统改造与内核优化拆解
android·智能手机
方白羽15 小时前
性能分析:Android Studio Profiler
android·性能优化·app