本指南中的功能描述了您可以在 设备政策控制器 (DPC) 应用中实现的安全管理功能。本文档包含代码示例,您还可以使用 Test DPC 应用作为 Android 企业功能的示例代码来源。
DPC 应用可以在个人设备上以资料所有者模式运行,或在完全托管设备上以设备所有者模式运行。下表指出了 DPC 在资料所有者模式或设备所有者模式下运行时可用的功能
功能 | 资料所有者 | 设备所有者 |
---|---|---|
停用应用访问 | ✓ | ✓ |
阻止来自未知来源的应用 | ✓ | ✓ |
限制 Google Play 中的账号 | ✓ | ✓ |
启用企业版恢复出厂设置保护 | ✓ | |
监控企业进程日志和远程 bug 报告 | ✓ | |
授予和移除客户端证书访问权限 | ✓ | ✓ |
安全密码重置 | ✓ | ✓ |
工作资料安全挑战 | ✓ |
停用应用访问
对于希望阻止员工在一天中的特定时间或一周中的特定几天在其 Android 设备上玩游戏或观看 YouTube 的组织,DPC 可以暂时停用对应用的访问权限。
要停用应用访问权限,以设备所有者或资料所有者模式运行的 DPC 会配置 setPackagesSuspended()
,然后选定的应用将表现为已停用(Google 启动器会将应用显示为灰色)。当用户点按该应用时,他们会看到一个系统对话框,提示该应用已暂停。
应用暂停后,它无法启动 activity,并且针对该软件包的通知会被抑制。暂停的软件包不会出现在概览屏幕中,它们无法显示对话框(包括浮动通知和 Snackbar),也无法播放音频或震动设备。
启动器可以通过调用 isPackageSuspended()
方法来判断应用是否已暂停。有关如何配置应用暂停的详细信息,请参阅 setPackagesSuspended
。
阻止来自未知来源的应用
未从 Google Play(或其他受信任的应用商店)安装的应用称为来自未知来源的应用。当人们安装这些应用时,设备和数据面临的风险可能会增加。
为防止有人安装来自未知来源的应用,完全托管设备和工作资料的管理员组件可以添加 DISALLOW_INSTALL_UNKNOWN_SOURCES
用户限制。
工作资料设备范围限制
当工作资料的管理员添加 DISALLOW_INSTALL_UNKNOWN_SOURCES
时,该限制仅适用于工作资料。但是,工作资料的管理员可以通过为 Google Play 设置托管配置来施加设备范围的限制。当已安装的 Google Play 应用版本为 80812500 或更高时,Android 8.0(或更高版本)中提供设备范围的限制。
要将应用安装限制为 Google Play,请按照以下步骤操作
- 为 Google Play 软件包
com.android.vending
设置托管配置包。 - 在该包中,为
verify_apps:device_wide_unknown_source_block
键放置一个布尔值。 - 添加
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 中的帐号,请执行以下操作
- 为 Google Play 软件包
com.android.vending
设置托管配置包。 - 在该包中,将逗号分隔的电子邮件地址作为字符串值放入
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 可以在完全托管设备的整个生命周期内配置这些帐号。
- IT 管理员可以使用 People API 中的
people.get
方法,并使用特殊值me
。这会检索登录帐号的userId
。userID
以people/[userId]
的形式作为整数字符串在resourceName
键中返回。新创建的帐号可能在 72 小时内无法用于恢复出厂设置目的。 - 您可能还希望启用一个或多个 IT 管理员在恢复出厂设置后解锁设备。让每个 IT 管理员登录其 Google 帐号,并按照步骤 1 操作,然后与您分享他们的
userId
,以便您可以在下一步中将这些userIds
添加到列表中。 - DPC 使用
setFactoryResetProtectionPolicy()
设置相应的应用限制,以设置可以在恢复出厂设置后配置设备的userId
列表。 - 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 Level 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 Level 30 引入的 setFactoryResetProtectionPolicy()
的设备,您的 DPC 可以使用 setApplicationRestrictions
在 com.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);
监控企业进程日志和远程 bug 报告
在您的 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()
回调。
远程请求 bug 报告
以设备所有者模式运行的 DPC 可以远程请求仅有一个用户或关联用户的设备上的 bug 报告。bug 报告会捕获请求 bug 报告时的设备活动,但可能还会包含前几个小时的活动,具体取决于 logcat 缓冲区刷新频率。
要远程请求 bug 报告,DPC 会调用 requestBugreport()
- 如果用户接受共享 bug 报告,DPC 会使用
onBugreportShared()
接收 bug 报告。 - 如果用户拒绝共享 bug 报告,DPC 会使用
onBugreportSharingDeclined()
收到共享请求被拒绝的消息。 - 如果 bug 报告失败,DPC 会看到
onBugreportFailed()
,其中包含BUGREPORT_FAILURE_FAILED_COMPLETING
或BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE
。
授予和移除客户端证书访问权限
如果以资料所有者或设备所有者模式运行的 DPC 授予第三方应用管理证书的能力,则该应用无需用户干预即可自行访问其安装的证书。要安装资料中所有应用都可以访问的证书,请使用 installKeyPair()
。
有关要配置的参数,请参阅 installKeyPair()
。此功能与现有的证书管理 API 协同工作。
部署场景
没有 installKeyPair()
方法
- 用户每次要授予证书访问权限时,都需要点按证书名称并点按允许。
- 用户在安装证书时会看到提示,并且必须为证书命名。
使用 installKeyPair()
方法
- 用户每次要授予证书访问权限时,无需点按允许。
- 用户无法重命名证书。
- 管理员拥有更多控制权,他们可以阻止不应访问特定证书的应用的证书。
移除客户端证书
授予客户端证书访问权限后,要远程移除通过 installKeyPair()
安装的客户端证书,请调用 removeKeyPair()
。
以设备所有者模式或资料所有者模式运行的 DPC,或委派的证书安装程序可以调用 removeKeyPair()
。这会移除在给定私钥别名下安装的证书和私钥对。
部署场景
如果组织正在迁移到更安全的客户端证书形式,请使用此功能。如果管理员推出新证书,并且其分发需要大量时间,则管理员可以在迁移完成后撤销已弃用的证书。
安全密码重置
您的 DPC 可以通过使用预注册的安全令牌授权更改来重置用户的密码。设备所有者和资料所有者可以分别调用安全密码重置 API 来更改设备和工作资料的密码。安全密码重置用以下改进取代了 resetPassword()
如果您的 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 可以要求用户为在工作资料中运行的应用指定安全挑战。当用户尝试打开任何工作应用时,系统会显示安全挑战。如果用户成功完成安全挑战,系统会解锁工作资料并在必要时解密它。
工作资料安全挑战的工作原理
- 如果 DPC 发送
ACTION_SET_NEW_PASSWORD
intent,系统会提示用户设置安全挑战。 - DPC 还可以发送
ACTION_SET_NEW_PARENT_PROFILE_PASSWORD
intent 来提示用户设置设备锁。
DPC 可以为工作挑战设置与设备其他密码不同的密码政策。例如,设备挑战响应的最小长度可以与针对其他密码要求的长度不同。DPC 使用常用的 DevicePolicyManager
方法设置挑战政策,例如 setPasswordQuality()
和 setPasswordMinimumLength()
。
注意事项
- DPC 可以重置工作资料上的密码,但无法重置设备(个人)密码。如果用户选择将工作密码和个人密码设置为相同,那么在工作资料上调用
resetPassword()
将导致仅在工作资料上重置密码,并且密码将与设备锁屏密码不同。 - DPC 可以使用
setOrganizationColor()
和setOrganizationName()
自定义工作挑战的凭据屏幕。 - 设备管理员不能使用
resetPassword()
来清除密码或更改已设置的密码。设备管理员仍然可以设置密码,但仅当设备没有密码、PIN 或图案时。
有关其他信息,请参阅 getParentProfileInstance()
和 DevicePolicyManager
下的参考文档。