安全性

本指南中的功能介绍了您可以在设备政策控制器 (DPC) 应用中实现的安全管理功能。本文档包含代码示例,您还可以将 Test DPC 应用用作 Android 企业功能示例代码的来源。

DPC 应用可以在个人设备上以资料所有者模式运行,在完全受管设备上以设备所有者模式运行。下表说明了当 DPC 在资料所有者模式或设备所有者模式下运行时,可以使用哪些功能:

功能 个人资料所有者 设备所有者
禁止访问应用
屏蔽来自未知来源的应用
限制 Google Play 中的帐号
启用企业恢复出厂设置保护
监控企业进程日志和远程 bug 报告
授予对客户端证书的访问权限和移除对客户端证书的访问权限
安全重置密码
工作资料安全性验证

停用应用访问权限

如果组织希望禁止员工在一天中的特定时段或一周中的某几天通过 Android 设备玩游戏或观看 YouTube,DPC 可以暂时停用对应用的访问权限。

如需停用对应用的访问权限,在设备所有者模式或资料所有者模式下运行的 DPC 会配置 setPackagesSuspended(),然后所选应用会表现为已停用(Google 启动器会将应用灰显)。当用户点按应用时,系统会显示一个系统对话框,说明应用已暂停。

应用被暂停后,将无法启动 activity,并且会抑制发送到软件包的通知。已暂停的软件包不会显示在概览屏幕中,不能显示对话框(包括消息框和信息提示控件),无法播放音频或振动设备。

启动器可以通过调用 isPackageSuspended() 方法来查明应用是否已暂停。如需详细了解如何配置应用暂停功能,请参阅 setPackagesSuspended

屏蔽来自未知来源的应用

不是从 Google Play(或其他可信应用商店)安装的应用称为来自未知来源的应用。如果用户安装这些应用,设备和数据可能会增加风险。

为了防止有人从未知来源安装应用,完全受管设备和工作资料的管理员组件可以添加 DISALLOW_INSTALL_UNKNOWN_SOURCES 用户限制。

工作资料设备级限制

当工作资料的管理员添加 DISALLOW_INSTALL_UNKNOWN_SOURCES 时,此限制仅适用于工作资料。不过,工作资料管理员可以通过为 Google Play 设置托管配置来设定设备级限制。当安装的 Google Play 应用版本为 80812500 或更高版本时,Android 8.0(或更高版本)中开始实施设备级限制。

如需限制在 Google Play 中安装应用,请按以下步骤操作:

  1. 为 Google Play 软件包 com.android.vending 设置托管配置包。
  2. 在 Bundle 中,为 verify_apps:device_wide_unknown_source_block 键设置一个布尔值。
  3. 添加 ENSURE_VERIFY_APPS 用户限制。

以下示例展示了如何检查 Google Play 是否支持此设置并将其值设置为 true

Kotlin

internal val DEVICE_WIDE_UNKNOWN_SOURCES = "verify_apps:device_wide_unknown_source_block"
internal val GOOGLE_PLAY_APK = "com.android.vending"

// ...

// Add the setting to Google Play's existing managed config. Supported in
// Google Play version 80812500 or higher--older versions ignore unsupported
// settings.
val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
var existingConfig = dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK)
val newConfig = Bundle(existingConfig)
newConfig.putBoolean(DEVICE_WIDE_UNKNOWN_SOURCES, true)
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig)

// Make sure that Google Play Protect verifies apps.
dpm.addUserRestriction(adminName, UserManager.ENSURE_VERIFY_APPS)
dpm.addUserRestriction(adminName, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)

Java

static final String DEVICE_WIDE_UNKNOWN_SOURCES =
    "verify_apps:device_wide_unknown_source_block";
static final String GOOGLE_PLAY_APK = "com.android.vending";

// ...


// Add the setting to Google Play's existing managed config. Supported in
// Google Play version 80812500 or higher--older versions ignore unsupported
// settings.
DevicePolicyManager dpm =
    (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
Bundle existingConfig =
    dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK);
Bundle newConfig = new Bundle(existingConfig);
newConfig.putBoolean(DEVICE_WIDE_UNKNOWN_SOURCES, true);
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig);

// Make sure that Google Play Protect verifies apps.
dpm.addUserRestriction(adminName, UserManager.ENSURE_VERIFY_APPS);
dpm.addUserRestriction(adminName, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);

系统设置中的界面仍处于活动状态,但系统会阻止应用安装。此限制会影响日后的安装 - 之前安装的应用仍会保留在设备上。设备用户可以继续使用 Android 调试桥 (adb) 将应用安装到个人资料中。

如需详细了解未知来源,请参阅替代分发选项

在 Google Play 中限制账号

有时,组织可能希望允许用户添加个人 Google 帐号(例如在 Gmail 中阅读邮件),但不希望个人帐号安装应用。您的 DPC 可以设置用户可以在 Google Play 中使用的帐号列表。

全托管式设备或工作资料的管理员组件可以通过为 Google Play 设置托管配置来限制帐号。已安装的 Google Play 应用版本为 80970100 或更高版本时,才适用帐号限制。

如需限制 Google Play 中的帐号,请执行以下操作:

  1. 为 Google Play 软件包 com.android.vending 设置托管配置包。
  2. 在捆绑包中,将以英文逗号分隔的电子邮件地址作为 allowed_accounts 键的字符串值。

以下示例展示了如何限制帐号:

Kotlin

internal val ALLOWED_ACCOUNTS = "allowed_accounts"
internal val GOOGLE_PLAY_APK = "com.android.vending"

// ...

// Limit Google Play to one work and one personal account. Use
// a comma-separated list of account email addresses (usernames).
val googleAccounts = "ali@gmail.com,ali.connors@example.com"

// Supported in Google Play version 80970100 or higher.
val existingConfig = dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK)
val newConfig = Bundle(existingConfig)
newConfig.putString(ALLOWED_ACCOUNTS, googleAccounts)
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig)

Java

static final String ALLOWED_ACCOUNTS = "allowed_accounts";
static final String GOOGLE_PLAY_APK = "com.android.vending";

// ...


// Limit Google Play to one work and one personal account. Use
// a comma-separated list of account email addresses (usernames).
String googleAccounts = "ali@gmail.com,ali.connors@example.com";

// Supported in Google Play version 80970100 or higher.
Bundle existingConfig =
    dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK);
Bundle newConfig = new Bundle(existingConfig);
newConfig.putString(ALLOWED_ACCOUNTS, googleAccounts);
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig);

如需将 Google Play 限制为仅使用工作帐号,请在 DPC 知道帐号的电子邮件地址后,立即将 allowed_accounts 设置为单个受管理的帐号。空字符串会阻止用户在 Google Play 中使用任何帐号。

启用企业恢复出厂设置保护

借助企业恢复出厂设置保护,组织可以指定哪些 Google 帐号可以配置恢复出厂设置的设备。

消费者恢复出厂设置保护旨在防止设备被盗。在允许任何人在未经授权的恢复出厂设置(例如使用 EMM)后配置设备之前,设置向导会要求用户针对设备个人资料之前包含的所有 Google 帐号进行身份验证。

在企业环境中,恢复出厂设置是员工离职时管理员工设备的重要工具。但是,如果组织不知道员工的帐号凭据,则恢复出厂设置保护功能可能会阻止组织向其他员工发放设备。

在恢复出厂设置后控制配置

在设备所有者模式下运行时,DPC 可以使用 setFactoryResetProtectionPolicy() 控制哪些帐号有权在恢复出厂设置后配置设备。如果此配置设为 null 或设为空列表,则获授权在恢复出厂设置后配置设备的帐号即为设备个人资料中的帐号。

DPC 可以在完全受管设备的整个生命周期内配置这些帐号。

  1. IT 管理员可以将 People API 中的 people.get 方法与特殊值 me 结合使用。这将检索已登录帐号的 userIduserIDresourceName 键中以整数字符串的形式返回 people/[userId]。新创建的帐号可能在 72 小时内无法恢复出厂设置。
  2. 您可能还需要允许一个或多个 IT 管理员在恢复出厂设置后解锁设备。让每位 IT 管理员登录其 Google 帐号,并按照第 1 步操作并与您共享他们的 userId,这样您就可以在下一步将这些 userIds 添加到列表中。
  3. DPC 使用 setFactoryResetProtectionPolicy() 设置适当的应用限制,以设置可配置恢复出厂设置设备的 userId 列表。
  4. DPC 启用后,可通过将广播 com.google.android.gms.auth.FRP_CONFIG_CHANGED 作为显式 intent 来配置设备的帐号,以防止因后台限制而被丢弃。

Kotlin

const val ACTION_FRP_CONFIG_CHANGED =
    "com.google.android.gms.auth.FRP_CONFIG_CHANGED"
const val GMSCORE_PACKAGE = "com.google.android.gms"

// ...

// List of userId that can provision a factory reset device.
// You can use the value returned calling people/me endpoint.
val accountIds = listOf("000000000000000000000")

dpm.setFactoryResetProtectionPolicy(
    adminName,
    FactoryResetProtectionPolicy.Builder()
        .setFactoryResetProtectionAccounts(accountIds)
        .setFactoryResetProtectionEnabled(true)
        .build()
)

val frpChangedIntent = Intent(ACTION_FRP_CONFIG_CHANGED)

frpChangedIntent.setPackage(GMSCORE_PACKAGE)
context.sendBroadcast(frpChangedIntent)

Java

static final String ACTION_FRP_CONFIG_CHANGED =
    "com.google.android.gms.auth.FRP_CONFIG_CHANGED";
static final String GMSCORE_PACKAGE = "com.google.android.gms";

// ...

// List of userId that can provision a factory reset device.
// You can use the value returned calling people/me endpoint.
List<String> accountIds = new ArrayList<String>();
accountIds.add("000000000000000000000");

dpm.setFactoryResetProtectionPolicy(
    adminName,
    new FactoryResetProtectionPolicy.Builder()
        .setFactoryResetProtectionAccounts(accountIds)
        .setFactoryResetProtectionEnabled(true)
        .build());

Intent frpChangedIntent = new Intent(ACTION_FRP_CONFIG_CHANGED);

frpChangedIntent.setPackage(GMSCORE_PACKAGE);
context.sendBroadcast(frpChangedIntent);

旧版

对于无法使用 API 级别 30 中引入的 setFactoryResetProtectionPolicy() 的设备,您的 DPC 可以使用 setApplicationRestrictions 将所选帐号添加到 com.google.android.gms 软件包的 factoryResetProtectionAdmin 托管配置中。

Kotlin

const val GOOGLE_PLAY_APK = "com.android.vending"
const val FACTORY_RESET_PROTECTION_ADMIN = "factoryResetProtectionAdmin"
const val DISABLE_FACTORY_RESET_PROTECTION_ADMIN = "disableFactoryResetProtectionAdmin"
const val GMSCORE_PACKAGE = "com.google.android.gms"

// ...

val existingConfig = dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK)
val newConfig = Bundle(existingConfig)
newConfig.putBoolean(DISABLE_FACTORY_RESET_PROTECTION_ADMIN, false)
newConfig.putString(FACTORY_RESET_PROTECTION_ADMIN, googleAccounts)
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig)

val frpChangedIntent = Intent(ACTION_FRP_CONFIG_CHANGED)

frpChangedIntent.setPackage(GMSCORE_PACKAGE)
context.sendBroadcast(frpChangedIntent)

Java

static final String GOOGLE_PLAY_APK = "com.android.vending";
static final String FACTORY_RESET_PROTECTION_ADMIN = "factoryResetProtectionAdmin";
static final String DISABLE_FACTORY_RESET_PROTECTION_ADMIN = "disableFactoryResetProtectionAdmin";
static final String GMSCORE_PACKAGE = "com.google.android.gms";

// ...

Bundle existingConfig =
        dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK);
Bundle newConfig = new Bundle(existingConfig);
newConfig.putBoolean(DISABLE_FACTORY_RESET_PROTECTION_ADMIN, false);
newConfig.putStringArray(FACTORY_RESET_PROTECTION_ADMIN,
        accountIds.toArray(new String[accountIds.size()]));
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig);

Intent frpChangedIntent = new Intent(ACTION_FRP_CONFIG_CHANGED);

frpChangedIntent.setPackage(GMSCORE_PACKAGE);
context.sendBroadcast(frpChangedIntent);

停用企业恢复出厂设置保护

如需停用恢复出厂设置保护,您的 DPC 可以使用 setFactoryResetProtectionPolicy() 传递值 null

Kotlin

const val ACTION_FRP_CONFIG_CHANGED =
    "com.google.android.gms.auth.FRP_CONFIG_CHANGED"
const val GMSCORE_PACKAGE = "com.google.android.gms"

// ...

dpm.setFactoryResetProtectionPolicy(adminName, null)

val frpChangedIntent = Intent(ACTION_FRP_CONFIG_CHANGED)

frpChangedIntent.setPackage(GMSCORE_PACKAGE)
context.sendBroadcast(frpChangedIntent)

Java

static final String ACTION_FRP_CONFIG_CHANGED =
    "com.google.android.gms.auth.FRP_CONFIG_CHANGED";
static final String GMSCORE_PACKAGE = "com.google.android.gms";

// ...

dpm.setFactoryResetProtectionPolicy(adminName, null);

Intent frpChangedIntent = new Intent(ACTION_FRP_CONFIG_CHANGED);

frpChangedIntent.setPackage(GMSCORE_PACKAGE);
context.sendBroadcast(frpChangedIntent);

旧版

对于无法使用 API 级别 30 中引入的 setFactoryResetProtectionPolicy() 的设备,您的 DPC 可以使用 setApplicationRestrictionscom.google.android.gms 软件包的 disableFactoryResetProtectionAdmin 托管配置中设置 true 键值对。

Kotlin

const val GOOGLE_PLAY_APK = "com.android.vending"
const val FACTORY_RESET_PROTECTION_ADMIN = "factoryResetProtectionAdmin"
const val DISABLE_FACTORY_RESET_PROTECTION_ADMIN = "disableFactoryResetProtectionAdmin"
const val GMSCORE_PACKAGE = "com.google.android.gms"

// ...

val existingConfig = dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK)
val newConfig = Bundle(existingConfig)
newConfig.putBoolean(DISABLE_FACTORY_RESET_PROTECTION_ADMIN, true)

dpm.setApplicationRestrictions(
    adminName, GOOGLE_PLAY_SERVICES_PACKAGE, restrictions
)

val frpChangedIntent = Intent(ACTION_FRP_CONFIG_CHANGED)

frpChangedIntent.setPackage(GMSCORE_PACKAGE)
context.sendBroadcast(frpChangedIntent)

Java

static final String GOOGLE_PLAY_APK = "com.android.vending";
static final String FACTORY_RESET_PROTECTION_ADMIN = "factoryResetProtectionAdmin";
static final String DISABLE_FACTORY_RESET_PROTECTION_ADMIN = "disableFactoryResetProtectionAdmin";
static final String GMSCORE_PACKAGE = "com.google.android.gms";

// ...

Bundle existingConfig =
        dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK);
Bundle newConfig = new Bundle(existingConfig);
newConfig.putBoolean(DISABLE_FACTORY_RESET_PROTECTION_ADMIN, true);

dpm.setApplicationRestrictions(
    adminName, GOOGLE_PLAY_SERVICES_PACKAGE, restrictions);

Intent frpChangedIntent = new Intent(ACTION_FRP_CONFIG_CHANGED);

frpChangedIntent.setPackage(GMSCORE_PACKAGE);
context.sendBroadcast(frpChangedIntent);

监控企业进程日志和远程错误报告

在 EMM 控制台中,管理员可以使用企业进程日志和远程 bug 报告监控全代管式设备。

记录企业设备活动

在设备所有者模式下运行的 DPC 可通过远程跟踪设备活动(包括应用启动、Android 调试桥 (adb) 活动和屏幕解锁)来识别可疑活动。进程日志不需要用户同意。

如需启用或停用日志记录,DPC 会调用 setSecurityLoggingEnabled()

当有新一批日志可用时,DeviceAdminReceiver 会收到 onSecurityLogsAvailable() 回调。为了(收到回调后)检索日志,DPC 会调用 retrieveSecurityLogs()

DPC 还可以调用 retrievePreRebootSecurityLogs() 来获取在上一个重新启动周期中生成的安全日志。这是从上次设备重新启动到其上一次重新启动之间的时间间隔。不支持 retrieveSecurityLogs() 的设备会返回 null。如果您的应用同时使用 retrievePreRebootSecurityLogs()retrieveSecurityLogs() 检索日志,则您需要检查是否存在重复条目。
注意:此功能仅记录设备上存在单个用户或关联用户的完全受管设备上的活动。此功能会记录设备级活动,因此不适用于个人设备。

此设置在安全事件后审核中非常有用,因为它会记录以下类型的操作:

  • 每次应用刚刚启动时。这有助于确定是否存在以遭到入侵的应用开头的恶意软件。
  • 在设备上失败的解锁尝试。这可以识别短时间内是否有多次失败的解锁尝试。
  • 当用户使用 USB 线将设备连接到计算机时,可能有害的 adb 命令。

如需详细了解如何读取日志,请参阅 SecurityLog

在开发和测试期间,您可以强制系统向 DPC 提供任何现有安全日志,而不必等待整批日志。在 Android 9.0(API 级别 28)或更高版本中,请在终端运行以下 Android 调试桥 (adb) 命令:

adb shell dpm force-security-logs

系统会限制您使用该工具的频率,并报告终端输出中任何有意减速的情况。如果有可用的日志,您的 DPC 会收到 onSecurityLogsAvailable() 回调。

远程请求错误报告

在设备所有者模式下运行的 DPC 可以针对只有一个用户或关联用户的用户设备远程请求 bug 报告。bug 报告会捕获请求 bug 报告那一刻的设备活动,但也可能包含过去几小时内的活动,具体取决于 logcat 缓冲区刷新的频率。

如需远程请求 bug 报告,DPC 会调用 requestBugreport()

授予和移除对客户端证书的访问权限

如果在资料所有者模式或设备所有者模式下运行的 DPC 授予了第三方应用管理证书的权限,则该应用可以授予自身对其安装的证书的访问权限,而无需用户干预。如需安装配置文件中所有应用均可访问的证书,请使用 installKeyPair()

如需了解要配置哪些参数,请参阅 installKeyPair()。此功能可与现有 API 结合使用,以管理证书。

部署场景

如果不使用 installKeyPair() 方法:

  • 用户每次想要授予对证书的访问权限时,都需要点按证书的名称,然后点按允许
  • 用户在安装证书时会看到一条提示,并且必须给证书命名。

使用 installKeyPair() 方法:

  • 用户无需在每次想要授予对证书的访问权限时点按允许
  • 用户无法重命名证书。
  • 管理员可以更好地控制不应有权访问特定证书的应用的证书。

移除客户端证书

在向客户端证书授予访问权限后,如需远程移除通过 installKeyPair() 安装的客户端证书,请调用 removeKeyPair()

在设备所有者模式或资料所有者模式下运行的 DPC 或委托证书安装程序可以调用 removeKeyPair()。这会移除安装在给定私钥别名下的证书和私钥对。

部署场景

如果组织正在迁移到更安全的客户端证书形式,请使用此功能。如果管理员发布了新证书,并且分发证书需要花费大量时间,则可以在迁移完成后撤消已弃用的证书。

已重置安全密码

您的 DPC 可以使用预注册的安全令牌授权更改,以重置用户密码。设备所有者和资料所有者可以调用安全密码重置 API 来分别更改设备和工作资料的密码。安全密码重置功能取代了 resetPassword(),并做出了以下改进:

如果您的 DPC build 以 Android 8.0(API 级别 26)或更高版本为目标平台,您应使用安全密码重置。在以 Android 8.0 或更高版本为目标平台的 DPC 中,调用 resetPassword() 会抛出 SecurityException,因此您可能需要更新 DPC。

设置和激活令牌

设备政策控制器 (DPC) 需要先设置并激活令牌,然后才能重置密码。由于您的 DPC 可能无法直接使用令牌,因此您可以在 IT 管理员可能需要使用令牌之前设置令牌。

密码重置令牌是一个加密强随机值,长度至少为 32 字节。为每个设备和个人资料创建令牌,请勿重复使用或共享生成的令牌。

我们建议在服务器上存储令牌或解密加密令牌的方式。如果您将令牌本地存储在凭据加密存储空间中,则在用户解锁设备或个人资料之前,DPC 无法重置密码。如果您将令牌本地存储在设备加密存储空间中(该存储空间会遭到破解),攻击者可能会使用该令牌获取对工作资料或主要用户的访问权限。

您可以在 DPC 中生成新的令牌,或从服务器提取令牌。以下示例展示了 DPC 自行生成令牌并将其报告给服务器:

Kotlin

val token = ByteArray(32)

// Generate a new token
val random = SecureRandom()
random.nextBytes(token)

// Set the token to use at a later date
val success: Boolean
success = dpm.setResetPasswordToken(DeviceAdminReceiver.getComponentName(context), token)

// Activate the token and update success variable...

// Store the token on a server
if (success) {
 sendTokenToServer(token)
}

Java

byte token[] = new byte[32]; // Minimum size token accepted

// Generate a new token
SecureRandom random = new SecureRandom();
random.nextBytes(token);

// Set the token to use at a later date
boolean success;
success = dpm.setResetPasswordToken(DeviceAdminReceiver.getComponentName(getContext()), token);

// Activate the token and update success variable ...

// Store the token on a server
if (success) {
 sendTokenToServer(token);
}

在大多数情况下,您的 DPC 需要在设置令牌后将其激活。但是,如果用户没有锁定屏幕密码,系统会直接激活令牌。如需激活令牌,请要求用户确认其凭据。您的 DPC 可以调用 KeyguardManager 方法 createConfirmDeviceCredentialIntent() 来获取用于启动确认的 Intent。在界面中向设备用户说明您要求他们进行身份验证的原因。以下代码段展示了如何在 DPC 中激活令牌:

Kotlin

// In your DPC, you'll need to localize the user prompt
val ACTIVATE_TOKEN_PROMPT = "Use your credentials to enable remote password reset"
val ACTIVATE_TOKEN_REQUEST = 1

// Create or fetch a token and set it in setResetPasswordToken() ...
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
val confirmIntent = keyguardManager.createConfirmDeviceCredentialIntent(null, ACTIVATE_TOKEN_PROMPT)

if (confirmIntent != null) {
 startActivityForResult(confirmIntent, ACTIVATE_TOKEN_REQUEST)
 // Check your onActivityResult() callback for RESULT_OK
} else {
 // Null means the user doesn't have a lock screen so the token is already active.
 // Call isResetPasswordTokenActive() if you need to confirm
}

Java

// In your DPC, you'll need to localize the user prompt
static final String ACTIVATE_TOKEN_PROMPT =
 "Use your credentials to enable remote password reset";
static final int ACTIVATE_TOKEN_REQUEST = 1;

// Create or fetch a token and set it in setResetPasswordToken() ...

KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
Intent confirmIntent = keyguardManager.createConfirmDeviceCredentialIntent(
  null, ACTIVATE_TOKEN_PROMPT);

if (confirmIntent != null) {
 startActivityForResult(confirmIntent, ACTIVATE_TOKEN_REQUEST);
 // Check your onActivityResult() callback for RESULT_OK
} else {
 // Null means the user doesn't have a lock screen so the token is already active.
 // Call isResetPasswordTokenActive() if you need to confirm
}

您需要在设备重新启动之前激活您的 DPC 设置的令牌。Android 会将未激活的令牌存储在内存中,并且在设备重新启动后不会保留该令牌。如果用户在激活令牌之前重新启动设备,您的 DPC 可以重新设置相同的令牌或生成新令牌。

您的 DPC 可以通过调用 isResetPasswordTokenActive() 并检查结果为 true 来确认令牌是否有效。

在 DPC 设置并激活令牌后,该令牌将一直有效,直到 DPC 删除或替换令牌(或设备恢复出厂设置)。该令牌独立于密码,并且不受用户更改或清除密码的影响。

删除令牌

您可以调用 clearResetPasswordToken() 来删除 DPC 之前设置的令牌。您可能需要撤消已破解的令牌,或者您可能需要移除重置密码的功能。以下示例展示了如何在 DPC 中执行此操作:

Kotlin

val dpm = getDpm()
val admin = DeviceAdminReceiver.getComponentName(requireActivity())

// Clear the token
if (!dpm.clearResetPasswordToken(admin)) {
 // Report the failure and possibly try later ...
}

Java

DevicePolicyManager dpm = getDpm();
ComponentName admin = DeviceAdminReceiver.getComponentName(getActivity());

// Clear the token
if (!dpm.clearResetPasswordToken(admin)) {
 // Report the failure and possibly try later ...
}

重置密码

当 IT 管理员需要重置密码时,请调用 resetPasswordWithToken() 并传递您的 DPC 设置和激活的令牌:

Kotlin

val token: ByteArray = getTokenFromServer()
val newPassword = "password"

try {
 val result: Boolean = dpm.resetPasswordWithToken(
 DeviceAdminReceiver.getComponentName(requireContext()),
 newPassword,
 token,
 0
 )

 if (result) {
 // The password is now 'password'
 } else {
 // Using 'password' doesn't meet password restrictions
 }
} catch (e: IllegalStateException) {
 // The token doesn't match the one set earlier.
}

Java

byte token[] = getTokenFromServer();
String newPassword = "password";

try {
 boolean result = dpm.resetPasswordWithToken(
  DeviceAdminReceiver.getComponentName(getContext()), newPassword, token, 0);

 if (result) {
 // The password is now 'password'
 } else {
 // Using `password` doesn't meet password restrictions
 }
} catch (IllegalStateException e) {
 // The token doesn't match the one set earlier.
}

如果新密码不符合以下限制条件,则调用 resetPasswordWithToken() 会返回 false,并且密码不会更改:

  • 字符数满足任意密码长度下限限制。调用 getPasswordMinimumLength(),了解 IT 管理员是否设置了时长限制。
  • 密码中字符的范围和复杂程度符合组合限制条件。调用 getPasswordQuality(),了解 IT 管理员是否设置了组合限制条件。

如果密码质量限制不要求设置密码,您可以将 null 或空字符串传递给 resetPasswordWithToken() 以移除密码。

工作资料安全性挑战

在资料所有者模式下运行的 DPC 可以要求用户为在工作资料中运行的应用指定安全质询。当用户尝试打开任何工作应用时,系统会显示安全质询。如果用户成功通过安全验证,系统会解锁工作资料并将其解密(如有必要)。

工作资料安全性验证的运作方式

  1. 如果 DPC 发送 ACTION_SET_NEW_PASSWORD intent,系统会提示用户设置安全性验证。
  2. DPC 还可以发送 ACTION_SET_NEW_PARENT_PROFILE_PASSWORD intent 来提示用户设置设备锁定。

DPC 可以为工作挑战设置密码政策,与其他设备密码政策不同。例如,设备质询响应的最小长度可以与其他密码所需的长度不同。DPC 使用常规 DevicePolicyManager 方法(例如 setPasswordQuality()setPasswordMinimumLength())设置验证政策。

注意事项

  • DPC 可以重置工作资料的密码,但无法重置设备(个人)密码。如果用户选择将工作密码和个人密码设置为相同的密码,则工作资料上的 resetPassword() 会导致仅重置工作资料的密码,并且密码与设备锁定屏幕的密码不同。
  • DPC 可以使用 setOrganizationColor()setOrganizationName() 自定义工作挑战的凭据屏幕。
  • 设备管理员无法使用 resetPassword() 清除或更改已设置的密码。设备管理员仍然可以设置密码,但只能在设备没有密码、PIN 码或图案时这样做。

如需了解详情,请参阅 getParentProfileInstance()DevicePolicyManager 下的参考文档。