[BiometricsV2] Refactor AutoCredentialViewModel

Refactor AutoCredentialViewModelTest and FingerprintEnrollmentViewModel
to kotlin and change LiveData to Flow

Bug: 286197659
Test: atest -m CredentialModelTest
Test: atest -m AutoCredentialViewModelTest
Test: atest -m FingerprintEnrollmentViewModelTest
Test: atest -m FingerprintEnrollmentActivityTest
Test: atest -m biometrics-enrollment-test
Change-Id: I84bab0b46e023303c0046a6ae6886ab1cf9458b8
This commit is contained in:
Milton Wu
2023-07-24 16:29:20 +08:00
parent 78f3760d26
commit acb8be5d25
10 changed files with 1067 additions and 1164 deletions

View File

@@ -38,22 +38,6 @@ class CredentialModelTest {
Truth.assertThat(credentialModel.userId).isEqualTo(UserHandle.myUserId())
}
@Test
fun testSameValueFromBundle() {
val bundle = newCredentialModelIntentExtras(1234, 6677L, byteArrayOf(33, 44, 55), 987654321)
val model1 = CredentialModel(bundle, clock)
val model2 = CredentialModel(model1.bundle, clock)
verifySameCredentialModels(model1, model2)
}
@Test
fun testSameValueFromBundle_nullToken() {
val bundle = newCredentialModelIntentExtras(22, 33L, null, 21L)
val model1 = CredentialModel(bundle, clock)
val model2 = CredentialModel(model1.bundle, clock)
verifySameCredentialModels(model1, model2)
}
companion object {
@JvmStatic
fun newCredentialModelIntentExtras(
@@ -148,36 +132,5 @@ class CredentialModelTest {
}
}
}
fun verifySameCredentialModels(
model1: CredentialModel,
model2: CredentialModel
) {
Truth.assertThat(model1.userId).isEqualTo(model2.userId)
Truth.assertThat(model1.challenge).isEqualTo(model2.challenge)
Truth.assertThat(model1.gkPwHandle).isEqualTo(model2.gkPwHandle)
val token1 = model1.token
val token2 = model2.token
if (token1 == null) {
Truth.assertThat(token2).isNull()
} else {
Truth.assertThat(token2).isNotNull()
Truth.assertThat(token1.size).isEqualTo(token2!!.size)
for (i in token1.indices) {
Truth.assertThat(token1[i]).isEqualTo(
token2[i]
)
}
}
val bundle1 = model1.bundle
val bundle2 = model2.bundle
val keySet1 = bundle1.keySet()
Truth.assertThat(keySet1 == bundle2.keySet()).isTrue()
checkBundleIntValue(bundle1, bundle2, Intent.EXTRA_USER_ID)
checkBundleIntValue(bundle1, bundle2, BiometricEnrollBase.EXTRA_KEY_SENSOR_ID)
checkBundleLongValue(bundle1, bundle2, BiometricEnrollBase.EXTRA_KEY_CHALLENGE)
checkBundleByteArrayValue(bundle1, bundle2, BiometricEnrollBase.EXTRA_KEY_CHALLENGE)
checkBundleLongValue(bundle1, bundle2, ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE)
}
}
}

View File

@@ -1,596 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.viewmodel;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE;
import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_CHALLENGE;
import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_GK_PW_HANDLE;
import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newCredentialModelIntentExtras;
import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newGkPwHandleCredentialIntentExtras;
import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newOnlySensorValidCredentialIntentExtras;
import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newValidTokenCredentialIntentExtras;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.ChallengeGenerator;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CredentialAction;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.GenerateChallengeCallback;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.KEY_CREDENTIAL_MODEL;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import androidx.activity.result.ActivityResult;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.password.ChooseLockPattern;
import com.android.settings.testutils.InstantTaskExecutorRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.concurrent.atomic.AtomicBoolean;
@RunWith(AndroidJUnit4.class)
public class AutoCredentialViewModelTest {
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
@Mock private LockPatternUtils mLockPatternUtils;
private TestChallengeGenerator mChallengeGenerator = null;
private AutoCredentialViewModel mViewModel;
@Before
public void setUp() {
mChallengeGenerator = new TestChallengeGenerator();
mViewModel = new AutoCredentialViewModel(
ApplicationProvider.getApplicationContext(),
mLockPatternUtils,
mChallengeGenerator);
}
private void setupGenerateChallenge(int userId, int newSensorId, long newChallenge) {
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
mChallengeGenerator.mUserId = userId;
mChallengeGenerator.mSensorId = newSensorId;
mChallengeGenerator.mChallenge = newChallenge;
}
@Test
public void testSetCredentialModel_sameResultFromSavedInstanceOrIntent() {
final Bundle extras = newCredentialModelIntentExtras(12, 33, new byte[] { 2, 3 }, 3L);
AutoCredentialViewModel viewModel2 = new AutoCredentialViewModel(
ApplicationProvider.getApplicationContext(),
mLockPatternUtils,
mChallengeGenerator);
mViewModel.setCredentialModel(null, new Intent().putExtras(extras));
final Bundle savedInstance = new Bundle();
mViewModel.onSaveInstanceState(savedInstance);
viewModel2.setCredentialModel(savedInstance, new Intent());
assertThat(mViewModel.getUserId()).isEqualTo(viewModel2.getUserId());
final byte[] token1 = mViewModel.getToken();
final byte[] token2 = viewModel2.getToken();
assertThat(token1).isNotNull();
assertThat(token2).isNotNull();
assertThat(token1.length).isEqualTo(token2.length);
for (int i = 0; i < token2.length; ++i) {
assertThat(token1[i]).isEqualTo(token2[i]);
}
}
@Test
public void testSetCredentialModel_sameResultFromSavedInstanceOrIntent_invalidValues() {
final Bundle extras = newCredentialModelIntentExtras(UserHandle.USER_NULL,
INVALID_CHALLENGE, null, INVALID_GK_PW_HANDLE);
AutoCredentialViewModel viewModel2 = new AutoCredentialViewModel(
ApplicationProvider.getApplicationContext(),
mLockPatternUtils,
mChallengeGenerator);
mViewModel.setCredentialModel(null, new Intent().putExtras(extras));
final Bundle savedInstance = new Bundle();
mViewModel.onSaveInstanceState(savedInstance);
viewModel2.setCredentialModel(savedInstance, new Intent());
assertThat(mViewModel.getUserId()).isEqualTo(UserHandle.USER_NULL);
assertThat(viewModel2.getUserId()).isEqualTo(UserHandle.USER_NULL);
assertThat(mViewModel.getToken()).isNull();
assertThat(viewModel2.getToken()).isNull();
}
@Test
public void testCheckCredential_validCredentialCase() {
final int userId = 99;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newValidTokenCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_VALID);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isFalse();
}
@Test
public void testCheckCredential_needToChooseLock() {
final int userId = 100;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_UNSPECIFIED);
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isFalse();
}
@Test
public void testCheckCredential_needToConfirmLockForSomething() {
final int userId = 101;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isFalse();
}
@Test
public void testCheckCredential_needToConfirmLockForNumeric() {
final int userId = 102;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_NUMERIC);
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isFalse();
}
@Test
public void testCheckCredential_needToConfirmLockForAlphabetic() {
final int userId = 103;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_ALPHABETIC);
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isFalse();
}
@Test
public void testCheckCredential_generateChallenge() {
final int userId = 104;
final long gkPwHandle = 1111L;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
final int newSensorId = 10;
final long newChallenge = 20L;
setupGenerateChallenge(userId, newSensorId, newChallenge);
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
final AtomicBoolean hasCalledRemoveGkPwHandle = new AtomicBoolean();
doAnswer(invocation -> {
hasCalledRemoveGkPwHandle.set(true);
return null;
}).when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
// Check data inside CredentialModel
assertThat(mViewModel.getToken()).isNotNull();
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
assertThat(hasCalledRemoveGkPwHandle.get()).isFalse();
// Check createGeneratingChallengeExtras()
final Bundle generatingChallengeExtras = mViewModel.createGeneratingChallengeExtras();
assertThat(generatingChallengeExtras).isNotNull();
assertThat(generatingChallengeExtras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
final byte[] tokens = generatingChallengeExtras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
assertThat(tokens).isNotNull();
assertThat(tokens.length).isEqualTo(1);
assertThat(tokens[0]).isEqualTo(1);
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isTrue();
}
@Test
public void testCheckCredential_generateChallengeFail() {
final int userId = 104;
final long gkPwHandle = 1111L;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
final int newSensorId = 10;
final long newChallenge = 20L;
setupGenerateChallenge(userId, newSensorId, newChallenge);
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
.thenReturn(newBadCredential(0));
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isTrue();
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isTrue();
}
@Test
public void testGetUserId_fromIntent() {
final int userId = 106;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
// Get userId
assertThat(mViewModel.getUserId()).isEqualTo(userId);
}
@Test
public void testGetUserId_fromSavedInstance() {
final int userId = 106;
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL,
newOnlySensorValidCredentialIntentExtras(userId));
mViewModel.setCredentialModel(savedInstance, new Intent());
// Get userId
assertThat(mViewModel.getUserId()).isEqualTo(userId);
}
@Test
public void testCreateGeneratingChallengeExtras_generateChallenge() {
final Bundle credentialExtras = newValidTokenCredentialIntentExtras(200);
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL, credentialExtras);
savedInstance.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL, true);
mViewModel.setCredentialModel(savedInstance, new Intent());
// Check createGeneratingChallengeExtras()
final Bundle actualExtras = mViewModel.createGeneratingChallengeExtras();
assertThat(actualExtras).isNotNull();
assertThat(actualExtras.getLong(EXTRA_KEY_CHALLENGE))
.isEqualTo(credentialExtras.getLong(EXTRA_KEY_CHALLENGE));
final byte[] actualToken = actualExtras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
final byte[] expectedToken = credentialExtras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
assertThat(actualToken).isNotNull();
assertThat(expectedToken).isNotNull();
assertThat(actualToken.length).isEqualTo(expectedToken.length);
for (int i = 0; i < actualToken.length; ++i) {
assertWithMessage("tokens[" + i + "] not match").that(actualToken[i])
.isEqualTo(expectedToken[i]);
}
}
@Test
public void testCreateGeneratingChallengeExtras_notGenerateChallenge() {
final Bundle credentialExtras = newValidTokenCredentialIntentExtras(201);
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL, credentialExtras);
savedInstance.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL, false);
mViewModel.setCredentialModel(savedInstance, new Intent());
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
}
@Test
public void testCreateGeneratingChallengeExtras_invalidToken() {
final Bundle credentialExtras = newOnlySensorValidCredentialIntentExtras(202);
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL, credentialExtras);
savedInstance.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL, true);
mViewModel.setCredentialModel(savedInstance, new Intent());
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
}
@Test
public void testCheckNewCredentialFromActivityResult_invalidChooseLock() {
final int userId = 107;
final long gkPwHandle = 3333L;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
final Intent intent = new Intent();
intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
// run checkNewCredentialFromActivityResult()
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
new ActivityResult(ChooseLockPattern.RESULT_FINISHED + 1, intent));
assertThat(ret).isFalse();
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
}
@Test
public void testCheckNewCredentialFromActivityResult_invalidConfirmLock() {
final int userId = 107;
final long gkPwHandle = 3333L;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
final Intent intent = new Intent();
intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
// run checkNewCredentialFromActivityResult()
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
new ActivityResult(Activity.RESULT_OK + 1, intent));
assertThat(ret).isFalse();
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
}
@Test
public void testCheckNewCredentialFromActivityResult_nullDataChooseLock() {
final int userId = 108;
final long gkPwHandle = 4444L;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
// run checkNewCredentialFromActivityResult()
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
new ActivityResult(ChooseLockPattern.RESULT_FINISHED, null));
assertThat(ret).isFalse();
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
}
@Test
public void testCheckNewCredentialFromActivityResult_nullDataConfirmLock() {
final int userId = 109;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
// run checkNewCredentialFromActivityResult()
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
new ActivityResult(Activity.RESULT_OK, null));
assertThat(ret).isFalse();
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
}
@Test
public void testCheckNewCredentialFromActivityResult_validChooseLock() {
final int userId = 108;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
final long gkPwHandle = 6666L;
final int newSensorId = 50;
final long newChallenge = 60L;
setupGenerateChallenge(userId, newSensorId, newChallenge);
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
final AtomicBoolean hasCalledRemoveGkPwHandle = new AtomicBoolean();
doAnswer(invocation -> {
hasCalledRemoveGkPwHandle.set(true);
return null;
}).when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
// Run checkNewCredentialFromActivityResult()
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
new ActivityResult(ChooseLockPattern.RESULT_FINISHED, intent));
assertThat(ret).isTrue();
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
assertThat(mViewModel.getToken()).isNotNull();
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue();
}
@Test
public void testCheckNewCredentialFromActivityResult_validConfirmLock() {
final int userId = 109;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
final long gkPwHandle = 5555L;
final int newSensorId = 80;
final long newChallenge = 90L;
setupGenerateChallenge(userId, newSensorId, newChallenge);
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
final AtomicBoolean hasCalledRemoveGkPwHandle = new AtomicBoolean();
doAnswer(invocation -> {
hasCalledRemoveGkPwHandle.set(true);
return null;
}).when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
// Run checkNewCredentialFromActivityResult()
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
new ActivityResult(Activity.RESULT_OK, intent));
assertThat(ret).isTrue();
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
assertThat(mViewModel.getToken()).isNotNull();
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue();
}
public static class TestChallengeGenerator implements ChallengeGenerator {
public int mSensorId = -1;
public int mUserId = UserHandle.myUserId();
public long mChallenge = INVALID_CHALLENGE;
public int mCallbackRunCount = 0;
private GenerateChallengeCallback mCallback;
@Nullable
@Override
public GenerateChallengeCallback getCallback() {
return mCallback;
}
@Override
public void setCallback(@Nullable GenerateChallengeCallback callback) {
mCallback = callback;
}
@Override
public void generateChallenge(int userId) {
final GenerateChallengeCallback callback = mCallback;
if (callback == null) {
return;
}
callback.onChallengeGenerated(mSensorId, mUserId, mChallenge);
++mCallbackRunCount;
}
}
private VerifyCredentialResponse newGoodCredential(long gkPwHandle, @NonNull byte[] hat) {
return new VerifyCredentialResponse.Builder()
.setGatekeeperPasswordHandle(gkPwHandle)
.setGatekeeperHAT(hat)
.build();
}
private VerifyCredentialResponse newBadCredential(int timeout) {
if (timeout > 0) {
return VerifyCredentialResponse.fromTimeout(timeout);
} else {
return VerifyCredentialResponse.fromError();
}
}
}

View File

@@ -0,0 +1,541 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.viewmodel
import android.app.Activity
import android.app.admin.DevicePolicyManager
import android.content.Intent
import android.os.Bundle
import android.os.SystemClock
import android.os.UserHandle
import androidx.activity.result.ActivityResult
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.VerifyCredentialResponse
import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics2.ui.model.CredentialModel
import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newGkPwHandleCredentialIntentExtras
import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newOnlySensorValidCredentialIntentExtras
import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newValidTokenCredentialIntentExtras
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.ChallengeGenerator
import com.android.settings.password.ChooseLockPattern
import com.android.settings.password.ChooseLockSettingsHelper
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import java.util.concurrent.atomic.AtomicBoolean
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AutoCredentialViewModelTest {
@get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var lockPatternUtils: LockPatternUtils
private var challengeGenerator: TestChallengeGenerator = TestChallengeGenerator()
private lateinit var viewModel: AutoCredentialViewModel
private fun newAutoCredentialViewModel(bundle: Bundle?): AutoCredentialViewModel {
return AutoCredentialViewModel(
ApplicationProvider.getApplicationContext(),
lockPatternUtils,
challengeGenerator,
CredentialModel(bundle, SystemClock.elapsedRealtimeClock())
)
}
@Before
fun setUp() {
challengeGenerator = TestChallengeGenerator()
}
private fun setupGenerateChallenge(userId: Int, newSensorId: Int, newChallenge: Long) {
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
challengeGenerator.userId = userId
challengeGenerator.sensorId = newSensorId
challengeGenerator.challenge = newChallenge
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckCredential_validCredentialCase() = runTest {
val userId = 99
viewModel = newAutoCredentialViewModel(newValidTokenCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(backgroundScope)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.CREDENTIAL_VALID)
assertThat(generateFails.size).isEqualTo(0)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckCredential_needToChooseLock() = runTest {
val userId = 100
viewModel = newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(backgroundScope)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CHOOSE_LOCK)
assertThat(generateFails.size).isEqualTo(0)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckCredential_needToConfirmLockForSomething() = runTest {
val userId = 101
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(backgroundScope)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
assertThat(generateFails.size).isEqualTo(0)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckCredential_needToConfirmLockForNumeric() = runTest {
val userId = 102
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(backgroundScope)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
assertThat(generateFails.size).isEqualTo(0)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckCredential_needToConfirmLockForAlphabetic() = runTest {
val userId = 103
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(this)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
assertThat(generateFails.size).isEqualTo(0)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckCredential_generateChallenge() = runTest {
val userId = 104
val gkPwHandle = 1111L
viewModel =
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val newSensorId = 10
val newChallenge = 20L
setupGenerateChallenge(userId, newSensorId, newChallenge)
whenever(
lockPatternUtils.verifyGatekeeperPasswordHandle(
gkPwHandle,
newChallenge,
userId
)
)
.thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
val hasCalledRemoveGkPwHandle = AtomicBoolean()
Mockito.doAnswer {
hasCalledRemoveGkPwHandle.set(true)
null
}.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(backgroundScope)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.IS_GENERATING_CHALLENGE)
assertThat(generateFails.size).isEqualTo(0)
// Check data inside CredentialModel
assertThat(viewModel.token).isNotNull()
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
assertThat(hasCalledRemoveGkPwHandle.get()).isFalse()
// Check createGeneratingChallengeExtras()
val generatingChallengeExtras = viewModel.createGeneratingChallengeExtras()
assertThat(generatingChallengeExtras).isNotNull()
assertThat(generatingChallengeExtras!!.getLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE))
.isEqualTo(newChallenge)
val tokens =
generatingChallengeExtras.getByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
assertThat(tokens).isNotNull()
assertThat(tokens!!.size).isEqualTo(1)
assertThat(tokens[0]).isEqualTo(1)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckCredential_generateChallengeFail() = runTest {
backgroundScope.launch {
val userId = 104
val gkPwHandle = 1111L
viewModel =
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val newSensorId = 10
val newChallenge = 20L
setupGenerateChallenge(userId, newSensorId, newChallenge)
whenever(
lockPatternUtils.verifyGatekeeperPasswordHandle(
gkPwHandle,
newChallenge,
userId
)
)
.thenReturn(newBadCredential(0))
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(this)
runCurrent()
assertThat(action).isEqualTo(CredentialAction.IS_GENERATING_CHALLENGE)
assertThat(generateFails.size).isEqualTo(1)
assertThat(generateFails[0]).isEqualTo(true)
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
}
@Test
fun testGetUserId_fromIntent() {
val userId = 106
viewModel = newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
// Get userId
assertThat(viewModel.userId).isEqualTo(userId)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testGenerateChallengeAsCredentialActivityResult_invalidChooseLock() = runTest {
backgroundScope.launch {
val userId = 107
val gkPwHandle = 3333L
viewModel =
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
val intent = Intent()
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val ret = viewModel.generateChallengeAsCredentialActivityResult(
true,
ActivityResult(ChooseLockPattern.RESULT_FINISHED + 1, intent),
backgroundScope
)
runCurrent()
assertThat(ret).isFalse()
assertThat(generateFails.size).isEqualTo(0)
}
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testGenerateChallengeAsCredentialActivityResult_invalidConfirmLock() = runTest {
backgroundScope.launch {
val userId = 107
val gkPwHandle = 3333L
viewModel =
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
val intent = Intent()
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val ret = viewModel.generateChallengeAsCredentialActivityResult(
false,
ActivityResult(Activity.RESULT_OK + 1, intent),
backgroundScope
)
runCurrent()
assertThat(ret).isFalse()
assertThat(generateFails.size).isEqualTo(0)
}
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testGenerateChallengeAsCredentialActivityResult_nullDataChooseLock() = runTest {
val userId = 108
val gkPwHandle = 4444L
viewModel =
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val ret = viewModel.generateChallengeAsCredentialActivityResult(
true,
ActivityResult(ChooseLockPattern.RESULT_FINISHED, null),
backgroundScope
)
runCurrent()
assertThat(ret).isFalse()
assertThat(generateFails.size).isEqualTo(0)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testGenerateChallengeAsCredentialActivityResult_nullDataConfirmLock() = runTest {
val userId = 109
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val ret = viewModel.generateChallengeAsCredentialActivityResult(
false,
ActivityResult(Activity.RESULT_OK, null),
backgroundScope
)
runCurrent()
assertThat(ret).isFalse()
assertThat(generateFails.size).isEqualTo(0)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testGenerateChallengeAsCredentialActivityResult_validChooseLock() = runTest {
val userId = 108
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val gkPwHandle = 6666L
val newSensorId = 50
val newChallenge = 60L
setupGenerateChallenge(userId, newSensorId, newChallenge)
whenever(
lockPatternUtils.verifyGatekeeperPasswordHandle(
gkPwHandle,
newChallenge,
userId
)
)
.thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
val hasCalledRemoveGkPwHandle = AtomicBoolean()
Mockito.doAnswer {
hasCalledRemoveGkPwHandle.set(true)
null
}.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val intent =
Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
val ret = viewModel.generateChallengeAsCredentialActivityResult(
true,
ActivityResult(ChooseLockPattern.RESULT_FINISHED, intent),
backgroundScope
)
runCurrent()
assertThat(ret).isTrue()
assertThat(generateFails.size).isEqualTo(0)
assertThat(viewModel.token).isNotNull()
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue()
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testGenerateChallengeAsCredentialActivityResult_validConfirmLock() = runTest {
val userId = 109
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val gkPwHandle = 5555L
val newSensorId = 80
val newChallenge = 90L
setupGenerateChallenge(userId, newSensorId, newChallenge)
whenever(
lockPatternUtils.verifyGatekeeperPasswordHandle(
gkPwHandle,
newChallenge,
userId
)
)
.thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
val hasCalledRemoveGkPwHandle = AtomicBoolean()
Mockito.doAnswer {
hasCalledRemoveGkPwHandle.set(true)
null
}.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val intent =
Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
val ret = viewModel.generateChallengeAsCredentialActivityResult(
false,
ActivityResult(Activity.RESULT_OK, intent),
backgroundScope
)
runCurrent()
assertThat(ret).isTrue()
assertThat(generateFails.size).isEqualTo(0)
assertThat(viewModel.token).isNotNull()
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue()
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun TestScope.listOfGenerateChallengeFailedFlow(): List<Boolean> =
mutableListOf<Boolean>().also {
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.generateChallengeFailedFlow.toList(it)
}
}
class TestChallengeGenerator : ChallengeGenerator {
var sensorId = -1
var userId = UserHandle.myUserId()
var challenge = CredentialModel.INVALID_CHALLENGE
var callbackRunCount = 0
private var _callback: AutoCredentialViewModel.GenerateChallengeCallback? = null
override fun getCallback(): AutoCredentialViewModel.GenerateChallengeCallback? {
return _callback
}
override fun setCallback(callback: AutoCredentialViewModel.GenerateChallengeCallback?) {
_callback = callback
}
override fun generateChallenge(userId: Int) {
val callback = _callback ?: return
callback.onChallengeGenerated(sensorId, this.userId, challenge)
++callbackRunCount
}
}
private fun newGoodCredential(gkPwHandle: Long, hat: ByteArray): VerifyCredentialResponse {
return VerifyCredentialResponse.Builder()
.setGatekeeperPasswordHandle(gkPwHandle)
.setGatekeeperHAT(hat)
.build()
}
private fun newBadCredential(timeout: Int): VerifyCredentialResponse {
return if (timeout > 0) {
VerifyCredentialResponse.fromTimeout(timeout)
} else {
VerifyCredentialResponse.fromError()
}
}
}

View File

@@ -23,12 +23,20 @@ import android.os.Bundle
import androidx.activity.result.ActivityResult
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics2.data.repository.FingerprintRepository
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newAllFalseRequest
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newIsSuwRequest
import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository
import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints
import com.android.settings.testutils.InstantTaskExecutorRule
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -42,8 +50,6 @@ class FingerprintEnrollmentViewModelTest {
@get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
@get:Rule val taskExecutorRule = InstantTaskExecutorRule()
private val application: Application
get() = ApplicationProvider.getApplicationContext()
@@ -69,12 +75,12 @@ class FingerprintEnrollmentViewModelTest {
@Test
fun testGetRequest() {
Truth.assertThat(viewModel.request).isNotNull()
assertThat(viewModel.request).isNotNull()
}
@Test
fun testIsWaitingActivityResultDefaultFalse() {
Truth.assertThat(viewModel.isWaitingActivityResult.value).isFalse()
assertThat(viewModel.isWaitingActivityResult.value).isFalse()
}
@@ -83,8 +89,8 @@ class FingerprintEnrollmentViewModelTest {
val retResult = viewModel.getOverrideActivityResult(
ActivityResult(22, null), null
)
Truth.assertThat(retResult).isNotNull()
Truth.assertThat(retResult.data).isNull()
assertThat(retResult).isNotNull()
assertThat(retResult.data).isNull()
}
@Test
@@ -93,8 +99,8 @@ class FingerprintEnrollmentViewModelTest {
val retResult = viewModel.getOverrideActivityResult(
ActivityResult(33, intent), null
)
Truth.assertThat(retResult).isNotNull()
Truth.assertThat(retResult.data).isEqualTo(intent)
assertThat(retResult).isNotNull()
assertThat(retResult.data).isEqualTo(intent)
}
@Test
@@ -106,8 +112,8 @@ class FingerprintEnrollmentViewModelTest {
ActivityResult(33, null), extra
)
Truth.assertThat(retResult).isNotNull()
Truth.assertThat(retResult.data).isNull()
assertThat(retResult).isNotNull()
assertThat(retResult.data).isNull()
}
@Test
@@ -124,16 +130,16 @@ class FingerprintEnrollmentViewModelTest {
val retResult = viewModel.getOverrideActivityResult(
ActivityResult(33, null), extra
)
Truth.assertThat(retResult).isNotNull()
assertThat(retResult).isNotNull()
val retIntent = retResult.data
Truth.assertThat(retIntent).isNotNull()
assertThat(retIntent).isNotNull()
val retExtra = retIntent!!.extras
Truth.assertThat(retExtra).isNotNull()
Truth.assertThat(retExtra!!.size).isEqualTo(extra.size)
Truth.assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1))
Truth.assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2))
assertThat(retExtra).isNotNull()
assertThat(retExtra!!.size).isEqualTo(extra.size)
assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1))
assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2))
}
@Test
@@ -149,15 +155,15 @@ class FingerprintEnrollmentViewModelTest {
val retResult = viewModel.getOverrideActivityResult(ActivityResult(33, intent), extra)
Truth.assertThat(retResult).isNotNull()
assertThat(retResult).isNotNull()
val retIntent = retResult.data
Truth.assertThat(retIntent).isNotNull()
assertThat(retIntent).isNotNull()
val retExtra = retIntent!!.extras
Truth.assertThat(retExtra).isNotNull()
Truth.assertThat(retExtra!!.size).isEqualTo(intent.extras!!.size)
Truth.assertThat(retExtra.getString(key2)).isEqualTo(intent.extras!!.getString(key2))
assertThat(retExtra).isNotNull()
assertThat(retExtra!!.size).isEqualTo(intent.extras!!.size)
assertThat(retExtra.getString(key2)).isEqualTo(intent.extras!!.getString(key2))
}
@Test
@@ -177,17 +183,17 @@ class FingerprintEnrollmentViewModelTest {
viewModel.isNewFingerprintAdded = true
val retResult = viewModel.getOverrideActivityResult(ActivityResult(33, intent), extra)
Truth.assertThat(retResult).isNotNull()
assertThat(retResult).isNotNull()
val retIntent = retResult.data
Truth.assertThat(retIntent).isNotNull()
assertThat(retIntent).isNotNull()
val retExtra = retIntent!!.extras
Truth.assertThat(retExtra).isNotNull()
Truth.assertThat(retExtra!!.size).isEqualTo(extra.size + intent.extras!!.size)
Truth.assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1))
Truth.assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2))
Truth.assertThat(retExtra.getLong(key3)).isEqualTo(intent.extras!!.getLong(key3))
assertThat(retExtra).isNotNull()
assertThat(retExtra!!.size).isEqualTo(extra.size + intent.extras!!.size)
assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1))
assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2))
assertThat(retExtra.getLong(key3)).isEqualTo(intent.extras!!.getLong(key3))
}
@Test
@@ -205,18 +211,120 @@ class FingerprintEnrollmentViewModelTest {
)
setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 0)
Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 1)
Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 2)
Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 3)
Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue()
assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue()
setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 4)
Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue()
assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue()
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testSetResultFlow_defaultEmpty() = runTest {
val activityResults = listOfSetResultFlow()
runCurrent()
assertThat(activityResults.size).isEqualTo(0)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckFinishActivityDuringOnPause_doNothingIfIsSuw() = runTest {
viewModel = FingerprintEnrollmentViewModel(
application,
fingerprintRepository,
newIsSuwRequest(application)
)
val activityResults = listOfSetResultFlow()
viewModel.checkFinishActivityDuringOnPause(
isActivityFinishing = false,
isChangingConfigurations = false,
scope = this
)
runCurrent()
assertThat(activityResults.size).isEqualTo(0)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckFinishActivityDuringOnPause_doNothingIfIsWaitingActivity() = runTest {
val activityResults = listOfSetResultFlow()
viewModel.isWaitingActivityResult.value = true
viewModel.checkFinishActivityDuringOnPause(
isActivityFinishing = false,
isChangingConfigurations = false,
scope = this
)
runCurrent()
assertThat(activityResults.size).isEqualTo(0)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckFinishActivityDuringOnPause_doNothingIfIsActivityFinishing() = runTest {
val activityResults = listOfSetResultFlow()
viewModel.checkFinishActivityDuringOnPause(
isActivityFinishing = true,
isChangingConfigurations = false,
scope = this
)
runCurrent()
assertThat(activityResults.size).isEqualTo(0)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckFinishActivityDuringOnPause_doNothingIfIsChangingConfigurations() = runTest {
val activityResults = listOfSetResultFlow()
viewModel.checkFinishActivityDuringOnPause(
isActivityFinishing = false,
isChangingConfigurations = true,
scope = this
)
runCurrent()
assertThat(activityResults.size).isEqualTo(0)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCheckFinishActivityDuringOnPause_defaultFinishSelf() = runTest {
val activityResults = listOfSetResultFlow()
viewModel.checkFinishActivityDuringOnPause(
isActivityFinishing = false,
isChangingConfigurations = false,
scope = backgroundScope
)
runCurrent()
assertThat(activityResults.size).isEqualTo(1)
assertThat(activityResults[0].resultCode).isEqualTo(BiometricEnrollBase.RESULT_TIMEOUT)
assertThat(activityResults[0].data).isEqualTo(null)
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun TestScope.listOfSetResultFlow(): List<ActivityResult> =
mutableListOf<ActivityResult>().also {
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.setResultFlow.toList(it)
}
}
}