diff options
5 files changed, 88 insertions, 0 deletions
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 82367834f93d..511c6802677e 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -109,4 +109,5 @@ interface ILockSettings { boolean isWeakEscrowTokenActive(long handle, int userId); boolean isWeakEscrowTokenValid(long handle, in byte[] token, int userId); void unlockUserKeyIfUnsecured(int userId); + boolean writeRepairModeCredential(int userId); } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index e46b8d7c5fae..f4ad487f48ac 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -449,6 +449,21 @@ public class LockPatternUtils { } /** + * Save the current password data to the repair mode file. + * + * @return true if success or false otherwise. + */ + public boolean writeRepairModeCredential(int userId) { + throwIfCalledOnMainThread(); + try { + return getLockSettings().writeRepairModeCredential(userId); + } catch (RemoteException re) { + Log.e(TAG, "Failed to write repair mode credential", re); + return false; + } + } + + /** * Check to see if a credential matches the saved one. * If credential matches, return an opaque attestation that the challenge was verified. * diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java index b90480a1e794..92a7d8ef890d 100644 --- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java @@ -68,6 +68,7 @@ import org.mockito.ArgumentCaptor; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.concurrent.CompletableFuture; @RunWith(AndroidJUnit4.class) @SmallTest @@ -316,6 +317,40 @@ public class LockPatternUtilsTest { assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_REPAIR_MODE); } + @Test + public void testWriteRepairModeCredential_mainThread() { + createTestLockSettings(); + var context = InstrumentationRegistry.getTargetContext(); + + var future = new CompletableFuture<Exception>(); + context.getMainThreadHandler().post(() -> { + try { + mLockPatternUtils.writeRepairModeCredential(USER_ID); + future.complete(null); + } catch (Exception e) { + future.complete(e); + } + }); + + var e = future.join(); + assertThat(e).isNotNull(); + assertThat(e.getMessage()).contains("should not be called from the main thread"); + } + + @Test + public void testWriteRepairModeCredential() throws Exception { + var ils = createTestLockSettings(); + + when(ils.writeRepairModeCredential(USER_ID)).thenReturn(false); + assertThat(mLockPatternUtils.writeRepairModeCredential(USER_ID)).isFalse(); + + when(ils.writeRepairModeCredential(USER_ID)).thenReturn(true); + assertThat(mLockPatternUtils.writeRepairModeCredential(USER_ID)).isTrue(); + + when(ils.writeRepairModeCredential(USER_ID)).thenThrow(new RemoteException()); + assertThat(mLockPatternUtils.writeRepairModeCredential(USER_ID)).isFalse(); + } + private TestStrongAuthTracker createStrongAuthTracker() { final Context context = new ContextWrapper(InstrumentationRegistry.getTargetContext()); return new TestStrongAuthTracker(context, Looper.getMainLooper()); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 71576465e035..7aae91b98348 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -1773,6 +1773,20 @@ public class LockSettingsService extends ILockSettings.Stub { } } + @Override + public boolean writeRepairModeCredential(int userId) { + checkWritePermission(); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mSpManager) { + long protectorId = getCurrentLskfBasedProtectorId(userId); + return mSpManager.writeRepairModeCredentialLocked(protectorId, userId); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + /** * @param savedCredential if the user is a profile with * {@link UserManager#isCredentialSharableWithParent()} with unified challenge and diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java index 70150c507460..4396c679ec24 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java @@ -19,6 +19,8 @@ package com.android.server.locksettings; import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE; import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; @@ -199,6 +201,27 @@ public class LockscreenRepairModeTest extends BaseLockSettingsServiceTests { .getResponseCode()); } + @Test + public void writeRepairModeCredential_noLock() { + assertThat(mService.writeRepairModeCredential(PRIMARY_USER_ID)).isFalse(); + } + + @Test + public void writeRepairModeCredential_hasLock() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + assertThat(mService.writeRepairModeCredential(PRIMARY_USER_ID)).isTrue(); + } + + @Test + public void writeRepairModeCredential_verifyRepairModeUser() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + mService.writeRepairModeCredential(PRIMARY_USER_ID); + setRepairModeActive(true); + + var response = mService.verifyCredential(newPin("1234"), USER_REPAIR_MODE, 0); + assertThat(response.isMatched()).isTrue(); + } + private void setRepairModeActive(boolean active) { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.REPAIR_MODE_ACTIVE, active ? 1 : 0); |