本指南中的功能介绍了您可以在设备政策控制器 (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 上架,请按以下步骤操作:
- 为 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 可以使用 factoryResetProtectionAdmin
控制哪些帐号有权在恢复出厂设置后配置设备。如果此托管配置不存在、设为 null
或设为空列表,则在恢复出厂设置后有权配置设备的帐号是当前在设备个人资料中的帐号。
DPC 可以在全代管式设备的整个生命周期内配置这些帐号。
- DPC 会(手动或以编程方式)获取可在恢复出厂设置后配置设备的帐号 ID。
- DPC 使用特殊值“me”作为
userId
来指示经过身份验证的用户。ID 以整数字符串的形式返回。新创建的帐号可能在 72 小时后无法恢复出厂设置。 - DPC 使用
DevicePolicyManager.setApplicationRestrictions()
设置适当的应用限制,以设置settings
软件包中的键值对的值并指明限制适用于软件包:
键名 -factoryResetProtectionAdmin
。
键值对 - 一个字符串,包含一个可配置恢复出厂设置设备的帐号 ID,或一个包含包含多个帐号 ID 的字符串数组。
软件包名称 -com.google.android.gms
. - DPC 通过发送广播
com.google.android.gms.auth.FRP_CONFIG_CHANGED
启用可在恢复出厂设置后配置设备的帐号。
停用恢复出厂设置保护
如需停用恢复出厂设置保护,DPC 必须将 disableFactoryResetProtectionAdmin
的键值对设为 true
。只需未设置此托管配置,不会停用恢复出厂设置保护功能。
- DPC 使用
DevicePolicyManager.setApplicationRestrictions()
设置适当的应用限制,以在settings
软件包中设置键值对的值并指明限制适用于软件包:
键名 -disableFactoryResetProtectionAdmin
。
键值对 -true
或false
的布尔值(默认值)。
软件包名称 -com.google.android.gms
. - DPC 通过发送广播
com.google.android.gms.auth.FRP_CONFIG_CHANGED
通知 Google Play 服务此变更。
监控企业进程日志和远程错误报告
在 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()
:
- 如果用户接受共享 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 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 可以要求用户为工作资料中运行的应用指定安全质询。当用户尝试打开任何工作应用时,系统会显示安全质询。如果用户成功完成安全验证,系统会解锁工作资料并将其解密(如有必要)。
工作资料安全验证的工作原理
- 如果 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
下的参考文档。