安全

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

DPC 应用可以在个人设备上的配置文件所有者模式或在完全托管设备上的设备所有者模式下运行。 此表指示当 DPC 在 配置文件所有者模式或设备所有者模式 下运行时可用的功能。

功能 配置文件所有者 设备所有者
禁用对应用的访问
阻止来自未知来源的应用
限制 Google Play 中的帐户
启用企业出厂重置保护
监控企业进程日志和远程错误报告
授予对客户端证书的访问权限并删除访问权限
安全密码重置
工作配置文件安全挑战

禁用对应用的访问

对于希望阻止员工在一天中的某些时间或一周中的某些日期在他们的 Android 设备上玩游戏或观看 YouTube 的组织,DPC 可以暂时禁用对应用的访问。

要禁用对应用的访问,在设备所有者或配置文件所有者模式下运行的 DPC 会配置 setPackagesSuspended(),然后选定的应用的行为就像已被禁用一样(Google 启动器会将应用灰显)。 当用户点击应用时,他们会看到一个系统对话框,提示该应用已暂停。

当应用处于暂停状态时,它无法启动活动,并且会抑制对该软件包的通知。 暂停的软件包不会显示在 概览屏幕 中,它们无法显示对话框(包括吐司和 Snackbars),并且它们无法播放音频或震动设备。

启动器可以通过调用 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. 在包中,为 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 = "[email protected],[email protected]"

// 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 = "[email protected],[email protected]";

// 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。这将检索登录帐户的 userIduserIDpeople/[userId] 的形式作为整数字符串返回在 resourceName 密钥中。新创建的帐户可能在 72 小时内无法用于出厂重置。
  2. 您可能还希望启用一个或多个 IT 管理员以在出厂重置后解锁设备。让这些 IT 管理员中的每一个都登录到他们的 Google 帐户,并按照步骤 1 操作,并将他们的 userId 与您共享,以便您可以将这些 userIds 添加到下一步中的列表中。
  3. DPC 使用 setFactoryResetProtectionPolicy() 设置适当的应用限制,以设置可以预配出厂重置设备的 userId 列表。
  4. DPC 通过将广播 com.google.android.gms.auth.FRP_CONFIG_CHANGED 作为显式意图发送来启用可以在出厂重置后预配设备的帐户,以防止由于后台限制而被丢弃。

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 控制台中,管理员可以使用企业流程日志和远程错误报告来监控完全托管的设备。

记录企业设备活动

在设备所有者模式下运行的 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 可以远程请求仅有一个用户或 关联用户 的用户设备的错误报告。错误报告会捕获请求错误报告的确切时刻的设备活动,但根据 logcat 缓冲区刷新的频率,也可能包含前几个小时的活动。

要远程请求错误报告,DPC 会调用 requestBugreport()

授予对客户端证书的访问权限并删除访问权限

如果在配置文件所有者或设备所有者模式下运行的 DPC 授予第三方应用管理证书的能力,则该应用可以在没有用户干预的情况下授予自己对其安装的证书的访问权限。要安装配置文件中所有应用都可以访问的证书,请使用 installKeyPair()

有关要配置的参数,请参阅 installKeyPair()。此功能与现有的 API 结合使用,用于管理证书。

部署方案

没有 installKeyPair() 方法

  • 每次用户想要授予证书访问权限时,都需要点击证书名称并点击允许
  • 安装证书时,用户会看到提示,并且必须为证书命名。

使用 installKeyPair() 方法

  • 用户无需每次都点击允许来授予证书访问权限。
  • 用户无法重命名证书。
  • 管理员拥有更多控制权,因为他们可以阻止不应访问特定证书的应用的证书。

删除客户端证书

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

在设备拥有者模式或配置文件拥有者模式下运行的 DPC,或委托的证书安装程序可以调用removeKeyPair()。这将移除在给定私钥别名下安装的证书和私钥对。

部署方案

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

安全密码重置

您的 DPC 可以通过使用预注册的安全令牌授权更改来重置用户的密码。设备拥有者和配置文件拥有者可以调用安全密码重置 API 来分别更改设备和工作配置文件的密码。安全密码重置用以下改进替换了resetPassword()

  • 在使用基于文件的加密的设备上,您的 DPC 可以在用户重新启动后解锁设备或配置文件之前重置密码。
  • 密码重置后,Android 密钥库会保留用户验证密钥

如果您的 DPC 构建目标为 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 下的参考文档。