diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index dfcde3dbee894..406505517bcc5 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -57,6 +57,7 @@ interface ILockSettings { void removeGatekeeperPasswordHandle(long gatekeeperPasswordHandle); int getCredentialType(int userId); int getPinLength(int userId); + boolean refreshStoredPinLength(int userId); byte[] getHashFactor(in LockscreenCredential currentCredential, int userId); void setSeparateProfileChallengeEnabled(int userId, boolean enabled, in LockscreenCredential managedUserPassword); boolean getSeparateProfileChallengeEnabled(int userId); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 38632d15e857c..fbad4b9fd377b 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -622,6 +622,24 @@ public class LockPatternUtils { return PIN_LENGTH_UNAVAILABLE; } } + + /** + * This method saves the pin length value to disk based on the user's auto pin + * confirmation flag setting. If the auto pin confirmation flag is disabled, or if the + * user does not have a PIN setup, or if length of PIN is less than minimum storable PIN length + * value, the pin length value is set to PIN_LENGTH_UNAVAILABLE. Otherwise, if the + * flag is enabled, the pin length value is set to the actual length of the user's PIN. + * @param userId user id of the user whose pin length we want to save + * @return true/false depending on whether PIN length has been saved or not + */ + public boolean refreshStoredPinLength(int userId) { + try { + return getLockSettings().refreshStoredPinLength(userId); + } catch (RemoteException e) { + Log.e(TAG, "Could not store PIN length on disk " + e); + return false; + } + } /** * Records that the user has chosen a pattern at some time, even if the pattern is * currently cleared. diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 0da94ff6aa0fa..7c99f8279d219 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -116,6 +116,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; +import android.util.Log; import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; @@ -1228,6 +1229,26 @@ public class LockSettingsService extends ILockSettings.Stub { } } + /** + * {@link LockPatternUtils#refreshStoredPinLength(int)} + * @param userId user id of the user whose pin length we want to save + * @return true/false depending on whether PIN length has been saved or not + */ + @Override + public boolean refreshStoredPinLength(int userId) { + checkPasswordHavePermission(); + synchronized (mSpManager) { + PasswordMetrics passwordMetrics = getUserPasswordMetrics(userId); + if (passwordMetrics != null) { + final long protectorId = getCurrentLskfBasedProtectorId(userId); + return mSpManager.refreshPinLengthOnDisk(passwordMetrics, protectorId, userId); + } else { + Log.w(TAG, "PasswordMetrics is not available"); + return false; + } + } + } + /** * This API is cached; whenever the result would change, * {@link com.android.internal.widget.LockPatternUtils#invalidateCredentialTypeCache} diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index de3a7ef4ed61e..731ecadc1372f 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -120,6 +120,11 @@ class LockSettingsStorage { writeKeyValue(mOpenHelper.getWritableDatabase(), key, value, userId); } + @VisibleForTesting + public boolean isAutoPinConfirmSettingEnabled(int userId) { + return getBoolean(LockPatternUtils.AUTO_PIN_CONFIRM, false, userId); + } + @VisibleForTesting public void writeKeyValue(SQLiteDatabase db, String key, String value, int userId) { ContentValues cv = new ContentValues(); diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index a4dab729250d4..8b8c5f6002555 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -16,6 +16,7 @@ package com.android.server.locksettings; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback; import static com.android.internal.widget.LockPatternUtils.PIN_LENGTH_UNAVAILABLE; @@ -545,6 +546,11 @@ class SyntheticPasswordManager { return null; } + @VisibleForTesting + public boolean isAutoPinConfirmationFeatureAvailable() { + return LockPatternUtils.isAutoPinConfirmFeatureAvailable(); + } + private synchronized boolean isWeaverAvailable() { if (mWeaver != null) { return true; @@ -901,8 +907,8 @@ class SyntheticPasswordManager { LockscreenCredential credential, SyntheticPassword sp, int userId) { long protectorId = generateProtectorId(); int pinLength = PIN_LENGTH_UNAVAILABLE; - if (LockPatternUtils.isAutoPinConfirmFeatureAvailable()) { - pinLength = derivePinLength(credential, userId); + if (isAutoPinConfirmationFeatureAvailable()) { + pinLength = derivePinLength(credential.size(), credential.isPin(), userId); } // There's no need to store password data about an empty LSKF. PasswordData pwd = credential.isNone() ? null : @@ -978,13 +984,13 @@ class SyntheticPasswordManager { return protectorId; } - private int derivePinLength(LockscreenCredential credential, int userId) { - if (!credential.isPin() - || !mStorage.getBoolean(LockPatternUtils.AUTO_PIN_CONFIRM, false, userId) - || credential.size() < LockPatternUtils.MIN_AUTO_PIN_REQUIREMENT_LENGTH) { + private int derivePinLength(int sizeOfCredential, boolean isPinCredential, int userId) { + if (!isPinCredential + || !mStorage.isAutoPinConfirmSettingEnabled(userId) + || sizeOfCredential < LockPatternUtils.MIN_AUTO_PIN_REQUIREMENT_LENGTH) { return PIN_LENGTH_UNAVAILABLE; } - return credential.size(); + return sizeOfCredential; } public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper, @@ -1347,18 +1353,38 @@ class SyntheticPasswordManager { savePasswordMetrics(credential, result.syntheticPassword, protectorId, userId); syncState(userId); // Not strictly needed as the upgrade can be re-done, but be safe. } - if (LockPatternUtils.isAutoPinConfirmFeatureAvailable() - && result.syntheticPassword != null && pwd != null) { - int expectedPinLength = derivePinLength(credential, userId); - if (pwd.pinLength != expectedPinLength) { - pwd.pinLength = expectedPinLength; - saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId); - syncState(userId); - } - } return result; } + /** + * {@link LockPatternUtils#refreshStoredPinLength(int)} + * @param passwordMetrics passwordMetrics object containing the cached pin length + * @param userId userId of the user whose pin length we want to store on disk + * @param protectorId current LSKF based protectorId + * @return true/false depending on whether PIN length has been saved on disk + */ + public boolean refreshPinLengthOnDisk(PasswordMetrics passwordMetrics, + long protectorId, int userId) { + if (!isAutoPinConfirmationFeatureAvailable()) { + return false; + } + + byte[] pwdDataBytes = loadState(PASSWORD_DATA_NAME, protectorId, userId); + if (pwdDataBytes == null) { + return false; + } + + PasswordData pwd = PasswordData.fromBytes(pwdDataBytes); + int pinLength = derivePinLength(passwordMetrics.length, + passwordMetrics.credType == CREDENTIAL_TYPE_PIN, userId); + if (pwd.pinLength != pinLength) { + pwd.pinLength = pinLength; + saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId); + syncState(userId); + } + return true; + } + /** * Tries to unlock a token-based SP protector (weak or strong), given its ID and the claimed * token. On success, returns the user's synthetic password, and also does a Gatekeeper diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index ad63da516b156..e960e995f6ce6 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -16,9 +16,6 @@ package com.android.server.locksettings; -import static android.app.admin.DevicePolicyManager.DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG; -import static android.provider.DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER; - import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -50,7 +47,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; -import android.provider.DeviceConfig; import android.provider.Settings; import android.security.KeyStore; @@ -235,9 +231,6 @@ public abstract class BaseLockSettingsServiceTests { // Adding a fake Device Owner app which will enable escrow token support in LSS. when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn( new ComponentName("com.dummy.package", ".FakeDeviceOwner")); - // TODO(b/258213147): Remove - DeviceConfig.setProperty(NAMESPACE_DEVICE_POLICY_MANAGER, - DEPRECATE_USERMANAGERINTERNAL_DEVICEPOLICY_FLAG, "true", /* makeDefault= */ false); when(mUserManagerInternal.isDeviceManaged()).thenReturn(true); when(mDeviceStateCache.isUserOrganizationManaged(anyInt())).thenReturn(true); when(mDeviceStateCache.isDeviceProvisioned()).thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java index 3f3b8d770f618..36dc6c5f9f956 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java @@ -87,7 +87,10 @@ public class LockSettingsStorageTestable extends LockSettingsStorage { PersistentDataBlockManagerInternal getPersistentDataBlockManager() { return mPersistentDataBlockManager; } - + @Override + public boolean isAutoPinConfirmSettingEnabled(int userId) { + return true; + } private File remapToStorageDir(File origPath) { File mappedPath = new File(mStorageDir, origPath.toString()); mappedPath.getParentFile().mkdirs(); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java index e8ef398f3738b..a48d2cc19c665 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java @@ -112,6 +112,11 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager { } } + @Override + public boolean isAutoPinConfirmationFeatureAvailable() { + return true; + } + @Override protected IWeaver getWeaverHidlService() throws RemoteException { return mWeaverService; diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index bdc5be6d3714d..bfb6b0f1b6c77 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -606,6 +606,24 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertArrayEquals(PAYLOAD2, deserialized.passwordHandle); } + @Test + public void testStorePinLengthOnDisk() { + int userId = 1; + LockscreenCredential lockscreenCredentialPin = LockscreenCredential.createPin("123456"); + MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage, + mGateKeeperService, mUserManager, mPasswordSlotManager); + SyntheticPassword sp = manager.newSyntheticPassword(userId); + long protectorId = manager.createLskfBasedProtector(mGateKeeperService, + lockscreenCredentialPin, sp, + userId); + PasswordMetrics passwordMetrics = + PasswordMetrics.computeForCredential(lockscreenCredentialPin); + boolean result = manager.refreshPinLengthOnDisk(passwordMetrics, protectorId, userId); + + assertEquals(manager.getPinLength(protectorId, userId), lockscreenCredentialPin.size()); + assertTrue(result); + } + @Test public void testPasswordDataV2VersionCredentialTypePin_deserialize() { // Test that we can deserialize existing PasswordData and don't inadvertently change the