androidsecurityexceptiondevice-admindevice-policy-manager

How to reset the password of the lock screen programmatically on Android SDK 26 or higher


In my application I want to change the password of the lock screen programmatically. So I wrote this method to reset the password:

@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);
    }
}

When I call the method I get this error on my device running Android 9 SDK 27

va.lang.SecurityException: Admin ComponentInfo{com.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)
...

Before I call this method, I' getting the device admin permissions with this method

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);
}

My policies looks like this

<?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>

Solution

  • For me this solution worked out:

    At first, define admin device permissions

    policies.xml

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

    Then create a class that extend 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);
    }
    

    }

    Get admin device permission with this function in your 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);
    }
    

    Then provision a working profile for your app in the 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();
        }
    }
    

    After that set the application package to the working profile

     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);
        }
    }
    

    Create a method to generate random password tokens

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

    Finally implement this method to reset the lock screen password with 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);
        }
    }