Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 289100bb authored by Rubin Xu's avatar Rubin Xu Committed by Android (Google) Code Review
Browse files

Merge "Unify lockscreen changing logic in LockPatternUtils"

parents 9af061c6 6232a4cd
Loading
Loading
Loading
Loading
+57 −206
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.app.admin.DevicePolicyManager;
import android.app.admin.PasswordMetrics;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
import android.content.ComponentName;
@@ -598,44 +597,6 @@ public class LockPatternUtils {
        }
    }

    /**
     * Clear any lock pattern or password, with the option to ignore incorrect existing credential.
     *
     * <p> This method will fail (returning {@code false}) if the previously
     * saved password provided is incorrect, or if the lockscreen verification
     * is still being throttled.
     *
     * @param savedCredential The previously saved credential
     * @param userHandle the user whose pattern is to be saved.
     * @return whether this was successful or not.
     * @throws RuntimeException if password change encountered an unrecoverable error.
     */
    private boolean clearLock(LockscreenCredential savedCredential, int userHandle,
            boolean allowUntrustedChange) {
        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
        setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userHandle);

        try {
            if (!getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE,
                    savedCredential.getCredential(), PASSWORD_QUALITY_UNSPECIFIED, userHandle,
                    allowUntrustedChange)) {
                return false;
            }
        } catch (RemoteException | RuntimeException e) {
            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
            throw new RuntimeException("Failed to clear lock", e);
        }

        if (userHandle == UserHandle.USER_SYSTEM) {
            // Set the encryption password to default.
            updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
            setCredentialRequiredToDecrypt(false);
        }

        onAfterChangingPassword(userHandle);
        return true;
    }

    /**
     * Disable showing lock screen at all for a given user.
     * This is only meaningful if pattern, pin or password are not set.
@@ -705,7 +666,7 @@ public class LockPatternUtils {
     * being throttled.
     * @param newCredential The new credential to save
     * @param savedCredential The current credential
     * @param userId the user whose lockscreen credential is to be changed
     * @param userHandle the user whose lockscreen credential is to be changed
     * @param allowUntrustedChange whether we want to allow saving a new pattern if the existing
     * credentialt being provided is incorrect.
     *
@@ -714,73 +675,43 @@ public class LockPatternUtils {
     * @throws RuntimeException if password change encountered an unrecoverable error.
     */
    public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
            @NonNull LockscreenCredential savedCredential, int userId,
            @NonNull LockscreenCredential savedCredential, int userHandle,
            boolean allowUntrustedChange) {
        if (newCredential.isNone()) {
            return clearLock(savedCredential, userId, allowUntrustedChange);
        } else if (newCredential.isPattern()) {
            return saveLockPattern(newCredential, savedCredential, userId, allowUntrustedChange);
        } else {
            return saveLockPassword(newCredential, savedCredential, newCredential.getQuality(),
                    userId, allowUntrustedChange);
        }

    }

    /**
     * Save a lock pattern.
     *
     * <p> This method will fail (returning {@code false}) if the previously saved pattern provided
     * is incorrect, or if the lockscreen verification is still being throttled.
     *
     * @param pattern The new pattern to save.
     * @param savedCredential The previously saved credential
     * @param userId the user whose pattern is to be saved.
     * @param allowUntrustedChange whether we want to allow saving a new pattern if the existing
     * credentialt being provided is incorrect.
     *
     * @return whether this was successful or not.
     * @throws RuntimeException if password change encountered an unrecoverable error.
     */
    private boolean saveLockPattern(LockscreenCredential pattern,
            LockscreenCredential savedCredential, int userId, boolean allowUntrustedChange) {
        if (!hasSecureLockScreen()) {
            throw new UnsupportedOperationException(
                    "This operation requires the lock screen feature.");
        }
        if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
            throw new IllegalArgumentException("pattern must not be null and at least "
                    + MIN_LOCK_PATTERN_SIZE + " dots long.");
        }
        newCredential.checkLength();

        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
        setKeyguardStoredPasswordQuality(newCredential.getQuality(), userHandle);

        final int currentQuality = getKeyguardStoredPasswordQuality(userId);
        setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId);
        try {
            if (!getLockSettings().setLockCredential(pattern.getCredential(), pattern.getType(),
                    savedCredential.getCredential(), PASSWORD_QUALITY_SOMETHING, userId,
                    allowUntrustedChange)) {
            if (!getLockSettings().setLockCredential(
                    newCredential.getCredential(), newCredential.getType(),
                    savedCredential.getCredential(),
                    newCredential.getQuality(), userHandle, allowUntrustedChange)) {
                setKeyguardStoredPasswordQuality(currentQuality, userHandle);
                return false;
            }
        } catch (RemoteException | RuntimeException e) {
            setKeyguardStoredPasswordQuality(currentQuality, userId);
            throw new RuntimeException("Couldn't save lock pattern", e);
        }
        // Update the device encryption password.
        if (userId == UserHandle.USER_SYSTEM
                && LockPatternUtils.isDeviceEncryptionEnabled()) {
            if (!shouldEncryptWithCredentials(true)) {
                clearEncryptionPassword();
            } else {
                updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN,
                        pattern.getCredential());
            }
            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
            throw new RuntimeException("Unable to save lock password", e);
        }

        reportPatternWasChosen(userId);
        onAfterChangingPassword(userId);
        onPostPasswordChanged(newCredential, userHandle);
        return true;
    }

    private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
        updateEncryptionPasswordIfNeeded(newCredential, userHandle);
        if (newCredential.isPattern()) {
            reportPatternWasChosen(userHandle);
        }
        updatePasswordHistory(newCredential, userHandle);
        reportEnabledTrustAgentsChanged(userHandle);
    }

    private void updateCryptoUserInfo(int userId) {
        if (userId != UserHandle.USER_SYSTEM) {
            return;
@@ -878,93 +809,24 @@ public class LockPatternUtils {
        }.execute();
    }

    /**
     * Save a lock password.
     *
     * <p> This method will fail (returning {@code false}) if the previously saved password provided
     * is incorrect and allowUntrustedChange is false, or if the lockscreen verification
     * is still being throttled.
     *
     * @param password The password to save
     * @param savedCredential The previously saved lock credential
     * @param requestedQuality {@link DevicePolicyManager#getPasswordQuality(
     * android.content.ComponentName)}
     * @param userHandle The userId of the user to change the password for
     * @param allowUntrustedChange whether we want to allow saving a new password if the existing
     * password being provided is incorrect.
     * @return whether this method saved the new password successfully or not. This flow will fail
     * and return false if the given credential is wrong and allowUntrustedChange is false.
     * @throws RuntimeException if password change encountered an unrecoverable error.
     */
    private boolean saveLockPassword(LockscreenCredential password,
            LockscreenCredential savedCredential, int requestedQuality, int userHandle,
            boolean allowUntrustedChange) {
        if (!hasSecureLockScreen()) {
            throw new UnsupportedOperationException(
                    "This operation requires the lock screen feature.");
        }
        if (password.isNone() || password.size() < MIN_LOCK_PASSWORD_SIZE) {
            throw new IllegalArgumentException("password must not be null and at least "
                    + "of length " + MIN_LOCK_PASSWORD_SIZE);
        }

        if (requestedQuality < PASSWORD_QUALITY_NUMERIC) {
            throw new IllegalArgumentException("quality must be at least NUMERIC, but was "
                    + requestedQuality);
        }

        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
        final int passwordQuality =
                PasswordMetrics.computeForPassword(password.getCredential()).quality;
        final int newKeyguardQuality =
                computeKeyguardQuality(CREDENTIAL_TYPE_PASSWORD, requestedQuality, passwordQuality);
        setKeyguardStoredPasswordQuality(newKeyguardQuality, userHandle);
        try {
            getLockSettings().setLockCredential(password.getCredential(), password.getType(),
                    savedCredential.getCredential(),
                    requestedQuality, userHandle, allowUntrustedChange);
        } catch (RemoteException | RuntimeException e) {
            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
            throw new RuntimeException("Unable to save lock password", e);
        }

        updateEncryptionPasswordIfNeeded(password.getCredential(), passwordQuality, userHandle);
        updatePasswordHistory(password, userHandle);
        onAfterChangingPassword(userHandle);
        return true;
    }

    /**
     * Compute keyguard credential quality to store in PASSWORD_TYPE_KEY by computing max between
     * them so that digit-only password is distinguished from PIN.
     *
     * TODO: remove this method and make CREDENTIAL_TYPE distinguish between PIN and password, so
     * that this quality is no longer needs to be persisted.
     */
    private int computeKeyguardQuality(
            @CredentialType int credentialType, int requestedQuality, int passwordQuality) {
        return credentialType == CREDENTIAL_TYPE_PASSWORD
                ? Math.max(passwordQuality, requestedQuality) : passwordQuality;
    }

    /**
     * Update device encryption password if calling user is USER_SYSTEM and device supports
     * encryption.
     */
    private void updateEncryptionPasswordIfNeeded(byte[] password, int quality, int userHandle) {
    private void updateEncryptionPasswordIfNeeded(LockscreenCredential credential, int userHandle) {
        // Update the device encryption password.
        if (userHandle == UserHandle.USER_SYSTEM
                && LockPatternUtils.isDeviceEncryptionEnabled()) {
        if (userHandle != UserHandle.USER_SYSTEM || !isDeviceEncryptionEnabled()) {
            return;
        }
        if (!shouldEncryptWithCredentials(true)) {
                clearEncryptionPassword();
            } else {
                boolean numeric = quality == PASSWORD_QUALITY_NUMERIC;
                boolean numericComplex = quality == PASSWORD_QUALITY_NUMERIC_COMPLEX;
                int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN
                        : StorageManager.CRYPT_TYPE_PASSWORD;
                updateEncryptionPassword(type, password);
            updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
            return;
        }
        if (credential.isNone()) {
            // Set the encryption password to default.
            setCredentialRequiredToDecrypt(false);
        }
        updateEncryptionPassword(credential.getStorageCryptType(), credential.getCredential());
    }

    /**
@@ -973,7 +835,10 @@ public class LockPatternUtils {
     */
    private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
        if (password.isNone()) {
            Log.e(TAG, "checkPasswordHistory: empty password");
            return;
        }
        if (password.isPattern()) {
            // Do not keep track of historical patterns
            return;
        }
        // Add the password to the password history. We assume all
@@ -1065,7 +930,7 @@ public class LockPatternUtils {
        try {
            getLockSettings().setSeparateProfileChallengeEnabled(userHandle, enabled,
                    managedUserPassword.getCredential());
            onAfterChangingPassword(userHandle);
            reportEnabledTrustAgentsChanged(userHandle);
        } catch (RemoteException e) {
            Log.e(TAG, "Couldn't update work profile challenge enabled");
        }
@@ -1506,7 +1371,7 @@ public class LockPatternUtils {
        }
    }

    private void onAfterChangingPassword(int userHandle) {
    private void reportEnabledTrustAgentsChanged(int userHandle) {
        getTrustManager().reportEnabledTrustAgentsChanged(userHandle);
    }

@@ -1674,48 +1539,34 @@ public class LockPatternUtils {
     * @param credential The new credential to be set
     * @param tokenHandle Handle of the escrow token
     * @param token Escrow token
     * @param userId The user who's lock credential to be changed
     * @param userHandle The user who's lock credential to be changed
     * @return {@code true} if the operation is successful.
     */
    public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
            byte[] token, int userId) {
            byte[] token, int userHandle) {
        if (!hasSecureLockScreen()) {
            throw new UnsupportedOperationException(
                    "This operation requires the lock screen feature.");
        }
        credential.checkLength();
        LockSettingsInternal localService = getLockSettingsInternal();
        if (!credential.isNone()) {
            if (credential.size() < MIN_LOCK_PASSWORD_SIZE) {
                throw new IllegalArgumentException("password must not be null and at least "
                        + "of length " + MIN_LOCK_PASSWORD_SIZE);
            }
            final int type = credential.getType();
            final int quality = PasswordMetrics.computeForCredential(credential).quality;
            final int keyguardQuality = computeKeyguardQuality(type, quality,
                    credential.getQuality());
            if (!localService.setLockCredentialWithToken(credential.getCredential(), type,
                    tokenHandle, token, keyguardQuality, userId)) {
                return false;
            }
            setKeyguardStoredPasswordQuality(quality, userId);

            updateEncryptionPasswordIfNeeded(credential.getCredential(), quality, userId);
            updatePasswordHistory(credential, userId);
            onAfterChangingPassword(userId);
        } else {
            if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE, tokenHandle,
                    token, PASSWORD_QUALITY_UNSPECIFIED, userId)) {
                return false;
            }
            setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userId);
        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
        setKeyguardStoredPasswordQuality(credential.getQuality(), userHandle);

            if (userId == UserHandle.USER_SYSTEM) {
                // Set the encryption password to default.
                updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
                setCredentialRequiredToDecrypt(false);
        try {
            if (!localService.setLockCredentialWithToken(credential.getCredential(),
                    credential.getType(),
                    tokenHandle, token, credential.getType(), userHandle)) {
                setKeyguardStoredPasswordQuality(currentQuality, userHandle);
                return false;
            }
        } catch (RuntimeException e) {
            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
            throw new RuntimeException("Unable to save lock credential", e);
        }
        onAfterChangingPassword(userId);

        onPostPasswordChanged(credential, userHandle);
        return true;
    }

+45 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.storage.StorageManager;
import android.text.TextUtils;

import com.android.internal.util.Preconditions;
@@ -187,6 +188,25 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
        return mCredential;
    }

    /**
     *  Returns the credential type recognized by {@link StorageManager}. Can be one of
     *  {@link StorageManager#CRYPT_TYPE_DEFAULT}, {@link StorageManager#CRYPT_TYPE_PATTERN},
     *  {@link StorageManager#CRYPT_TYPE_PIN} or {@link StorageManager#CRYPT_TYPE_PASSWORD}.
     */
    public int getStorageCryptType() {
        if (isNone()) {
            return StorageManager.CRYPT_TYPE_DEFAULT;
        }
        if (isPattern()) {
            return StorageManager.CRYPT_TYPE_PATTERN;
        }
        if (isPassword()) {
            return mQuality == PASSWORD_QUALITY_NUMERIC
                    ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD;
        }
        throw new IllegalStateException("Unhandled credential type");
    }

    /** Returns whether this is an empty credential */
    public boolean isNone() {
        ensureNotZeroized();
@@ -227,6 +247,31 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
        }
    }

    /**
     * Check if the credential meets minimal length requirement.
     *
     * @throws IllegalArgumentException if the credential is too short.
     */
    public void checkLength() {
        if (isNone()) {
            return;
        }
        if (isPattern()) {
            if (size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
                throw new IllegalArgumentException("pattern must not be null and at least "
                        + LockPatternUtils.MIN_LOCK_PATTERN_SIZE + " dots long.");
            }
            return;
        }
        if (isPassword()) {
            if (size() < LockPatternUtils.MIN_LOCK_PASSWORD_SIZE) {
                throw new IllegalArgumentException("password must not be null and at least "
                        + "of length " + LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
            }
            return;
        }
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mType);
+3 −3
Original line number Diff line number Diff line
@@ -513,7 +513,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
        assertFalse(mService.havePattern(PRIMARY_USER_ID));
    }

    public void testgetHashFactorPrimaryUser() throws RemoteException {
    public void testGetHashFactorPrimaryUser() throws RemoteException {
        final byte[] password = "password".getBytes();
        mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
@@ -528,7 +528,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
        assertArrayEquals(hashFactor, newHashFactor);
    }

    public void testgetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
    public void testGetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
        final byte[] pattern = "1236".getBytes();
        mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
                null, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID, false);
@@ -536,7 +536,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
        assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID));
    }

    public void testgetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
    public void testGetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
        final byte[] primaryPassword = "primary".getBytes();
        final byte[] profilePassword = "profile".getBytes();
        mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,