Loading core/java/com/android/internal/widget/LockscreenCredential.java +64 −24 Original line number Diff line number Diff line Loading @@ -78,6 +78,10 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { // needed to do that check, since the conversion to mCredential may have been lossy. private final boolean mHasInvalidChars; // Whether the credential is a unified profile password, i.e. a long random password the system // generates and manages transparently to the user. Implies mType == CREDENTIAL_TYPE_PASSWORD. private final boolean mIsUnifiedProfilePassword; /** * Private constructor, use static builder methods instead. * Loading @@ -85,7 +89,11 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { * array and pass it in here. LockscreenCredential will only store the reference internally * without copying. This is to minimize the number of extra copies introduced. */ private LockscreenCredential(int type, byte[] credential, boolean hasInvalidChars) { private LockscreenCredential( int type, byte[] credential, boolean hasInvalidChars, boolean isUnifiedProfilePassword) { Objects.requireNonNull(credential); if (type == CREDENTIAL_TYPE_NONE) { Preconditions.checkArgument(credential.length == 0); Loading @@ -102,28 +110,37 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { // LockscreenCredential object to be constructed so that the validation logic can run, // even though the validation logic will ultimately reject the credential as too short. } Preconditions.checkState(!isUnifiedProfilePassword || type == CREDENTIAL_TYPE_PASSWORD); mType = type; mCredential = credential; mHasInvalidChars = hasInvalidChars; mIsUnifiedProfilePassword = isUnifiedProfilePassword; } private LockscreenCredential(int type, CharSequence credential) { this(type, charsToBytesTruncating(credential), hasInvalidChars(credential)); this( type, charsToBytesTruncating(credential), hasInvalidChars(credential), /* isUnifiedProfilePassword= */ false); } /** * Creates a LockscreenCredential object representing a none credential. */ public static LockscreenCredential createNone() { return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0], false); return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0], false, false); } /** * Creates a LockscreenCredential object representing the given pattern. */ public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) { return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN, LockPatternUtils.patternToByteArray(pattern), /* hasInvalidChars= */ false); return new LockscreenCredential( CREDENTIAL_TYPE_PATTERN, LockPatternUtils.patternToByteArray(pattern), /* hasInvalidChars= */ false, /* isUnifiedProfilePassword= */ false); } /** Loading @@ -140,8 +157,11 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { * can then supersede the isLockTiedToParent argument in various places in LSS. */ public static LockscreenCredential createUnifiedProfilePassword(@NonNull byte[] password) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, copyOfArrayNonMovable(password), /* hasInvalidChars= */ false); return new LockscreenCredential( CREDENTIAL_TYPE_PASSWORD, copyOfArrayNonMovable(password), /* hasInvalidChars= */ false, /* isUnifiedProfilePassword= */ true); } /** Loading Loading @@ -234,11 +254,24 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { return mHasInvalidChars; } /** * Returns whether this is a unified profile password credential. * * <p>Note that currently "unified profile password" is not a dedicated credential type, but * rather a subset of the "password" type. {@link #isPassword()} also returns true for them. */ public boolean isUnifiedProfilePassword() { ensureNotZeroized(); return mIsUnifiedProfilePassword; } /** Create a copy of the credential */ public LockscreenCredential duplicate() { return new LockscreenCredential(mType, return new LockscreenCredential( mType, mCredential != null ? copyOfArrayNonMovable(mCredential) : null, mHasInvalidChars); mHasInvalidChars, mIsUnifiedProfilePassword); } /** Loading Loading @@ -367,6 +400,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { dest.writeInt(mType); dest.writeByteArray(mCredential); dest.writeBoolean(mHasInvalidChars); dest.writeBoolean(mIsUnifiedProfilePassword); } public static final Parcelable.Creator<LockscreenCredential> CREATOR = Loading @@ -374,7 +408,10 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { @Override public LockscreenCredential createFromParcel(Parcel source) { return new LockscreenCredential(source.readInt(), source.createByteArray(), return new LockscreenCredential( source.readInt(), source.createByteArray(), source.readBoolean(), source.readBoolean()); } Loading Loading @@ -402,7 +439,8 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { @Override public int hashCode() { // Effective Java — Always override hashCode when you override equals return Objects.hash(mType, Arrays.hashCode(mCredential), mHasInvalidChars); return Objects.hash( mType, Arrays.hashCode(mCredential), mHasInvalidChars, mIsUnifiedProfilePassword); } @Override Loading @@ -410,8 +448,10 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { if (o == this) return true; if (!(o instanceof LockscreenCredential)) return false; final LockscreenCredential other = (LockscreenCredential) o; return mType == other.mType && Arrays.equals(mCredential, other.mCredential) && mHasInvalidChars == other.mHasInvalidChars; return mType == other.mType && Arrays.equals(mCredential, other.mCredential) && mHasInvalidChars == other.mHasInvalidChars && mIsUnifiedProfilePassword == other.mIsUnifiedProfilePassword; } private static boolean hasInvalidChars(CharSequence chars) { Loading core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java +40 −0 Original line number Diff line number Diff line Loading @@ -31,7 +31,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; import java.util.Random; /** atest FrameworksCoreTests:LockscreenCredentialTest */ @RunWith(AndroidJUnit4.class) public class LockscreenCredentialTest { Loading @@ -46,6 +48,7 @@ public class LockscreenCredentialTest { assertFalse(none.isPin()); assertFalse(none.isPassword()); assertFalse(none.isPattern()); assertFalse(none.isUnifiedProfilePassword()); assertFalse(none.hasInvalidChars()); none.validateBasicRequirements(); } Loading @@ -61,6 +64,7 @@ public class LockscreenCredentialTest { assertFalse(pin.isNone()); assertFalse(pin.isPassword()); assertFalse(pin.isPattern()); assertFalse(pin.isUnifiedProfilePassword()); assertFalse(pin.hasInvalidChars()); pin.validateBasicRequirements(); } Loading @@ -76,6 +80,7 @@ public class LockscreenCredentialTest { assertFalse(password.isNone()); assertFalse(password.isPin()); assertFalse(password.isPattern()); assertFalse(password.isUnifiedProfilePassword()); assertFalse(password.hasInvalidChars()); password.validateBasicRequirements(); } Loading @@ -97,10 +102,30 @@ public class LockscreenCredentialTest { assertFalse(pattern.isNone()); assertFalse(pattern.isPin()); assertFalse(pattern.isPassword()); assertFalse(pattern.isUnifiedProfilePassword()); assertFalse(pattern.hasInvalidChars()); pattern.validateBasicRequirements(); } @Test public void testUnifiedProfilePasswordCredential() { final byte[] passwordBytes = new byte[40]; new Random().nextBytes(passwordBytes); final LockscreenCredential password = LockscreenCredential.createUnifiedProfilePassword(passwordBytes); assertTrue(password.isPassword()); assertTrue(password.isUnifiedProfilePassword()); assertEquals(passwordBytes.length, password.size()); assertArrayEquals(passwordBytes, password.getCredential()); assertFalse(password.isNone()); assertFalse(password.isPin()); assertFalse(password.isPattern()); assertFalse(password.hasInvalidChars()); password.validateBasicRequirements(); } // Constructing a LockscreenCredential with a too-short length, even 0, should not throw an // exception. This is because LockscreenCredential needs to be able to represent a request to // set a credential that is too short. Loading Loading @@ -196,6 +221,11 @@ public class LockscreenCredentialTest { password.hasInvalidChars(); fail("Sanitized credential still accessible"); } catch (IllegalStateException expected) { } try { password.isUnifiedProfilePassword(); fail("Sanitized credential still accessible"); } catch (IllegalStateException expected) { } try { password.getCredential(); fail("Sanitized credential still accessible"); Loading Loading @@ -242,6 +272,12 @@ public class LockscreenCredentialTest { // the same byte[] (due to the truncation bug) but different values of mHasInvalidChars. assertNotEquals(LockscreenCredential.createPassword("™™™™"), LockscreenCredential.createPassword("\"\"\"\"")); // Test that mIsUnifiedProfilePassword is compared. final String password = "password"; assertNotEquals( LockscreenCredential.createPassword(password), LockscreenCredential.createUnifiedProfilePassword(password.getBytes())); } @Test Loading @@ -260,6 +296,10 @@ public class LockscreenCredentialTest { // Test that mHasInvalidChars is duplicated. credential = LockscreenCredential.createPassword("™™™™"); assertEquals(credential, credential.duplicate()); // Test that mIsUnifiedProfilePassword is duplicated. credential = LockscreenCredential.createUnifiedProfilePassword("password".getBytes()); assertEquals(credential, credential.duplicate()); } @Test Loading Loading
core/java/com/android/internal/widget/LockscreenCredential.java +64 −24 Original line number Diff line number Diff line Loading @@ -78,6 +78,10 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { // needed to do that check, since the conversion to mCredential may have been lossy. private final boolean mHasInvalidChars; // Whether the credential is a unified profile password, i.e. a long random password the system // generates and manages transparently to the user. Implies mType == CREDENTIAL_TYPE_PASSWORD. private final boolean mIsUnifiedProfilePassword; /** * Private constructor, use static builder methods instead. * Loading @@ -85,7 +89,11 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { * array and pass it in here. LockscreenCredential will only store the reference internally * without copying. This is to minimize the number of extra copies introduced. */ private LockscreenCredential(int type, byte[] credential, boolean hasInvalidChars) { private LockscreenCredential( int type, byte[] credential, boolean hasInvalidChars, boolean isUnifiedProfilePassword) { Objects.requireNonNull(credential); if (type == CREDENTIAL_TYPE_NONE) { Preconditions.checkArgument(credential.length == 0); Loading @@ -102,28 +110,37 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { // LockscreenCredential object to be constructed so that the validation logic can run, // even though the validation logic will ultimately reject the credential as too short. } Preconditions.checkState(!isUnifiedProfilePassword || type == CREDENTIAL_TYPE_PASSWORD); mType = type; mCredential = credential; mHasInvalidChars = hasInvalidChars; mIsUnifiedProfilePassword = isUnifiedProfilePassword; } private LockscreenCredential(int type, CharSequence credential) { this(type, charsToBytesTruncating(credential), hasInvalidChars(credential)); this( type, charsToBytesTruncating(credential), hasInvalidChars(credential), /* isUnifiedProfilePassword= */ false); } /** * Creates a LockscreenCredential object representing a none credential. */ public static LockscreenCredential createNone() { return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0], false); return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0], false, false); } /** * Creates a LockscreenCredential object representing the given pattern. */ public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) { return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN, LockPatternUtils.patternToByteArray(pattern), /* hasInvalidChars= */ false); return new LockscreenCredential( CREDENTIAL_TYPE_PATTERN, LockPatternUtils.patternToByteArray(pattern), /* hasInvalidChars= */ false, /* isUnifiedProfilePassword= */ false); } /** Loading @@ -140,8 +157,11 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { * can then supersede the isLockTiedToParent argument in various places in LSS. */ public static LockscreenCredential createUnifiedProfilePassword(@NonNull byte[] password) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, copyOfArrayNonMovable(password), /* hasInvalidChars= */ false); return new LockscreenCredential( CREDENTIAL_TYPE_PASSWORD, copyOfArrayNonMovable(password), /* hasInvalidChars= */ false, /* isUnifiedProfilePassword= */ true); } /** Loading Loading @@ -234,11 +254,24 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { return mHasInvalidChars; } /** * Returns whether this is a unified profile password credential. * * <p>Note that currently "unified profile password" is not a dedicated credential type, but * rather a subset of the "password" type. {@link #isPassword()} also returns true for them. */ public boolean isUnifiedProfilePassword() { ensureNotZeroized(); return mIsUnifiedProfilePassword; } /** Create a copy of the credential */ public LockscreenCredential duplicate() { return new LockscreenCredential(mType, return new LockscreenCredential( mType, mCredential != null ? copyOfArrayNonMovable(mCredential) : null, mHasInvalidChars); mHasInvalidChars, mIsUnifiedProfilePassword); } /** Loading Loading @@ -367,6 +400,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { dest.writeInt(mType); dest.writeByteArray(mCredential); dest.writeBoolean(mHasInvalidChars); dest.writeBoolean(mIsUnifiedProfilePassword); } public static final Parcelable.Creator<LockscreenCredential> CREATOR = Loading @@ -374,7 +408,10 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { @Override public LockscreenCredential createFromParcel(Parcel source) { return new LockscreenCredential(source.readInt(), source.createByteArray(), return new LockscreenCredential( source.readInt(), source.createByteArray(), source.readBoolean(), source.readBoolean()); } Loading Loading @@ -402,7 +439,8 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { @Override public int hashCode() { // Effective Java — Always override hashCode when you override equals return Objects.hash(mType, Arrays.hashCode(mCredential), mHasInvalidChars); return Objects.hash( mType, Arrays.hashCode(mCredential), mHasInvalidChars, mIsUnifiedProfilePassword); } @Override Loading @@ -410,8 +448,10 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { if (o == this) return true; if (!(o instanceof LockscreenCredential)) return false; final LockscreenCredential other = (LockscreenCredential) o; return mType == other.mType && Arrays.equals(mCredential, other.mCredential) && mHasInvalidChars == other.mHasInvalidChars; return mType == other.mType && Arrays.equals(mCredential, other.mCredential) && mHasInvalidChars == other.mHasInvalidChars && mIsUnifiedProfilePassword == other.mIsUnifiedProfilePassword; } private static boolean hasInvalidChars(CharSequence chars) { Loading
core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java +40 −0 Original line number Diff line number Diff line Loading @@ -31,7 +31,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; import java.util.Random; /** atest FrameworksCoreTests:LockscreenCredentialTest */ @RunWith(AndroidJUnit4.class) public class LockscreenCredentialTest { Loading @@ -46,6 +48,7 @@ public class LockscreenCredentialTest { assertFalse(none.isPin()); assertFalse(none.isPassword()); assertFalse(none.isPattern()); assertFalse(none.isUnifiedProfilePassword()); assertFalse(none.hasInvalidChars()); none.validateBasicRequirements(); } Loading @@ -61,6 +64,7 @@ public class LockscreenCredentialTest { assertFalse(pin.isNone()); assertFalse(pin.isPassword()); assertFalse(pin.isPattern()); assertFalse(pin.isUnifiedProfilePassword()); assertFalse(pin.hasInvalidChars()); pin.validateBasicRequirements(); } Loading @@ -76,6 +80,7 @@ public class LockscreenCredentialTest { assertFalse(password.isNone()); assertFalse(password.isPin()); assertFalse(password.isPattern()); assertFalse(password.isUnifiedProfilePassword()); assertFalse(password.hasInvalidChars()); password.validateBasicRequirements(); } Loading @@ -97,10 +102,30 @@ public class LockscreenCredentialTest { assertFalse(pattern.isNone()); assertFalse(pattern.isPin()); assertFalse(pattern.isPassword()); assertFalse(pattern.isUnifiedProfilePassword()); assertFalse(pattern.hasInvalidChars()); pattern.validateBasicRequirements(); } @Test public void testUnifiedProfilePasswordCredential() { final byte[] passwordBytes = new byte[40]; new Random().nextBytes(passwordBytes); final LockscreenCredential password = LockscreenCredential.createUnifiedProfilePassword(passwordBytes); assertTrue(password.isPassword()); assertTrue(password.isUnifiedProfilePassword()); assertEquals(passwordBytes.length, password.size()); assertArrayEquals(passwordBytes, password.getCredential()); assertFalse(password.isNone()); assertFalse(password.isPin()); assertFalse(password.isPattern()); assertFalse(password.hasInvalidChars()); password.validateBasicRequirements(); } // Constructing a LockscreenCredential with a too-short length, even 0, should not throw an // exception. This is because LockscreenCredential needs to be able to represent a request to // set a credential that is too short. Loading Loading @@ -196,6 +221,11 @@ public class LockscreenCredentialTest { password.hasInvalidChars(); fail("Sanitized credential still accessible"); } catch (IllegalStateException expected) { } try { password.isUnifiedProfilePassword(); fail("Sanitized credential still accessible"); } catch (IllegalStateException expected) { } try { password.getCredential(); fail("Sanitized credential still accessible"); Loading Loading @@ -242,6 +272,12 @@ public class LockscreenCredentialTest { // the same byte[] (due to the truncation bug) but different values of mHasInvalidChars. assertNotEquals(LockscreenCredential.createPassword("™™™™"), LockscreenCredential.createPassword("\"\"\"\"")); // Test that mIsUnifiedProfilePassword is compared. final String password = "password"; assertNotEquals( LockscreenCredential.createPassword(password), LockscreenCredential.createUnifiedProfilePassword(password.getBytes())); } @Test Loading @@ -260,6 +296,10 @@ public class LockscreenCredentialTest { // Test that mHasInvalidChars is duplicated. credential = LockscreenCredential.createPassword("™™™™"); assertEquals(credential, credential.duplicate()); // Test that mIsUnifiedProfilePassword is duplicated. credential = LockscreenCredential.createUnifiedProfilePassword("password".getBytes()); assertEquals(credential, credential.duplicate()); } @Test Loading