Refactor LockSettingsService to unify the handling of pattern and password
Also fix LockSettingsStorageTests. More unit tests on LockSettingsService
to be added in the next CL.
Bug: 33126408
Test: runtest frameworks-services -c com.android.server.LockSettingsStorageTests
Change-Id: I0f143b26fed1d5ae122fba3b57bd39c7793ad8d9
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index a5552b8..51503c0 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -16,6 +16,11 @@
package com.android.server;
+import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.content.Context.KEYGUARD_SERVICE;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
+
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.KeyguardManager;
@@ -35,31 +40,23 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
-
-import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
-import static android.content.Context.KEYGUARD_SERVICE;
-import static android.content.Context.USER_SERVICE;
-import static android.Manifest.permission.READ_CONTACTS;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
-
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IProgressListener;
-import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ShellCallback;
-import android.os.storage.IStorageManager;
-import android.os.storage.StorageManager;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
@@ -109,20 +106,21 @@
import javax.crypto.spec.GCMParameterSpec;
/**
- * Keeps the lock pattern/password data and related settings for each user.
- * Used by LockPatternUtils. Needs to be a service because Settings app also needs
- * to be able to save lockscreen information for secondary users.
+ * Keeps the lock pattern/password data and related settings for each user. Used by
+ * LockPatternUtils. Needs to be a service because Settings app also needs to be able to save
+ * lockscreen information for secondary users.
+ *
* @hide
*/
public class LockSettingsService extends ILockSettings.Stub {
private static final String TAG = "LockSettingsService";
private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
- private static final Intent ACTION_NULL; // hack to ensure notification shows the bouncer
private static final int FBE_ENCRYPTED_NOTIFICATION = 0;
private static final boolean DEBUG = false;
private static final int PROFILE_KEY_IV_SIZE = 12;
private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
+
private final Object mSeparateChallengeLock = new Object();
private final Context mContext;
@@ -142,21 +140,9 @@
/**
* The UIDs that are used for system credential storage in keystore.
*/
- private static final int[] SYSTEM_CREDENTIAL_UIDS = {Process.WIFI_UID, Process.VPN_UID,
- Process.ROOT_UID, Process.SYSTEM_UID};
-
- static {
- // Just launch the home screen, which happens anyway
- ACTION_NULL = new Intent(Intent.ACTION_MAIN);
- ACTION_NULL.addCategory(Intent.CATEGORY_HOME);
- }
-
- private interface CredentialUtil {
- void setCredential(String credential, String savedCredential, int userId)
- throws RemoteException;
- byte[] toHash(String credential, int userId);
- String adjustForKeystore(String credential);
- }
+ private static final int[] SYSTEM_CREDENTIAL_UIDS = {
+ Process.WIFI_UID, Process.VPN_UID,
+ Process.ROOT_UID, Process.SYSTEM_UID };
// This class manages life cycle events for encrypted users on File Based Encryption (FBE)
// devices. The most basic of these is to show/hide notifications about missing features until
@@ -258,7 +244,8 @@
try {
randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40);
String newPassword = String.valueOf(HexEncoding.encode(randomLockSeed));
- setLockPasswordInternal(newPassword, managedUserPassword, managedUserId);
+ setLockCredentialInternal(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ managedUserPassword, managedUserId);
// We store a private credential for the managed user that's unlocked by the primary
// account holder's credential. As such, the user will never be prompted to enter this
// password directly, so we always store a password.
@@ -306,8 +293,8 @@
}
/**
- * If the account is credential-encrypted, show notification requesting the user to unlock
- * the device.
+ * If the account is credential-encrypted, show notification requesting the user to unlock the
+ * device.
*/
private void maybeShowEncryptionNotificationForUser(@UserIdInt int userId) {
final UserInfo user = mUserManager.getUserInfo(userId);
@@ -342,19 +329,21 @@
com.android.internal.R.string.profile_encrypted_detail);
final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
- final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier());
+ final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null,
+ user.getIdentifier());
if (unlockIntent == null) {
return;
}
- unlockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ unlockIntent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
showEncryptionNotification(user, title, message, detail, intent);
}
- private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message,
- CharSequence detail, PendingIntent intent) {
+ private void showEncryptionNotification(UserHandle user, CharSequence title,
+ CharSequence message, CharSequence detail, PendingIntent intent) {
if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
// Suppress all notifications on non-FBE devices for now
@@ -365,7 +354,7 @@
.setWhen(0)
.setOngoing(true)
.setTicker(title)
- .setDefaults(0) // please be quiet
+ .setDefaults(0) // please be quiet
.setPriority(Notification.PRIORITY_MAX)
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color))
@@ -379,7 +368,7 @@
}
private void hideEncryptionNotification(UserHandle userHandle) {
- if (DEBUG) Slog.v(TAG, "hide encryption notification, user: "+ userHandle.getIdentifier());
+ if (DEBUG) Slog.v(TAG, "hide encryption notification, user: " + userHandle.getIdentifier());
mNotificationManager.cancelAsUser(null, FBE_ENCRYPTED_NOTIFICATION, userHandle);
}
@@ -491,7 +480,7 @@
Settings.Secure.putStringForUser(cr, OWNER_INFO, "", userId);
}
- // Migrate owner info enabled. Note there was a bug where older platforms only
+ // Migrate owner info enabled. Note there was a bug where older platforms only
// stored this value if the checkbox was toggled at least once. The code detects
// this case by handling the exception.
final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
@@ -788,8 +777,9 @@
private void unlockChildProfile(int profileHandle) throws RemoteException {
try {
- doVerifyPassword(getDecryptedPasswordForTiedProfile(profileHandle), false,
- 0 /* no challenge */, profileHandle, null /* progressCallback */);
+ doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ false, 0 /* no challenge */, profileHandle, null /* progressCallback */);
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
| NoSuchAlgorithmException | NoSuchPaddingException
| InvalidAlgorithmParameterException | IllegalBlockSizeException
@@ -852,35 +842,13 @@
}
private byte[] getCurrentHandle(int userId) {
- CredentialHash credential;
- byte[] currentHandle;
-
- int currentHandleType = mStorage.getStoredCredentialType(userId);
- switch (currentHandleType) {
- case CredentialHash.TYPE_PATTERN:
- credential = mStorage.readPatternHash(userId);
- currentHandle = credential != null
- ? credential.hash
- : null;
- break;
- case CredentialHash.TYPE_PASSWORD:
- credential = mStorage.readPasswordHash(userId);
- currentHandle = credential != null
- ? credential.hash
- : null;
- break;
- case CredentialHash.TYPE_NONE:
- default:
- currentHandle = null;
- break;
- }
+ CredentialHash credential = mStorage.readCredentialHash(userId);
// sanity check
- if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) {
- Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available");
+ if (credential.type != LockPatternUtils.CREDENTIAL_TYPE_NONE && credential.hash == null) {
+ Slog.e(TAG, "Stored handle type [" + credential.type + "] but no handle available");
}
-
- return currentHandle;
+ return credential.hash;
}
private void onUserLockChanged(int userId) throws RemoteException {
@@ -902,7 +870,7 @@
} else {
clearUserKeyProtection(managedUserId);
getGateKeeperService().clearSecureUserId(managedUserId);
- mStorage.writePatternHash(null, managedUserId);
+ mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), managedUserId);
setKeystorePassword(null, managedUserId);
fixateNewestUserKeyAuth(managedUserId);
mStorage.removeChildProfileLock(managedUserId);
@@ -923,97 +891,37 @@
}
// This method should be called by LockPatternUtil only, all internal methods in this class
- // should call setLockPatternInternal.
+ // should call setLockCredentialInternal.
@Override
- public void setLockPattern(String pattern, String savedCredential, int userId)
+ public void setLockCredential(String credential, int type, String savedCredential, int userId)
throws RemoteException {
checkWritePermission(userId);
synchronized (mSeparateChallengeLock) {
- setLockPatternInternal(pattern, savedCredential, userId);
+ setLockCredentialInternal(credential, type, savedCredential, userId);
setSeparateProfileChallengeEnabled(userId, true, null);
notifyPasswordChanged(userId);
}
}
- private void setLockPatternInternal(String pattern, String savedCredential, int userId)
- throws RemoteException {
+ private void setLockCredentialInternal(String credential, int credentialType,
+ String savedCredential, int userId) throws RemoteException {
byte[] currentHandle = getCurrentHandle(userId);
-
- if (pattern == null) {
+ if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+ if (credential != null) {
+ Slog.wtf(TAG, "CredentialType is none, but credential is non-null.");
+ }
clearUserKeyProtection(userId);
getGateKeeperService().clearSecureUserId(userId);
- mStorage.writePatternHash(null, userId);
+ mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId);
setKeystorePassword(null, userId);
fixateNewestUserKeyAuth(userId);
onUserLockChanged(userId);
notifyActivePasswordMetricsAvailable(null, userId);
return;
}
-
- if (isManagedProfileWithUnifiedLock(userId)) {
- // get credential from keystore when managed profile has unified lock
- try {
- savedCredential = getDecryptedPasswordForTiedProfile(userId);
- } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
- | NoSuchAlgorithmException | NoSuchPaddingException
- | InvalidAlgorithmParameterException | IllegalBlockSizeException
- | BadPaddingException | CertificateException | IOException e) {
- if (e instanceof FileNotFoundException) {
- Slog.i(TAG, "Child profile key not found");
- } else {
- Slog.e(TAG, "Failed to decrypt child profile key", e);
- }
- }
- } else {
- if (currentHandle == null) {
- if (savedCredential != null) {
- Slog.w(TAG, "Saved credential provided, but none stored");
- }
- savedCredential = null;
- }
+ if (credential == null) {
+ throw new RemoteException("Null credential with mismatched credential type");
}
-
- byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
- if (enrolledHandle != null) {
- CredentialHash willStore
- = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER);
- setUserKeyProtection(userId, pattern,
- doVerifyPattern(pattern, willStore, true, 0, userId, null /* progressCallback */));
- mStorage.writePatternHash(enrolledHandle, userId);
- fixateNewestUserKeyAuth(userId);
- onUserLockChanged(userId);
- } else {
- throw new RemoteException("Failed to enroll pattern");
- }
- }
-
- // This method should be called by LockPatternUtil only, all internal methods in this class
- // should call setLockPasswordInternal.
- @Override
- public void setLockPassword(String password, String savedCredential, int userId)
- throws RemoteException {
- checkWritePermission(userId);
- synchronized (mSeparateChallengeLock) {
- setLockPasswordInternal(password, savedCredential, userId);
- setSeparateProfileChallengeEnabled(userId, true, null);
- notifyPasswordChanged(userId);
- }
- }
-
- private void setLockPasswordInternal(String password, String savedCredential, int userId)
- throws RemoteException {
- byte[] currentHandle = getCurrentHandle(userId);
- if (password == null) {
- clearUserKeyProtection(userId);
- getGateKeeperService().clearSecureUserId(userId);
- mStorage.writePasswordHash(null, userId);
- setKeystorePassword(null, userId);
- fixateNewestUserKeyAuth(userId);
- onUserLockChanged(userId);
- notifyActivePasswordMetricsAvailable(null, userId);
- return;
- }
-
if (isManagedProfileWithUnifiedLock(userId)) {
// get credential from keystore when managed profile has unified lock
try {
@@ -1035,18 +943,21 @@
}
}
- byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
+ byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, credential,
+ userId);
if (enrolledHandle != null) {
- CredentialHash willStore
- = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER);
- setUserKeyProtection(userId, password,
- doVerifyPassword(password, willStore, true, 0, userId,
- null /* progressCallback */));
- mStorage.writePasswordHash(enrolledHandle, userId);
+ CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType);
+ mStorage.writeCredentialHash(willStore, userId);
+ // Refresh the auth token
+ setUserKeyProtection(userId, credential,
+ doVerifyCredential(credential, credentialType, true, 0, userId,
+ null /* progressCallback */));
fixateNewestUserKeyAuth(userId);
onUserLockChanged(userId);
} else {
- throw new RemoteException("Failed to enroll password");
+ throw new RemoteException("Failed to enroll " +
+ (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? "password"
+ : "pattern"));
}
}
@@ -1143,7 +1054,7 @@
}
if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
throw new RemoteException("Non-OK response verifying a credential we just set: "
- + vcr.getResponseCode());
+ + vcr.getResponseCode());
}
byte[] token = vcr.getPayload();
if (token == null) {
@@ -1240,90 +1151,61 @@
}
@Override
- public VerifyCredentialResponse checkPattern(String pattern, int userId,
+ public VerifyCredentialResponse checkCredential(String credential, int type, int userId,
ICheckCredentialProgressCallback progressCallback) throws RemoteException {
- return doVerifyPattern(pattern, false, 0, userId, progressCallback);
+ checkPasswordReadPermission(userId);
+ return doVerifyCredential(credential, type, false, 0, userId, progressCallback);
}
@Override
- public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId)
- throws RemoteException {
- return doVerifyPattern(pattern, true, challenge, userId, null /* progressCallback */);
+ public VerifyCredentialResponse verifyCredential(String credential, int type, long challenge,
+ int userId) throws RemoteException {
+ checkPasswordReadPermission(userId);
+ return doVerifyCredential(credential, type, true, challenge, userId,
+ null /* progressCallback */);
}
- private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge,
- long challenge, int userId, ICheckCredentialProgressCallback progressCallback)
- throws RemoteException {
- checkPasswordReadPermission(userId);
- if (TextUtils.isEmpty(pattern)) {
- throw new IllegalArgumentException("Pattern can't be null or empty");
- }
- CredentialHash storedHash = mStorage.readPatternHash(userId);
- return doVerifyPattern(pattern, storedHash, hasChallenge, challenge, userId,
- progressCallback);
- }
-
- private VerifyCredentialResponse doVerifyPattern(String pattern, CredentialHash storedHash,
+ /**
+ * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
+ * format.
+ */
+ private VerifyCredentialResponse doVerifyCredential(String credential, int credentialType,
boolean hasChallenge, long challenge, int userId,
ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+ if (TextUtils.isEmpty(credential)) {
+ throw new IllegalArgumentException("Credential can't be null or empty");
+ }
- if (TextUtils.isEmpty(pattern)) {
- throw new IllegalArgumentException("Pattern can't be null or empty");
- }
- boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
+ CredentialHash storedHash = mStorage.readCredentialHash(userId);
+ if (storedHash.type != credentialType) {
+ Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??"
+ + " stored: " + storedHash.type + " passed in: " + credentialType);
+ return VerifyCredentialResponse.ERROR;
+ }
- String patternToVerify;
- if (shouldReEnrollBaseZero) {
- patternToVerify = LockPatternUtils.patternStringToBaseZero(pattern);
- } else {
- patternToVerify = pattern;
- }
+ boolean shouldReEnrollBaseZero = storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
+ && storedHash.isBaseZeroPattern;
- VerifyCredentialResponse response = verifyCredential(userId, storedHash, patternToVerify,
- hasChallenge, challenge,
- new CredentialUtil() {
- @Override
- public void setCredential(String pattern, String oldPattern, int userId)
- throws RemoteException {
- setLockPatternInternal(pattern, oldPattern, userId);
- }
+ String credentialToVerify;
+ if (shouldReEnrollBaseZero) {
+ credentialToVerify = LockPatternUtils.patternStringToBaseZero(credential);
+ } else {
+ credentialToVerify = credential;
+ }
- @Override
- public byte[] toHash(String pattern, int userId) {
- return LockPatternUtils.patternToHash(
- LockPatternUtils.stringToPattern(pattern));
- }
+ VerifyCredentialResponse response = verifyCredential(userId, storedHash, credentialToVerify,
+ hasChallenge, challenge, progressCallback);
- @Override
- public String adjustForKeystore(String pattern) {
- return LockPatternUtils.patternStringToBaseZero(pattern);
- }
- },
- progressCallback
- );
+ if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
+ && shouldReEnrollBaseZero) {
+ setLockCredentialInternal(credential, storedHash.type, credentialToVerify, userId);
+ }
- if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
- && shouldReEnrollBaseZero) {
- setLockPatternInternal(pattern, patternToVerify, userId);
- }
-
- return response;
+ return response;
}
@Override
- public VerifyCredentialResponse checkPassword(String password, int userId,
- ICheckCredentialProgressCallback progressCallback) throws RemoteException {
- return doVerifyPassword(password, false, 0, userId, progressCallback);
- }
-
- @Override
- public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId)
- throws RemoteException {
- return doVerifyPassword(password, true, challenge, userId, null /* progressCallback */);
- }
-
- @Override
- public VerifyCredentialResponse verifyTiedProfileChallenge(String password, boolean isPattern,
+ public VerifyCredentialResponse verifyTiedProfileChallenge(String credential, int type,
long challenge, int userId) throws RemoteException {
checkPasswordReadPermission(userId);
if (!isManagedProfileWithUnifiedLock(userId)) {
@@ -1331,11 +1213,13 @@
}
final int parentProfileId = mUserManager.getProfileParent(userId).id;
// Unlock parent by using parent's challenge
- final VerifyCredentialResponse parentResponse = isPattern
- ? doVerifyPattern(password, true, challenge, parentProfileId,
- null /* progressCallback */)
- : doVerifyPassword(password, true, challenge, parentProfileId,
- null /* progressCallback */);
+ final VerifyCredentialResponse parentResponse = doVerifyCredential(
+ credential,
+ type,
+ true /* hasChallenge */,
+ challenge,
+ parentProfileId,
+ null /* progressCallback */);
if (parentResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
// Failed, just return parent's response
return parentResponse;
@@ -1343,7 +1227,9 @@
try {
// Unlock work profile, and work profile with unified lock must use password only
- return doVerifyPassword(getDecryptedPasswordForTiedProfile(userId), true,
+ return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId),
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ true,
challenge,
userId, null /* progressCallback */);
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
@@ -1355,48 +1241,14 @@
}
}
- private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
- long challenge, int userId, ICheckCredentialProgressCallback progressCallback)
- throws RemoteException {
- checkPasswordReadPermission(userId);
- if (TextUtils.isEmpty(password)) {
- throw new IllegalArgumentException("Password can't be null or empty");
- }
- CredentialHash storedHash = mStorage.readPasswordHash(userId);
- return doVerifyPassword(password, storedHash, hasChallenge, challenge, userId,
- progressCallback);
- }
-
- private VerifyCredentialResponse doVerifyPassword(String password, CredentialHash storedHash,
- boolean hasChallenge, long challenge, int userId,
- ICheckCredentialProgressCallback progressCallback) throws RemoteException {
- if (TextUtils.isEmpty(password)) {
- throw new IllegalArgumentException("Password can't be null or empty");
- }
- return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
- new CredentialUtil() {
- @Override
- public void setCredential(String password, String oldPassword, int userId)
- throws RemoteException {
- setLockPasswordInternal(password, oldPassword, userId);
- }
-
- @Override
- public byte[] toHash(String password, int userId) {
- return mLockPatternUtils.passwordToHash(password, userId);
- }
-
- @Override
- public String adjustForKeystore(String password) {
- return password;
- }
- }, progressCallback);
- }
-
+ /**
+ * Lowest-level credential verification routine that talks to GateKeeper. If verification
+ * passes, unlock the corresponding user and keystore. Also handles the migration from legacy
+ * hash to GK.
+ */
private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
- String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil,
- ICheckCredentialProgressCallback progressCallback)
- throws RemoteException {
+ String credential, boolean hasChallenge, long challenge,
+ ICheckCredentialProgressCallback progressCallback) throws RemoteException {
if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) {
// don't need to pass empty credentials to GateKeeper
return VerifyCredentialResponse.OK;
@@ -1411,10 +1263,18 @@
StrictMode.noteDiskRead();
if (storedHash.version == CredentialHash.VERSION_LEGACY) {
- byte[] hash = credentialUtil.toHash(credential, userId);
+ final byte[] hash;
+ if (storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
+ hash = LockPatternUtils.patternToHash(LockPatternUtils.stringToPattern(credential));
+ } else {
+ hash = mLockPatternUtils.passwordToHash(credential, userId);
+ }
if (Arrays.equals(hash, storedHash.hash)) {
- unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
-
+ if (storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
+ unlockKeystore(LockPatternUtils.patternStringToBaseZero(credential), userId);
+ } else {
+ unlockKeystore(credential, userId);
+ }
// Users with legacy credentials don't have credential-backed
// FBE keys, so just pass through a fake token/secret
Slog.i(TAG, "Unlocking user with fake token: " + userId);
@@ -1422,7 +1282,7 @@
unlockUser(userId, fakeToken, fakeToken);
// migrate credential to GateKeeper
- credentialUtil.setCredential(credential, null, userId);
+ setLockCredentialInternal(credential, storedHash.type, null, userId);
if (!hasChallenge) {
notifyActivePasswordMetricsAvailable(credential, userId);
return VerifyCredentialResponse.OK;
@@ -1441,7 +1301,7 @@
.verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes());
int responseCode = gateKeeperResponse.getResponseCode();
if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
- response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
+ response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
} else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
byte[] token = gateKeeperResponse.getPayload();
if (token == null) {
@@ -1458,7 +1318,6 @@
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
-
// credential has matched
if (progressCallback != null) {
@@ -1468,7 +1327,7 @@
unlockKeystore(credential, userId);
Slog.i(TAG, "Unlocking user " + userId +
- " with token length " + response.getPayload().length);
+ " with token length " + response.getPayload().length);
unlockUser(userId, response.getPayload(), secretFromCredential(credential));
if (isManagedProfileWithSeparatedLock(userId)) {
@@ -1477,7 +1336,7 @@
trustManager.setDeviceLockedForUser(userId, false);
}
if (shouldReEnroll) {
- credentialUtil.setCredential(credential, credential, userId);
+ setLockCredentialInternal(credential, storedHash.type, credential, userId);
}
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
if (response.getTimeout() > 0) {
@@ -1550,8 +1409,9 @@
try {
if (mLockPatternUtils.isLockPatternEnabled(userId)) {
- if (checkPattern(password, userId, null /* progressCallback */).getResponseCode()
- == GateKeeperResponse.RESPONSE_OK) {
+ if (checkCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, userId,
+ null /* progressCallback */)
+ .getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
return true;
}
}
@@ -1560,8 +1420,9 @@
try {
if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
- if (checkPassword(password, userId, null /* progressCallback */).getResponseCode()
- == GateKeeperResponse.RESPONSE_OK) {
+ if (checkCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, userId,
+ null /* progressCallback */)
+ .getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
return true;
}
}
@@ -1581,7 +1442,7 @@
try {
final IGateKeeperService gk = getGateKeeperService();
if (gk != null) {
- gk.clearSecureUserId(userId);
+ gk.clearSecureUserId(userId);
}
} catch (RemoteException ex) {
Slog.w(TAG, "unable to clear GK secure user id");
@@ -1661,28 +1522,28 @@
}
private static final String[] VALID_SETTINGS = new String[] {
- LockPatternUtils.LOCKOUT_PERMANENT_KEY,
- LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
- LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
- LockPatternUtils.PASSWORD_TYPE_KEY,
- LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
- LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
- LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
- LockPatternUtils.LOCKSCREEN_OPTIONS,
- LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
- LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
- LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
- LockPatternUtils.PASSWORD_HISTORY_KEY,
- Secure.LOCK_PATTERN_ENABLED,
- Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
- Secure.LOCK_PATTERN_VISIBLE,
- Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
+ LockPatternUtils.LOCKOUT_PERMANENT_KEY,
+ LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
+ LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
+ LockPatternUtils.PASSWORD_TYPE_KEY,
+ LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
+ LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
+ LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
+ LockPatternUtils.LOCKSCREEN_OPTIONS,
+ LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
+ LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
+ LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
+ LockPatternUtils.PASSWORD_HISTORY_KEY,
+ Secure.LOCK_PATTERN_ENABLED,
+ Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
+ Secure.LOCK_PATTERN_VISIBLE,
+ Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
};
// Reading these settings needs the contacts permission
private static final String[] READ_CONTACTS_PROTECTED_SETTINGS = new String[] {
- Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
- Secure.LOCK_SCREEN_OWNER_INFO
+ Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
+ Secure.LOCK_SCREEN_OWNER_INFO
};
// Reading these settings needs the same permission as checking the password
@@ -1694,8 +1555,8 @@
};
private static final String[] SETTINGS_TO_BACKUP = new String[] {
- Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
- Secure.LOCK_SCREEN_OWNER_INFO
+ Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
+ Secure.LOCK_SCREEN_OWNER_INFO
};
private IStorageManager getStorageManager() {
@@ -1720,8 +1581,7 @@
return mGateKeeperService;
}
- final IBinder service =
- ServiceManager.getService(Context.GATEKEEPER_SERVICE);
+ final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE);
if (service != null) {
service.linkToDeath(new GateKeeperDiedRecipient(), 0);
mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index ab91a73..3d973a0 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -16,7 +16,7 @@
package com.android.server;
-import com.android.internal.annotations.VisibleForTesting;
+import static android.content.Context.USER_SERVICE;
import android.content.ContentValues;
import android.content.Context;
@@ -29,14 +29,15 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
-import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.widget.LockPatternUtils;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
-import static android.content.Context.USER_SERVICE;
-
/**
* Storage for the lock settings service.
*/
@@ -72,29 +73,48 @@
private final Cache mCache = new Cache();
private final Object mFileWriteLock = new Object();
- private SparseArray<Integer> mStoredCredentialType;
-
- static class CredentialHash {
- static final int TYPE_NONE = -1;
- static final int TYPE_PATTERN = 1;
- static final int TYPE_PASSWORD = 2;
-
+ @VisibleForTesting
+ public static class CredentialHash {
static final int VERSION_LEGACY = 0;
static final int VERSION_GATEKEEPER = 1;
- CredentialHash(byte[] hash, int version) {
+ private CredentialHash(byte[] hash, int type, int version) {
+ if (type != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+ if (hash == null) {
+ throw new RuntimeException("Empty hash for CredentialHash");
+ }
+ } else /* type == LockPatternUtils.CREDENTIAL_TYPE_NONE */ {
+ if (hash != null) {
+ throw new RuntimeException("None type CredentialHash should not have hash");
+ }
+ }
this.hash = hash;
+ this.type = type;
this.version = version;
this.isBaseZeroPattern = false;
}
- CredentialHash(byte[] hash, boolean isBaseZeroPattern) {
+ private CredentialHash(byte[] hash, boolean isBaseZeroPattern) {
this.hash = hash;
+ this.type = LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
this.version = VERSION_GATEKEEPER;
this.isBaseZeroPattern = isBaseZeroPattern;
}
+ static CredentialHash create(byte[] hash, int type) {
+ if (type == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+ throw new RuntimeException("Bad type for CredentialHash");
+ }
+ return new CredentialHash(hash, type, VERSION_GATEKEEPER);
+ }
+
+ static CredentialHash createEmptyHash() {
+ return new CredentialHash(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
+ VERSION_GATEKEEPER);
+ }
+
byte[] hash;
+ int type;
int version;
boolean isBaseZeroPattern;
}
@@ -102,7 +122,6 @@
public LockSettingsStorage(Context context, Callback callback) {
mContext = context;
mOpenHelper = new DatabaseHelper(context, callback);
- mStoredCredentialType = new SparseArray<Integer>();
}
public void writeKeyValue(String key, String value, int userId) {
@@ -178,75 +197,62 @@
}
// Populate cache by reading the password and pattern files.
- readPasswordHash(userId);
- readPatternHash(userId);
+ readCredentialHash(userId);
}
- public int getStoredCredentialType(int userId) {
- final Integer cachedStoredCredentialType = mStoredCredentialType.get(userId);
- if (cachedStoredCredentialType != null) {
- return cachedStoredCredentialType.intValue();
- }
-
- int storedCredentialType;
- CredentialHash pattern = readPatternHash(userId);
- if (pattern == null) {
- if (readPasswordHash(userId) != null) {
- storedCredentialType = CredentialHash.TYPE_PASSWORD;
- } else {
- storedCredentialType = CredentialHash.TYPE_NONE;
- }
- } else {
- CredentialHash password = readPasswordHash(userId);
- if (password != null) {
- // Both will never be GateKeeper
- if (password.version == CredentialHash.VERSION_GATEKEEPER) {
- storedCredentialType = CredentialHash.TYPE_PASSWORD;
- } else {
- storedCredentialType = CredentialHash.TYPE_PATTERN;
- }
- } else {
- storedCredentialType = CredentialHash.TYPE_PATTERN;
- }
- }
- mStoredCredentialType.put(userId, storedCredentialType);
- return storedCredentialType;
- }
-
-
- public CredentialHash readPasswordHash(int userId) {
+ private CredentialHash readPasswordHashIfExists(int userId) {
byte[] stored = readFile(getLockPasswordFilename(userId));
- if (stored != null && stored.length > 0) {
- return new CredentialHash(stored, CredentialHash.VERSION_GATEKEEPER);
+ if (!ArrayUtils.isEmpty(stored)) {
+ return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ CredentialHash.VERSION_GATEKEEPER);
}
stored = readFile(getLegacyLockPasswordFilename(userId));
- if (stored != null && stored.length > 0) {
- return new CredentialHash(stored, CredentialHash.VERSION_LEGACY);
+ if (!ArrayUtils.isEmpty(stored)) {
+ return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ CredentialHash.VERSION_LEGACY);
}
-
return null;
}
- public CredentialHash readPatternHash(int userId) {
+ private CredentialHash readPatternHashIfExists(int userId) {
byte[] stored = readFile(getLockPatternFilename(userId));
- if (stored != null && stored.length > 0) {
- return new CredentialHash(stored, CredentialHash.VERSION_GATEKEEPER);
+ if (!ArrayUtils.isEmpty(stored)) {
+ return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ CredentialHash.VERSION_GATEKEEPER);
}
stored = readFile(getBaseZeroLockPatternFilename(userId));
- if (stored != null && stored.length > 0) {
+ if (!ArrayUtils.isEmpty(stored)) {
return new CredentialHash(stored, true);
}
stored = readFile(getLegacyLockPatternFilename(userId));
- if (stored != null && stored.length > 0) {
- return new CredentialHash(stored, CredentialHash.VERSION_LEGACY);
+ if (!ArrayUtils.isEmpty(stored)) {
+ return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ CredentialHash.VERSION_LEGACY);
}
-
return null;
}
+ public CredentialHash readCredentialHash(int userId) {
+ CredentialHash passwordHash = readPasswordHashIfExists(userId);
+ CredentialHash patternHash = readPatternHashIfExists(userId);
+ if (passwordHash != null && patternHash != null) {
+ if (passwordHash.version == CredentialHash.VERSION_GATEKEEPER) {
+ return passwordHash;
+ } else {
+ return patternHash;
+ }
+ } else if (passwordHash != null) {
+ return passwordHash;
+ } else if (patternHash != null) {
+ return patternHash;
+ } else {
+ return CredentialHash.createEmptyHash();
+ }
+ }
+
public void removeChildProfileLock(int userId) {
if (DEBUG)
Slog.e(TAG, "Remove child profile lock for user: " + userId);
@@ -355,26 +361,17 @@
}
}
- public void writePatternHash(byte[] hash, int userId) {
- mStoredCredentialType.put(userId, hash == null ? CredentialHash.TYPE_NONE
- : CredentialHash.TYPE_PATTERN);
- writeFile(getLockPatternFilename(userId), hash);
- clearPasswordHash(userId);
- }
+ public void writeCredentialHash(CredentialHash hash, int userId) {
+ byte[] patternHash = null;
+ byte[] passwordHash = null;
- private void clearPatternHash(int userId) {
- writeFile(getLockPatternFilename(userId), null);
- }
-
- public void writePasswordHash(byte[] hash, int userId) {
- mStoredCredentialType.put(userId, hash == null ? CredentialHash.TYPE_NONE
- : CredentialHash.TYPE_PASSWORD);
- writeFile(getLockPasswordFilename(userId), hash);
- clearPatternHash(userId);
- }
-
- private void clearPasswordHash(int userId) {
- writeFile(getLockPasswordFilename(userId), null);
+ if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
+ passwordHash = hash.hash;
+ } else if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
+ patternHash = hash.hash;
+ }
+ writeFile(getLockPasswordFilename(userId), passwordHash);
+ writeFile(getLockPatternFilename(userId), patternHash);
}
@VisibleForTesting
diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
index 7d28e39..9d52153 100644
--- a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
@@ -16,6 +16,10 @@
package com.android.server;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.UserInfo;
@@ -24,13 +28,23 @@
import android.os.UserManager;
import android.test.AndroidTestCase;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LockSettingsStorage.CredentialHash;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
+/**
+ * runtest frameworks-services -c com.android.server.LockSettingsStorageTests
+ */
public class LockSettingsStorageTests extends AndroidTestCase {
+ private final byte[] PASSWORD_0 = "thepassword0".getBytes();
+ private final byte[] PASSWORD_1 = "password1".getBytes();
+ private final byte[] PATTERN_0 = "123654".getBytes();
+ private final byte[] PATTERN_1 = "147852369".getBytes();
+
LockSettingsStorage mStorage;
File mStorageDir;
@@ -47,24 +61,16 @@
assertTrue(!mDb.exists() || mDb.delete());
final Context ctx = getContext();
+ final UserManager mockUserManager = mock(UserManager.class);
+ // User 2 is a profile of user 1.
+ when(mockUserManager.getProfileParent(eq(2))).thenReturn(new UserInfo(1, "name", 0));
+ // User 3 is a profile of user 0.
+ when(mockUserManager.getProfileParent(eq(3))).thenReturn(new UserInfo(0, "name", 0));
setContext(new ContextWrapper(ctx) {
@Override
public Object getSystemService(String name) {
if (USER_SERVICE.equals(name)) {
- return new UserManager(ctx, null) {
- @Override
- public UserInfo getProfileParent(int userHandle) {
- if (userHandle == 2) {
- // User 2 is a profile of user 1.
- return new UserInfo(1, "name", 0);
- }
- if (userHandle == 3) {
- // User 3 is a profile of user 0.
- return new UserInfo(0, "name", 0);
- }
- return null;
- }
- };
+ return mockUserManager;
}
return super.getSystemService(name);
}
@@ -201,151 +207,149 @@
public void testRemoveUser() {
mStorage.writeKeyValue("key", "value", 0);
- mStorage.writePasswordHash(new byte[]{1}, 0);
- mStorage.writePatternHash(new byte[]{2}, 0);
+ writePasswordBytes(PASSWORD_0, 0);
+ writePatternBytes(PATTERN_0, 0);
mStorage.writeKeyValue("key", "value", 1);
- mStorage.writePasswordHash(new byte[]{1}, 1);
- mStorage.writePatternHash(new byte[]{2}, 1);
+ writePasswordBytes(PASSWORD_1, 1);
+ writePatternBytes(PATTERN_1, 1);
mStorage.removeUser(0);
assertEquals("value", mStorage.readKeyValue("key", "default", 1));
assertEquals("default", mStorage.readKeyValue("key", "default", 0));
- assertNotNull(mStorage.readPasswordHash(1));
- assertNull(mStorage.readPasswordHash(0));
- assertNotNull(mStorage.readPatternHash(1));
- assertNull(mStorage.readPatternHash(0));
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_NONE, mStorage.readCredentialHash(0).type);
+ assertPatternBytes(PATTERN_1, 1);
}
- public void testPassword_Default() {
- assertNull(mStorage.readPasswordHash(0));
+ public void testCredential_Default() {
+ assertEquals(mStorage.readCredentialHash(0).type, LockPatternUtils.CREDENTIAL_TYPE_NONE);
}
public void testPassword_Write() {
- mStorage.writePasswordHash("thepassword".getBytes(), 0);
+ writePasswordBytes(PASSWORD_0, 0);
- assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0).hash);
+ assertPasswordBytes(PASSWORD_0, 0);
mStorage.clearCache();
- assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0).hash);
+ assertPasswordBytes(PASSWORD_0, 0);
}
public void testPassword_WriteProfileWritesParent() {
- mStorage.writePasswordHash("parentpasswordd".getBytes(), 1);
- mStorage.writePasswordHash("profilepassword".getBytes(), 2);
+ writePasswordBytes(PASSWORD_0, 1);
+ writePasswordBytes(PASSWORD_1, 2);
- assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1).hash);
- assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2).hash);
+ assertPasswordBytes(PASSWORD_0, 1);
+ assertPasswordBytes(PASSWORD_1, 2);
mStorage.clearCache();
- assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1).hash);
- assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2).hash);
+ assertPasswordBytes(PASSWORD_0, 1);
+ assertPasswordBytes(PASSWORD_1, 2);
}
public void testLockType_WriteProfileWritesParent() {
- mStorage.writePasswordHash("parentpassword".getBytes(), 10);
- mStorage.writePatternHash("12345678".getBytes(), 20);
+ writePasswordBytes(PASSWORD_0, 10);
+ writePatternBytes(PATTERN_0, 20);
- assertEquals(2, mStorage.getStoredCredentialType(10));
- assertEquals(1, mStorage.getStoredCredentialType(20));
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ mStorage.readCredentialHash(10).type);
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ mStorage.readCredentialHash(20).type);
mStorage.clearCache();
- assertEquals(2, mStorage.getStoredCredentialType(10));
- assertEquals(1, mStorage.getStoredCredentialType(20));
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ mStorage.readCredentialHash(10).type);
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ mStorage.readCredentialHash(20).type);
+ }
+
+ public void testPassword_WriteParentWritesProfile() {
+ writePasswordBytes(PASSWORD_0, 2);
+ writePasswordBytes(PASSWORD_1, 1);
+
+ assertPasswordBytes(PASSWORD_1, 1);
+ assertPasswordBytes(PASSWORD_0, 2);
+ mStorage.clearCache();
+ assertPasswordBytes(PASSWORD_1, 1);
+ assertPasswordBytes(PASSWORD_0, 2);
}
public void testProfileLock_ReadWriteChildProfileLock() {
assertFalse(mStorage.hasChildProfileLock(20));
- mStorage.writeChildProfileLock(20, "profilepassword".getBytes());
- assertArrayEquals("profilepassword".getBytes(), mStorage.readChildProfileLock(20));
+ mStorage.writeChildProfileLock(20, PASSWORD_0);
+ assertArrayEquals(PASSWORD_0, mStorage.readChildProfileLock(20));
assertTrue(mStorage.hasChildProfileLock(20));
mStorage.clearCache();
- assertArrayEquals("profilepassword".getBytes(), mStorage.readChildProfileLock(20));
+ assertArrayEquals(PASSWORD_0, mStorage.readChildProfileLock(20));
assertTrue(mStorage.hasChildProfileLock(20));
}
- public void testPassword_WriteParentWritesProfile() {
- mStorage.writePasswordHash("profilepassword".getBytes(), 2);
- mStorage.writePasswordHash("parentpasswordd".getBytes(), 1);
-
- assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1).hash);
- assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2).hash);
- mStorage.clearCache();
- assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1).hash);
- assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2).hash);
- }
-
- public void testPattern_Default() {
- assertNull(mStorage.readPasswordHash(0));
- }
-
public void testPattern_Write() {
- mStorage.writePatternHash("thepattern".getBytes(), 0);
+ writePatternBytes(PATTERN_0, 0);
- assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0).hash);
+ assertPatternBytes(PATTERN_0, 0);
mStorage.clearCache();
- assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0).hash);
+ assertPatternBytes(PATTERN_0, 0);
}
public void testPattern_WriteProfileWritesParent() {
- mStorage.writePatternHash("parentpatternn".getBytes(), 1);
- mStorage.writePatternHash("profilepattern".getBytes(), 2);
+ writePatternBytes(PATTERN_0, 1);
+ writePatternBytes(PATTERN_1, 2);
- assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1).hash);
- assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2).hash);
+ assertPatternBytes(PATTERN_0, 1);
+ assertPatternBytes(PATTERN_1, 2);
mStorage.clearCache();
- assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1).hash);
- assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2).hash);
+ assertPatternBytes(PATTERN_0, 1);
+ assertPatternBytes(PATTERN_1, 2);
}
public void testPattern_WriteParentWritesProfile() {
- mStorage.writePatternHash("profilepattern".getBytes(), 2);
- mStorage.writePatternHash("parentpatternn".getBytes(), 1);
+ writePatternBytes(PATTERN_1, 2);
+ writePatternBytes(PATTERN_0, 1);
- assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1).hash);
- assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2).hash);
+ assertPatternBytes(PATTERN_0, 1);
+ assertPatternBytes(PATTERN_1, 2);
mStorage.clearCache();
- assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1).hash);
- assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2).hash);
+ assertPatternBytes(PATTERN_0, 1);
+ assertPatternBytes(PATTERN_1, 2);
}
public void testPrefetch() {
mStorage.writeKeyValue("key", "toBeFetched", 0);
- mStorage.writePatternHash("pattern".getBytes(), 0);
- mStorage.writePasswordHash("password".getBytes(), 0);
+ writePatternBytes(PATTERN_0, 0);
mStorage.clearCache();
mStorage.prefetchUser(0);
assertEquals("toBeFetched", mStorage.readKeyValue("key", "default", 0));
- assertArrayEquals("pattern".getBytes(), mStorage.readPatternHash(0).hash);
- assertArrayEquals("password".getBytes(), mStorage.readPasswordHash(0).hash);
+ assertPatternBytes(PATTERN_0, 0);
}
public void testFileLocation_Owner() {
LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
- assertEquals("/data/system/gesture.key", storage.getLockPatternFilename(0));
- assertEquals("/data/system/password.key", storage.getLockPasswordFilename(0));
+ assertEquals("/data/system/gesture.key", storage.getLegacyLockPatternFilename(0));
+ assertEquals("/data/system/password.key", storage.getLegacyLockPasswordFilename(0));
+ assertEquals("/data/system/gatekeeper.pattern.key", storage.getLockPatternFilename(0));
+ assertEquals("/data/system/gatekeeper.password.key", storage.getLockPasswordFilename(0));
}
public void testFileLocation_SecondaryUser() {
LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
- assertEquals("/data/system/users/1/gesture.key", storage.getLockPatternFilename(1));
- assertEquals("/data/system/users/1/password.key", storage.getLockPasswordFilename(1));
+ assertEquals("/data/system/users/1/gatekeeper.pattern.key", storage.getLockPatternFilename(1));
+ assertEquals("/data/system/users/1/gatekeeper.password.key", storage.getLockPasswordFilename(1));
}
public void testFileLocation_ProfileToSecondary() {
LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
- assertEquals("/data/system/users/1/gesture.key", storage.getLockPatternFilename(2));
- assertEquals("/data/system/users/1/password.key", storage.getLockPasswordFilename(2));
+ assertEquals("/data/system/users/2/gatekeeper.pattern.key", storage.getLockPatternFilename(2));
+ assertEquals("/data/system/users/2/gatekeeper.password.key", storage.getLockPasswordFilename(2));
}
public void testFileLocation_ProfileToOwner() {
LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
- assertEquals("/data/system/gesture.key", storage.getLockPatternFilename(3));
- assertEquals("/data/system/password.key", storage.getLockPasswordFilename(3));
+ assertEquals("/data/system/users/3/gatekeeper.pattern.key", storage.getLockPatternFilename(3));
+ assertEquals("/data/system/users/3/gatekeeper.password.key", storage.getLockPasswordFilename(3));
}
private static void assertArrayEquals(byte[] expected, byte[] actual) {
@@ -354,4 +358,26 @@
"> but was:<" + Arrays.toString(actual) + ">");
}
}
+
+ private void writePasswordBytes(byte[] password, int userId) {
+ mStorage.writeCredentialHash(CredentialHash.create(
+ password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId);
+ }
+
+ private void writePatternBytes(byte[] pattern, int userId) {
+ mStorage.writeCredentialHash(CredentialHash.create(
+ pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN), userId);
+ }
+
+ private void assertPasswordBytes(byte[] password, int userId) {
+ CredentialHash cred = mStorage.readCredentialHash(userId);
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, cred.type);
+ assertArrayEquals(password, cred.hash);
+ }
+
+ private void assertPatternBytes(byte[] pattern, int userId) {
+ CredentialHash cred = mStorage.readCredentialHash(userId);
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, cred.type);
+ assertArrayEquals(pattern, cred.hash);
+ }
}