- 自定义 MyAdminReceiver,继承自 DeviceAdminReceiver
- xml目录下建立device-admin规则
- 注册清单文件
代码
自定义 Receiver
class MyAdminReceiver : DeviceAdminReceiver() {
override fun onEnabled(context: Context?, intent: Intent?) {
super.onEnabled(context, intent)
Logs.d("MyAdminReceiver.onEnabled() called with: context = [$context], intent = [$intent]")
}
override fun onDisabled(context: Context?, intent: Intent?) {
super.onDisabled(context, intent)
Logs.d("MyAdminReceiver.onDisabled() called with: context = [$context], intent = [$intent]")
}
override fun onPasswordChanged(context: Context?, intent: Intent?, user: UserHandle?) {
super.onPasswordChanged(context, intent, user)
Logs.d("MyAdminReceiver.onPasswordChanged() called with: context = [$context], intent = [$intent], user = [$user]")
}
override fun onPasswordFailed(context: Context?, intent: Intent?, user: UserHandle?) {
super.onPasswordFailed(context, intent, user)
Logs.d("MyAdminReceiver.onPasswordFailed() called with: context = [$context], intent = [$intent], user = [$user]")
}
override fun onPasswordSucceeded(context: Context?, intent: Intent?, user: UserHandle?) {
super.onPasswordSucceeded(context, intent, user)
Logs.d("MyAdminReceiver.onPasswordSucceeded() called with: context = [$context], intent = [$intent], user = [$user]")
}
override fun onPasswordExpiring(context: Context?, intent: Intent?, user: UserHandle?) {
super.onPasswordExpiring(context, intent, user)
Logs.d("MyAdminReceiver.onPasswordExpiring() called with: context = [$context], intent = [$intent], user = [$user]")
}
override fun onBugreportFailed(context: Context?, intent: Intent?, failureCode: Int) {
super.onBugreportFailed(context, intent, failureCode)
Logs.d("MyAdminReceiver.onBugreportFailed() called with: context = [$context], intent = [$intent], failureCode = [$failureCode]")
}
override fun onBugreportShared(context: Context?, intent: Intent?, bugreportHash: String?) {
super.onBugreportShared(context, intent, bugreportHash)
Logs.d("MyAdminReceiver.onBugreportShared() called with: context = [$context], intent = [$intent], bugreportHash = [$bugreportHash]")
}
override fun onBugreportSharingDeclined(context: Context?, intent: Intent?) {
super.onBugreportSharingDeclined(context, intent)
Logs.d("MyAdminReceiver.onBugreportSharingDeclined() called with: context = [$context], intent = [$intent]")
}
override fun onChoosePrivateKeyAlias(context: Context?, intent: Intent?, uid: Int, uri: Uri?, alias: String?): String? {
Logs.d("MyAdminReceiver.onChoosePrivateKeyAlias() called with: context = [$context], intent = [$intent], uid = [$uid], uri = [$uri], alias = [$alias]")
return super.onChoosePrivateKeyAlias(context, intent, uid, uri, alias)
}
override fun onDisableRequested(context: Context?, intent: Intent?): CharSequence? {
Logs.d("MyAdminReceiver.onDisableRequested() called with: context = [$context], intent = [$intent]")
return super.onDisableRequested(context, intent)
}
override fun onLockTaskModeEntering(context: Context?, intent: Intent?, pkg: String?) {
Logs.d("MyAdminReceiver.onLockTaskModeEntering() called with: context = [$context], intent = [$intent], pkg = [$pkg]")
super.onLockTaskModeEntering(context, intent, pkg)
}
override fun onLockTaskModeExiting(context: Context?, intent: Intent?) {
super.onLockTaskModeExiting(context, intent)
Logs.d("MyAdminReceiver.onLockTaskModeExiting() called with: context = [$context], intent = [$intent]")
}
override fun onNetworkLogsAvailable(context: Context?, intent: Intent?, batchToken: Long, networkLogsCount: Int) {
super.onNetworkLogsAvailable(context, intent, batchToken, networkLogsCount)
Logs.d("MyAdminReceiver.onNetworkLogsAvailable() called with: context = [$context], intent = [$intent], batchToken = [$batchToken], networkLogsCount = [$networkLogsCount]")
}
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
Logs.d("MyAdminReceiver.onReceive() called with: context = [$context], intent = [$intent]")
}
}
自定义 my_device_admin.xml
xml目录下创建文件 my_device_admin.xml
<?xml version="1.0" encoding="utf-8"?>
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
<uses-policies>
<limit-password /> 设置密码规则(数字、图案、字母等)
<watch-login /> 监听登录回调(失败、成功等)
<force-lock /> 强制锁屏
<reset-password /> 重置密码
<wipe-data /> 清除数据
<disable-camera /> 禁止相机
<encrypted-storage /> 加密存储数据
</uses-policies>
</device-admin>
清单文件注册
<receiver
android:name=".module.funs.admin.MyAdminReceiver"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/my_device_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
锁屏调用
- 锁屏
DevicePolicyManager#lockNow
- 重置密码
用来重置密码,但是需要 成为Owner
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
manager.resetPasswordWithToken(componentName,password, getPwdToken(),0)
}else {
manager.resetPassword(password, 0)
}
API 24+ 即Android N 后,系统做了限制,无法针对已设置密码的设备 进行重置密码的操作,否则会抛异常
Admin cannot change current password
or
Android SecurityException: Admin does not own the profile
- 激活组件
fun activeDeviceAdmin() {
val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName)
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "--其他描述--")
startActivityForResult(intent, 0)
}
具体代码
fun lockScreen(password: String) {
val manager = ctx().getSystemService(Context.DEVICE_POLICY_SERVICE) as? DevicePolicyManager
?: return
val componentName = ComponentName(ctx(), MyAdminReceiver::class.java)
//判断设备管理器组件是否激活
if (manager.isAdminActive(componentName)) {
try {
manager.setPasswordQuality(componentName, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
manager.resetPasswordWithToken(componentName,password, getPwdToken(),0)
}
// 用来重置密码,但是需要 成为Owner。
// API 24+ 即Android N 后,系统做了限制,无法针对已设置密码的设备 进行重置密码的操作,否则会 抛错
// manager.resetPassword(password, 0)
// 调用锁屏
manager.lockNow()
} catch (e: Exception) {
e.printStackTrace()
}
} else {
//激活设备管理器组件
activeDeviceAdmin()
}
}
源码
setResetPasswordToken
API 24,Android N
/**
* @param token 一种安全令牌,至少32字节长,必须由加密的强随机数生成器生成。
*/
public boolean setResetPasswordToken(ComponentName admin, byte[] token) {}
由配置文件或设备所有者调用,以提供一个令牌,该令牌可用于稍后通过{@link #resetPasswordWithToken}重置设备锁屏密码(如果由设备所有者调用),或托管配置文件挑战(如果由配置文件所有者调用)。
如果用户当前拥有锁定屏幕密码,则所提供的令牌将不能立即使用;它只在用户执行确认凭据操作之后才会激活,该操作可以由{@link KeyguardManager# createcon固件devicecredentialintent}触发。如果用户没有锁定屏幕密码,则立即激活令牌。在所有情况下,当前令牌的活动状态都可以由{@link #isResetPasswordTokenActive}检查。出于安全原因,未激活的令牌只存储在内存中,并且在设备重新启动后将丢失。在这种情况下,需要再次提供一个新的令牌。
一旦准备好并激活了令牌,即使用户更改或清除锁定屏幕密码,令牌也将保持有效。
这个令牌是高度敏感的,应该与用户凭证在同一级别上处理。特别是,永远不要将此令牌以明文形式存储在设备上。如果需要在用户解锁之前重置基于文件的加密设备上的密码,请不要将明文令牌存储在设备加密的存储中。请仔细考虑如何将密码令牌存储在服务器上,以及谁需要访问它们。令牌可能是合法访问请求的主体。
resetPasswordWithToken
public boolean resetPasswordWithToken(@NonNull ComponentName admin, String password,
byte[] token, int flags) {}
由设备或配置文件所有者调用,强制对当前用户设置新设备解锁密码或托管配置文件挑战。这立即生效。
与{@link #resetPassword}不同,这个API甚至可以在用户或设备未锁定或解密之前更改密码。所提供的令牌必须事先通过{@link #setResetPasswordToken}和在活动状态{@link #isResetPasswordTokenActive}提供。
给定的密码必须满足{@link #getPasswordQuality(ComponentName)}和{@link #getPasswordMinimumLength(ComponentName)}返回的当前密码质量和长度约束;如果它不满足这些约束,那么它将被拒绝并返回false。注意,密码可能是更强的质量,例如,当请求的质量只是数字时,包含字母数字字符的密码。
如果当前密码约束允许,使用{@code null}或空密码调用将清除任何现有PIN、模式或密码。
网友评论