如何在 Android SDK 26 或更高版本上以编程方式重置锁屏密码

Posted

技术标签:

【中文标题】如何在 Android SDK 26 或更高版本上以编程方式重置锁屏密码【英文标题】:How to reset the password of the lock screen programmatically on Android SDK 26 or higher 【发布时间】:2020-01-03 13:02:12 【问题描述】:

在我的应用程序中,我想以编程方式更改锁定屏幕的密码。所以我写了这个方法来重置密码:

@TargetApi(26)
private void changePasswordWithToken() 
    SecureRandom secureRandom = new SecureRandom();
    byte[] token = secureRandom.generateSeed(32);
    DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getApplicationContext().getSystemService(
            DEVICE_POLICY_SERVICE);
    if (devicePolicyManager != null) 
        devicePolicyManager.setResetPasswordToken(compName, token);
        devicePolicyManager.resetPasswordWithToken(compName, "1234", token, 0);
    

当我调用该方法时,我在运行 android 9 SDK 27 的设备上收到此错误

va.lang.SecurityException: Admin ComponentInfocom.xxx.xxx/com.xxx.xxxx.MyAdmin does not own the profile
        at android.os.Parcel.createException(Parcel.java:1942)
        at android.os.Parcel.readException(Parcel.java:1910)
        at android.os.Parcel.readException(Parcel.java:1860)
        at android.app.admin.IDevicePolicyManager$Stub$Proxy.setResetPasswordToken(IDevicePolicyManager.java:9995)
        at android.app.admin.DevicePolicyManager.setResetPasswordToken(DevicePolicyManager.java:3091)
        at com.ssaurel.lockdevice.MainActivity.changePasswordWithToken(MainActivity.java:136)
        at com.xx.xx.MainActivity.onClick(MainActivity.java:93)
        at android.view.View.performClick(View.java:6597)
        at android.view.View.performClickInternal(View.java:6574)
...

在我调用这个方法之前,我用这个方法获得了设备管理员权限

private void provisionDeviceAdmin() 
    Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
    intent.putExtra(EXTRA_DEVICE_ADMIN, compName);
    intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
                    "Additional text explaining why we need this permission");
    startActivityForResult(intent, RESULT_ENABLE);

我的政策是这样的

<?xml version="1.0" encoding="utf-8"?>
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-policies>
        <force-lock />
        <reset-password />
    </uses-policies>
</device-admin>

【问题讨论】:

【参考方案1】:

请查看文档,其中明确指出:

由设备或个人资料所有者调用以强制设置新设备解锁 当前用户的密码或托管配置文件质询。这需要 立即生效。

从您的代码看来,您不是“设备或个人资料所有者”;请不要将其与您的应用看起来的“设备管理员”混为一谈(或试图从您的代码中不确定它是否真的成功)。

【讨论】:

【参考方案2】:

对我来说,这个解决方案很有效:

首先,定义管理员设备权限

policies.xml

<?xml version="1.0" encoding="utf-8"?> <device-admin> <uses-policies> <reset-password/> </uses-policies> </device-admin>

然后创建一个扩展DeviceAdminReceiver的类

public class MyAdmin extends DeviceAdminReceiver 

@Override
public void onEnabled(Context context, Intent intent) 
    Toast.makeText(context, "Device Admin : enabled", Toast.LENGTH_SHORT).show();


@Override
public void onDisabled(Context context, Intent intent) 
    Toast.makeText(context, "Device Admin : disabled", Toast.LENGTH_SHORT).show();


/**
 * Generates a @link ComponentName that is used throughout the app.
 * @return a @link ComponentName
 */
public static ComponentName getComponentName(Context context) 
    return new ComponentName(context.getApplicationContext(), MyAdmin.class);

在您的MainActivity 中使用此功能获取管理员设备权限

private void provisionDeviceAdmin() 
    Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
    intent.putExtra(EXTRA_DEVICE_ADMIN, MyAdmin.getComponentName(this));
    intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
                    "Additional text explaining why we need this permission");
    startActivityForResult(intent, RESULT_ENABLE);

然后在MainActivity 中为您的应用配置工作配置文件

private void provisionManagedProfile() 
    Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE);

    // Use a different intent extra below M to configure the admin component.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) 
        //noinspection deprecation
        intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
                        MyAdmin.getComponentName(this));
     else 
        final ComponentName component = new ComponentName(this,
                                                          MyAdmin.class.getName());
        intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
                        component);
    

    if (intent.resolveActivity(this.getPackageManager()) != null) 
        startActivityForResult(intent, REQUEST_PROVISION_MANAGED_PROFILE);
        this.finish();
     else 
        Toast.makeText(this, "Device provisioning is not enabled. Stopping.",
                       Toast.LENGTH_SHORT).show();
    

然后将应用程序包设置为工作配置文件

 private void setAppEnabled(String packageName, boolean enabled) 
    PackageManager packageManager = getPackageManager();
    DevicePolicyManager devicePolicyManager =
            (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
    try 
        int packageFlags;
        if(Build.VERSION.SDK_INT < 24)
            //noinspection deprecation
            packageFlags = PackageManager.GET_UNINSTALLED_PACKAGES;
        else
            packageFlags = PackageManager.MATCH_UNINSTALLED_PACKAGES;
        
        ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName,
                                                                            packageFlags);
        // Here, we check the ApplicationInfo of the target app, and see if the flags have
        // ApplicationInfo.FLAG_INSTALLED turned on using bitwise operation.
        if (0 == (applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)) 
            // If the app is not installed in this profile, we can enable it by
            // DPM.enableSystemApp
            if (enabled) 
                devicePolicyManager.enableSystemApp(MyAdmin.getComponentName(this), packageName);
             else 
                // But we cannot disable the app since it is already disabled
                Log.e("TAG", "Cannot disable this app: " + packageName);
                return;
            
         else 
            // If the app is already installed, we can enable or disable it by
            // DPM.setApplicationHidden
            devicePolicyManager.setApplicationHidden(
                    MyAdmin.getComponentName(this), packageName, !enabled);
        
        Toast.makeText(this, enabled ? "Enabled" : "Disabled",
                       Toast.LENGTH_SHORT).show();
     catch (PackageManager.NameNotFoundException e) 
        Log.e("TAG", "The app cannot be found: " + packageName, e);
    

创建一个生成随机密码令牌的方法

private byte[] generateRandomPasswordToken() 
    try 
        return SecureRandom.getInstance("SHA1PRNG").generateSeed(32);
     catch (NoSuchAlgorithmException e) 
        e.printStackTrace();
        return null;
    

终于实现了这个用token重置锁屏密码的方法

 @TargetApi(26)
private void changePasswordWithToken() 
    byte[] token = generateRandomPasswordToken();
    DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getApplicationContext().getSystemService(
            DEVICE_POLICY_SERVICE);
    KeyguardManager keyguardManager = (KeyguardManager) this.getSystemService(KEYGUARD_SERVICE);
    keyguardManager.createConfirmDeviceCredentialIntent(null, null);
    if (devicePolicyManager != null) 
        devicePolicyManager.setResetPasswordToken(MyAdmin.getComponentName(this), token);
        devicePolicyManager.resetPasswordWithToken(MyAdmin.getComponentName(this), "1234", token, 0);
    

【讨论】:

以上是关于如何在 Android SDK 26 或更高版本上以编程方式重置锁屏密码的主要内容,如果未能解决你的问题,请参考以下文章

在Android 1.0或更高版本中使Android项目Gradle兼容

此 Android SDK 需要 ADT 版本 23.0.0 或更高版本。当前版本是 22.6。请更新 ADT 到最新版本? [复制]

PhoneGap iOS 提交错误:ITMS-90807:无效授权 - 如何使用 SDK 13 或更高版本进行构建?

如何查看我的 Xamarin.Forms 应用程序是不是在 Visual Studio 中的 Android 6(或更高版本)上运行或/和可运行?

如何在 iOS 9 fb sdk 4.8 或更高版本中从 facebook 获取好友列表?

如何在 android 10 或更高版本中获取设备 ID 或 imei 号码