Fix FingerprintEnrollmentActivity rotaiton crash

1. When FragmentActivity rotated, FragmentManager will call default
   fragment constructor w/o parameters. Remove parameters of
   FingerprintEnrollIntroFragment constructor. And also remove
   BiometricsFragmentFactory because of useless now.
2. Remove some LiveData inside AutoCredentialViewModel because it causes
   jitter on activity transition.
3. Save and restore data inside AutoCredentialViewModel for handling
   activity recreation during rotation.
4. clear FingerprintEnrollIntroViewModel.mActionLiveData to prevent
   that activity gets previous action after recreate
5. Fix launching wrong activity during setupwizard

Bug: 259626932
Test: atest AutoCredentialViewModelTest CredentialModelTest
            FingerprintEnrollIntroViewModelTest
Change-Id: Ia26c86dc99ad91dbddef90538d9f5e5583585063
This commit is contained in:
Milton Wu
2022-11-22 16:37:11 +08:00
parent f137463ddc
commit 5174310392
9 changed files with 520 additions and 246 deletions

View File

@@ -1,57 +0,0 @@
/*
* Copyright (C) 2022 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.factory;
import android.app.Application;
import android.app.admin.DevicePolicyManager;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentFactory;
import androidx.lifecycle.ViewModelProvider;
import com.android.settings.biometrics2.ui.view.FingerprintEnrollIntroFragment;
/**
* Fragment factory for biometrics
*/
public class BiometricsFragmentFactory extends FragmentFactory {
private final Application mApplication;
private final ViewModelProvider mViewModelProvider;
public BiometricsFragmentFactory(Application application,
ViewModelProvider viewModelProvider) {
mApplication = application;
mViewModelProvider = viewModelProvider;
}
@NonNull
@Override
public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
final Class<? extends Fragment> clazz = loadFragmentClass(classLoader, className);
if (FingerprintEnrollIntroFragment.class.equals(clazz)) {
final DevicePolicyManager devicePolicyManager =
mApplication.getSystemService(DevicePolicyManager.class);
if (devicePolicyManager != null) {
return new FingerprintEnrollIntroFragment(mViewModelProvider,
devicePolicyManager.getResources());
}
}
return super.instantiate(classLoader, className);
}
}

View File

@@ -22,6 +22,7 @@ import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_C
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle; import android.os.UserHandle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -80,17 +81,30 @@ public final class CredentialModel {
@Nullable @Nullable
private Long mClearGkPwHandleMillis = null; private Long mClearGkPwHandleMillis = null;
public CredentialModel(@NonNull Intent intent, @NonNull Clock clock) { public CredentialModel(@NonNull Bundle bundle, @NonNull Clock clock) {
mUserId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); mUserId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
mSensorId = intent.getIntExtra(EXTRA_KEY_SENSOR_ID, INVALID_SENSOR_ID); mSensorId = bundle.getInt(EXTRA_KEY_SENSOR_ID, INVALID_SENSOR_ID);
mChallenge = intent.getLongExtra(EXTRA_KEY_CHALLENGE, INVALID_CHALLENGE); mChallenge = bundle.getLong(EXTRA_KEY_CHALLENGE, INVALID_CHALLENGE);
mToken = intent.getByteArrayExtra(EXTRA_KEY_CHALLENGE_TOKEN); mToken = bundle.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
mGkPwHandle = intent.getLongExtra(EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle = bundle.getLong(EXTRA_KEY_GK_PW_HANDLE, INVALID_GK_PW_HANDLE);
INVALID_GK_PW_HANDLE);
mClock = clock; mClock = clock;
mInitMillis = mClock.millis(); mInitMillis = mClock.millis();
} }
/**
* Get a bundle which can be used to recreate CredentialModel
*/
@NonNull
public Bundle getBundle() {
final Bundle bundle = new Bundle();
bundle.putInt(Intent.EXTRA_USER_ID, mUserId);
bundle.putInt(EXTRA_KEY_SENSOR_ID, mSensorId);
bundle.putLong(EXTRA_KEY_CHALLENGE, mChallenge);
bundle.putByteArray(EXTRA_KEY_CHALLENGE_TOKEN, mToken);
bundle.putLong(EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle);
return bundle;
}
/** /**
* Get userId for this credential * Get userId for this credential
*/ */

View File

@@ -25,7 +25,7 @@ import static com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroSt
import static com.google.android.setupdesign.util.DynamicColorPalette.ColorType.ACCENT; import static com.google.android.setupdesign.util.DynamicColorPalette.ColorType.ACCENT;
import android.app.Activity; import android.app.Activity;
import android.app.admin.DevicePolicyResourcesManager; import android.app.admin.DevicePolicyManager;
import android.content.Context; import android.content.Context;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffColorFilter;
@@ -63,9 +63,6 @@ public class FingerprintEnrollIntroFragment extends Fragment {
private static final String TAG = "FingerprintEnrollIntroFragment"; private static final String TAG = "FingerprintEnrollIntroFragment";
@NonNull private final ViewModelProvider mViewModelProvider;
@Nullable private final DevicePolicyResourcesManager mDevicePolicyMgrRes;
private FingerprintEnrollIntroViewModel mViewModel = null; private FingerprintEnrollIntroViewModel mViewModel = null;
private View mView = null; private View mView = null;
@@ -75,12 +72,8 @@ public class FingerprintEnrollIntroFragment extends Fragment {
private TextView mFooterMessage6 = null; private TextView mFooterMessage6 = null;
@Nullable private PorterDuffColorFilter mIconColorFilter; @Nullable private PorterDuffColorFilter mIconColorFilter;
public FingerprintEnrollIntroFragment( public FingerprintEnrollIntroFragment() {
@NonNull ViewModelProvider viewModelProvider,
@Nullable DevicePolicyResourcesManager devicePolicyMgrRes) {
super(); super();
mViewModelProvider = viewModelProvider;
mDevicePolicyMgrRes = devicePolicyMgrRes;
} }
@Nullable @Nullable
@@ -197,7 +190,8 @@ public class FingerprintEnrollIntroFragment extends Fragment {
@Override @Override
public void onAttach(@NonNull Context context) { public void onAttach(@NonNull Context context) {
mViewModel = mViewModelProvider.get(FingerprintEnrollIntroViewModel.class); mViewModel = new ViewModelProvider(getActivity())
.get(FingerprintEnrollIntroViewModel.class);
getLifecycle().addObserver(mViewModel); getLifecycle().addObserver(mViewModel);
super.onAttach(context); super.onAttach(context);
} }
@@ -232,12 +226,16 @@ public class FingerprintEnrollIntroFragment extends Fragment {
private String getDescriptionDisabledByAdmin(@NonNull Context context) { private String getDescriptionDisabledByAdmin(@NonNull Context context) {
final int defaultStrId = final int defaultStrId =
R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled; R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled;
if (mDevicePolicyMgrRes == null) {
final DevicePolicyManager devicePolicyManager = getActivity()
.getSystemService(DevicePolicyManager.class);
if (devicePolicyManager != null) {
return devicePolicyManager.getResources().getString(FINGERPRINT_UNLOCK_DISABLED,
() -> context.getString(defaultStrId));
} else {
Log.w(TAG, "getDescriptionDisabledByAdmin, null device policy manager res"); Log.w(TAG, "getDescriptionDisabledByAdmin, null device policy manager res");
return ""; return "";
} }
return mDevicePolicyMgrRes.getString(FINGERPRINT_UNLOCK_DISABLED,
() -> context.getString(defaultStrId));
} }
private void setHeaderText(@NonNull Activity activity, int resId) { private void setHeaderText(@NonNull Activity activity, int resId) {

View File

@@ -19,20 +19,19 @@ package com.android.settings.biometrics2.ui.view;
import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY; import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY;
import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR; import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE;
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_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_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.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL;
import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.content.Intent; import android.content.Intent;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.Color; import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResult;
@@ -42,7 +41,6 @@ import androidx.annotation.ColorInt;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.viewmodel.CreationExtras; import androidx.lifecycle.viewmodel.CreationExtras;
import androidx.lifecycle.viewmodel.MutableCreationExtras; import androidx.lifecycle.viewmodel.MutableCreationExtras;
@@ -51,11 +49,9 @@ import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor; import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollEnrolling; import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor;
import com.android.settings.biometrics2.data.repository.FingerprintRepository; import com.android.settings.biometrics2.data.repository.FingerprintRepository;
import com.android.settings.biometrics2.factory.BiometricsFragmentFactory;
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory; import com.android.settings.biometrics2.factory.BiometricsViewModelFactory;
import com.android.settings.biometrics2.ui.model.CredentialModel;
import com.android.settings.biometrics2.ui.model.EnrollmentRequest; import com.android.settings.biometrics2.ui.model.EnrollmentRequest;
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel; import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator; import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator;
@@ -102,12 +98,12 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
getLifecycle().addObserver(mViewModel); getLifecycle().addObserver(mViewModel);
mAutoCredentialViewModel = viewModelProvider.get(AutoCredentialViewModel.class); mAutoCredentialViewModel = viewModelProvider.get(AutoCredentialViewModel.class);
mAutoCredentialViewModel.setCredentialModel(new CredentialModel(getIntent(), mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent());
SystemClock.elapsedRealtimeClock())); mAutoCredentialViewModel.getGenerateChallengeFailLiveData().observe(this,
getLifecycle().addObserver(mAutoCredentialViewModel); this::onGenerateChallengeFail);
mViewModel.getSetResultLiveData().observe(this, this::onSetActivityResult); mViewModel.getSetResultLiveData().observe(this, this::onSetActivityResult);
mAutoCredentialViewModel.getActionLiveData().observe(this, this::onCredentialAction); checkCredential();
// Theme // Theme
setTheme(mViewModel.getRequest().getTheme()); setTheme(mViewModel.getRequest().getTheme());
@@ -116,22 +112,30 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
// fragment // fragment
setContentView(R.layout.biometric_enrollment_container); setContentView(R.layout.biometric_enrollment_container);
final FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.setFragmentFactory(
new BiometricsFragmentFactory(getApplication(), viewModelProvider));
final FingerprintEnrollIntroViewModel fingerprintEnrollIntroViewModel = final FingerprintEnrollIntroViewModel fingerprintEnrollIntroViewModel =
viewModelProvider.get(FingerprintEnrollIntroViewModel.class); viewModelProvider.get(FingerprintEnrollIntroViewModel.class);
fingerprintEnrollIntroViewModel.setEnrollmentRequest(mViewModel.getRequest()); fingerprintEnrollIntroViewModel.setEnrollmentRequest(mViewModel.getRequest());
fingerprintEnrollIntroViewModel.setUserId(mAutoCredentialViewModel.getUserId()); fingerprintEnrollIntroViewModel.setUserId(mAutoCredentialViewModel.getUserId());
// Clear ActionLiveData in FragmentViewModel to prevent getting previous action when
// recreate
fingerprintEnrollIntroViewModel.clearActionLiveData();
fingerprintEnrollIntroViewModel.getActionLiveData().observe( fingerprintEnrollIntroViewModel.getActionLiveData().observe(
this, this::observeIntroAction); this, this::observeIntroAction);
if (savedInstanceState == null) {
final String tag = "FingerprintEnrollIntroFragment"; final String tag = "FingerprintEnrollIntroFragment";
fragmentManager.beginTransaction() getSupportFragmentManager().beginTransaction()
.setReorderingAllowed(true) .setReorderingAllowed(true)
.add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null, tag) .add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null,
tag)
.commit(); .commit();
} }
}
private void onGenerateChallengeFail(@NonNull Boolean isFail) {
onSetActivityResult(new ActivityResult(RESULT_CANCELED, null));
}
private void onSetActivityResult(@NonNull ActivityResult result) { private void onSetActivityResult(@NonNull ActivityResult result) {
setResult(mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction() setResult(mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()
@@ -141,8 +145,8 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
finish(); finish();
} }
private void onCredentialAction(@NonNull Integer action) { private void checkCredential() {
switch (action) { switch (mAutoCredentialViewModel.checkCredential()) {
case CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK: { case CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK: {
final Intent intent = mAutoCredentialViewModel.getChooseLockIntent(this, final Intent intent = mAutoCredentialViewModel.getChooseLockIntent(this,
mViewModel.getRequest().isSuw(), mViewModel.getRequest().getSuwExtras()); mViewModel.getRequest().isSuw(), mViewModel.getRequest().getSuwExtras());
@@ -168,12 +172,9 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
} }
return; return;
} }
case CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE: { case CREDENTIAL_VALID:
Log.w(TAG, "observeCredentialLiveData, finish with action:" + action); case CREDENTIAL_IS_GENERATING_CHALLENGE: {
if (mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()) { // Do nothing
setResult(Activity.RESULT_CANCELED);
}
finish();
} }
} }
} }
@@ -186,10 +187,15 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
if (mAutoCredentialViewModel.checkNewCredentialFromActivityResult( if (mAutoCredentialViewModel.checkNewCredentialFromActivityResult(
isChooseLock, activityResult)) { isChooseLock, activityResult)) {
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out); overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
} else {
onSetActivityResult(activityResult);
} }
} }
private void observeIntroAction(@NonNull Integer action) { private void observeIntroAction(@Nullable Integer action) {
if (action == null) {
return;
}
switch (action) { switch (action) {
case FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH: { case FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH: {
onSetActivityResult( onSetActivityResult(
@@ -207,9 +213,9 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
Log.w(TAG, "startNext, isSuw:" + isSuw + ", fail to set isWaiting flag"); Log.w(TAG, "startNext, isSuw:" + isSuw + ", fail to set isWaiting flag");
} }
final Intent intent = new Intent(this, isSuw final Intent intent = new Intent(this, isSuw
? SetupFingerprintEnrollEnrolling.class ? SetupFingerprintEnrollFindSensor.class
: FingerprintEnrollFindSensor.class); : FingerprintEnrollFindSensor.class);
intent.putExtras(mAutoCredentialViewModel.getCredentialBundle()); intent.putExtras(mAutoCredentialViewModel.getCredentialIntentExtra());
intent.putExtras(mViewModel.getNextActivityBaseIntentExtras()); intent.putExtras(mViewModel.getNextActivityBaseIntentExtras());
mNextActivityLauncher.launch(intent); mNextActivityLauncher.launch(intent);
} }
@@ -272,5 +278,6 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
protected void onSaveInstanceState(@NonNull Bundle outState) { protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
mViewModel.onSaveInstanceState(outState); mViewModel.onSaveInstanceState(outState);
mAutoCredentialViewModel.onSaveInstanceState(outState);
} }
} }

View File

@@ -30,20 +30,21 @@ import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResult;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse; import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.biometrics.BiometricUtils; import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException;
import com.android.settings.biometrics2.data.repository.FingerprintRepository; import com.android.settings.biometrics2.data.repository.FingerprintRepository;
import com.android.settings.biometrics2.ui.model.CredentialModel; import com.android.settings.biometrics2.ui.model.CredentialModel;
import com.android.settings.password.ChooseLockGeneric; import com.android.settings.password.ChooseLockGeneric;
@@ -57,31 +58,40 @@ import java.lang.annotation.RetentionPolicy;
* AutoCredentialViewModel which uses CredentialModel to determine next actions for activity, like * AutoCredentialViewModel which uses CredentialModel to determine next actions for activity, like
* start ChooseLockActivity, start ConfirmLockActivity, GenerateCredential, or do nothing. * start ChooseLockActivity, start ConfirmLockActivity, GenerateCredential, or do nothing.
*/ */
public class AutoCredentialViewModel extends AndroidViewModel implements DefaultLifecycleObserver { public class AutoCredentialViewModel extends AndroidViewModel {
private static final String TAG = "AutoCredentialViewModel"; private static final String TAG = "AutoCredentialViewModel";
private static final boolean DEBUG = true;
@VisibleForTesting
static final String KEY_CREDENTIAL_MODEL = "credential_model";
private static final boolean DEBUG = false;
/**
* Valid credential, activity doesn't need to do anything.
*/
public static final int CREDENTIAL_VALID = 0;
/**
* This credential looks good, but still need to run generateChallenge().
*/
public static final int CREDENTIAL_IS_GENERATING_CHALLENGE = 1;
/** /**
* Need activity to run choose lock * Need activity to run choose lock
*/ */
public static final int CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK = 1; public static final int CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK = 2;
/** /**
* Need activity to run confirm lock * Need activity to run confirm lock
*/ */
public static final int CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK = 2; public static final int CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK = 3;
/**
* Fail to use challenge from hardware generateChallenge(), shall finish activity with proper
* error code
*/
public static final int CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE = 3;
@IntDef(prefix = { "CREDENTIAL_" }, value = { @IntDef(prefix = { "CREDENTIAL_" }, value = {
CREDENTIAL_VALID,
CREDENTIAL_IS_GENERATING_CHALLENGE,
CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK, CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK,
CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK, CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK
CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE
}) })
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface CredentialAction {} public @interface CredentialAction {}
@@ -157,11 +167,10 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default
} }
} }
@NonNull private final LockPatternUtils mLockPatternUtils; @NonNull private final LockPatternUtils mLockPatternUtils;
@NonNull private final ChallengeGenerator mChallengeGenerator; @NonNull private final ChallengeGenerator mChallengeGenerator;
private CredentialModel mCredentialModel = null; private CredentialModel mCredentialModel = null;
@NonNull private final MutableLiveData<Integer> mActionLiveData = @NonNull private final MutableLiveData<Boolean> mGenerateChallengeFailLiveData =
new MutableLiveData<>(); new MutableLiveData<>();
public AutoCredentialViewModel( public AutoCredentialViewModel(
@@ -173,51 +182,63 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default
mChallengeGenerator = challengeGenerator; mChallengeGenerator = challengeGenerator;
} }
public void setCredentialModel(@NonNull CredentialModel credentialModel) { /**
mCredentialModel = credentialModel; * Set CredentialModel, the source is coming from savedInstanceState or activity intent
*/
public void setCredentialModel(@Nullable Bundle savedInstanceState, @NonNull Intent intent) {
mCredentialModel = new CredentialModel(
savedInstanceState != null
? savedInstanceState.getBundle(KEY_CREDENTIAL_MODEL)
: intent.getExtras(),
SystemClock.elapsedRealtimeClock());
if (DEBUG) {
Log.d(TAG, "setCredentialModel " + mCredentialModel + ", savedInstanceState exist:"
+ (savedInstanceState != null));
}
} }
/** /**
* Observe ActionLiveData for actions about choosing lock, confirming lock, or finishing * Handle onSaveInstanceState from activity
* activity
*/ */
@NonNull public void onSaveInstanceState(@NonNull Bundle outState) {
public LiveData<Integer> getActionLiveData() { outState.putBundle(KEY_CREDENTIAL_MODEL, mCredentialModel.getBundle());
return mActionLiveData;
} }
@Override @NonNull
public void onCreate(@NonNull LifecycleOwner owner) { public LiveData<Boolean> getGenerateChallengeFailLiveData() {
checkCredential(); return mGenerateChallengeFailLiveData;
} }
/** /**
* Check credential status for biometric enrollment. * Check credential status for biometric enrollment.
*/ */
private void checkCredential() { @CredentialAction
public int checkCredential() {
if (isValidCredential()) { if (isValidCredential()) {
return; return CREDENTIAL_VALID;
} }
final long gkPwHandle = mCredentialModel.getGkPwHandle(); final long gkPwHandle = mCredentialModel.getGkPwHandle();
if (isUnspecifiedPassword()) { if (isUnspecifiedPassword()) {
mActionLiveData.postValue(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK); return CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
} else if (CredentialModel.isValidGkPwHandle(gkPwHandle)) { } else if (CredentialModel.isValidGkPwHandle(gkPwHandle)) {
generateChallenge(gkPwHandle); generateChallenge(gkPwHandle);
return CREDENTIAL_IS_GENERATING_CHALLENGE;
} else { } else {
mActionLiveData.postValue(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK); return CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
} }
} }
private void generateChallenge(long gkPwHandle) { private void generateChallenge(long gkPwHandle) {
mChallengeGenerator.setCallback((sensorId, userId, challenge) -> { mChallengeGenerator.setCallback((sensorId, userId, challenge) -> {
mCredentialModel.setSensorId(sensorId);
mCredentialModel.setChallenge(challenge);
try { try {
final byte[] newToken = requestGatekeeperHat(gkPwHandle, challenge, userId); final byte[] newToken = requestGatekeeperHat(gkPwHandle, challenge, userId);
mCredentialModel.setSensorId(sensorId);
mCredentialModel.setChallenge(challenge);
mCredentialModel.setToken(newToken); mCredentialModel.setToken(newToken);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Log.e(TAG, "generateChallenge, IllegalStateException", e); Log.e(TAG, "generateChallenge, IllegalStateException", e);
mActionLiveData.postValue(CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE); mGenerateChallengeFailLiveData.postValue(true);
return; return;
} }
@@ -231,7 +252,7 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default
// Check credential again // Check credential again
if (!isValidCredential()) { if (!isValidCredential()) {
Log.w(TAG, "generateChallenge, invalid Credential"); Log.w(TAG, "generateChallenge, invalid Credential");
mActionLiveData.postValue(CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE); mGenerateChallengeFailLiveData.postValue(true);
} }
}); });
mChallengeGenerator.generateChallenge(getUserId()); mChallengeGenerator.generateChallenge(getUserId());
@@ -282,16 +303,16 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default
final VerifyCredentialResponse response = mLockPatternUtils final VerifyCredentialResponse response = mLockPatternUtils
.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId); .verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId);
if (!response.isMatched()) { if (!response.isMatched()) {
throw new IllegalStateException("Unable to request Gatekeeper HAT"); throw new GatekeeperCredentialNotMatchException("Unable to request Gatekeeper HAT");
} }
return response.getGatekeeperHAT(); return response.getGatekeeperHAT();
} }
/** /**
* Get Credential bundle which will be used to launch next activity. * Get Credential intent extra which will be used to launch next activity.
*/ */
@NonNull @NonNull
public Bundle getCredentialBundle() { public Bundle getCredentialIntentExtra() {
final Bundle retBundle = new Bundle(); final Bundle retBundle = new Bundle();
final long gkPwHandle = mCredentialModel.getGkPwHandle(); final long gkPwHandle = mCredentialModel.getGkPwHandle();
if (CredentialModel.isValidGkPwHandle(gkPwHandle)) { if (CredentialModel.isValidGkPwHandle(gkPwHandle)) {

View File

@@ -144,6 +144,13 @@ public class FingerprintEnrollIntroViewModel extends AndroidViewModel
return mPageStatusLiveData; return mPageStatusLiveData;
} }
/**
* Clear user's action live data (like clicking Agree, Skip, or Done)
*/
public void clearActionLiveData() {
mActionLiveData.setValue(null);
}
/** /**
* Get user's action live data (like clicking Agree, Skip, or Done) * Get user's action live data (like clicking Agree, Skip, or Done)
*/ */

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) 2022 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.model;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_SENSOR_ID;
import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_CHALLENGE;
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 android.annotation.NonNull;
import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;
import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.password.ChooseLockSettingsHelper;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Clock;
import java.util.Arrays;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class CredentialModelTest {
private final Clock mClock = SystemClock.elapsedRealtimeClock();
public static Bundle newCredentialModelIntentExtras(int userId, long challenge, int sensorId,
@Nullable byte[] token, long gkPwHandle) {
final Bundle bundle = new Bundle();
bundle.putInt(Intent.EXTRA_USER_ID, userId);
bundle.putInt(EXTRA_KEY_SENSOR_ID, sensorId);
bundle.putLong(EXTRA_KEY_CHALLENGE, challenge);
bundle.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
bundle.putLong(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
return bundle;
}
public static Bundle newValidTokenCredentialIntentExtras(int userId) {
return newCredentialModelIntentExtras(userId, 1L, 1, new byte[] { 0 }, 0L);
}
public static Bundle newInvalidChallengeCredentialIntentExtras(int userId) {
return newCredentialModelIntentExtras(userId, INVALID_CHALLENGE, 1, null, 0L);
}
public static Bundle newGkPwHandleCredentialIntentExtras(int userId, long gkPwHandle) {
return newCredentialModelIntentExtras(userId, INVALID_CHALLENGE, 1, null, gkPwHandle);
}
private static void checkBundleLongValue(@NonNull Bundle bundle1, @NonNull Bundle bundle2,
@NonNull String key) {
if (!bundle1.containsKey(key)) {
return;
}
final int value1 = bundle1.getInt(key);
final int value2 = bundle2.getInt(key);
assertWithMessage("bundle not match, key:" + key + ", value1:" + value1 + ", value2:"
+ value2).that(value1).isEqualTo(value2);
}
private static void checkBundleIntValue(@NonNull Bundle bundle1, @NonNull Bundle bundle2,
@NonNull String key) {
if (!bundle1.containsKey(key)) {
return;
}
final long value1 = bundle1.getLong(key);
final long value2 = bundle2.getLong(key);
assertWithMessage("bundle not match, key:" + key + ", value1:" + value1 + ", value2:"
+ value2).that(value1).isEqualTo(value2);
}
private static void checkBundleByteArrayValue(@NonNull Bundle bundle1, @NonNull Bundle bundle2,
@NonNull String key) {
if (!bundle1.containsKey(key)) {
return;
}
final byte[] value1 = bundle1.getByteArray(key);
final byte[] value2 = bundle2.getByteArray(key);
final String errMsg = "bundle not match, key:" + key + ", value1:" + Arrays.toString(value1)
+ ", value2:" + Arrays.toString(value2);
if (value1 == null) {
assertWithMessage(errMsg).that(value2).isNull();
} else {
assertWithMessage(errMsg).that(value1.length).isEqualTo(value2.length);
for (int i = 0; i < value1.length; ++i) {
assertWithMessage(errMsg).that(value1[i]).isEqualTo(value2[i]);
}
}
}
public static void verifySameCredentialModels(@NonNull CredentialModel model1,
@NonNull CredentialModel model2) {
assertThat(model1.getUserId()).isEqualTo(model2.getUserId());
assertThat(model1.getSensorId()).isEqualTo(model2.getSensorId());
assertThat(model1.getChallenge()).isEqualTo(model2.getChallenge());
assertThat(model1.getGkPwHandle()).isEqualTo(model2.getGkPwHandle());
final byte[] token1 = model1.getToken();
final byte[] token2 = model2.getToken();
if (token1 == null) {
assertThat(token2).isNull();
} else {
assertThat(token2).isNotNull();
assertThat(token1.length).isEqualTo(token2.length);
for (int i = 0; i < token1.length; ++i) {
assertThat(token1[i]).isEqualTo(token2[i]);
}
}
final Bundle bundle1 = model1.getBundle();
final Bundle bundle2 = model2.getBundle();
final Set<String> keySet1 = bundle1.keySet();
assertThat(keySet1.equals(bundle2.keySet())).isTrue();
checkBundleIntValue(bundle1, bundle2, Intent.EXTRA_USER_ID);
checkBundleIntValue(bundle1, bundle2, EXTRA_KEY_SENSOR_ID);
checkBundleLongValue(bundle1, bundle2, EXTRA_KEY_CHALLENGE);
checkBundleByteArrayValue(bundle1, bundle2, EXTRA_KEY_CHALLENGE);
checkBundleLongValue(bundle1, bundle2, EXTRA_KEY_GK_PW_HANDLE);
}
@Test
public void sameValueFromBundle() {
final Bundle bundle = newCredentialModelIntentExtras(1234, 6677L, 1,
new byte[] { 33, 44, 55 }, 987654321);
final CredentialModel model1 = new CredentialModel(bundle, mClock);
final CredentialModel model2 = new CredentialModel(model1.getBundle(), mClock);
verifySameCredentialModels(model1, model2);
}
@Test
public void sameValueFromBundle_nullToken() {
final Bundle bundle = newCredentialModelIntentExtras(22, 33L, 1, null, 21L);
final CredentialModel model1 = new CredentialModel(bundle, mClock);
final CredentialModel model2 = new CredentialModel(model1.getBundle(), mClock);
verifySameCredentialModels(model1, model2);
}
}

View File

@@ -24,12 +24,22 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE; import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_SENSOR_ID; import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_SENSOR_ID;
import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_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.CredentialModel.INVALID_SENSOR_ID; import static com.android.settings.biometrics2.ui.model.CredentialModel.INVALID_SENSOR_ID;
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.newInvalidChallengeCredentialIntentExtras;
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_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_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.ChallengeGenerator;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CredentialAction; 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.GenerateChallengeCallback;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.KEY_CREDENTIAL_MODEL;
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.assertThat;
@@ -38,12 +48,11 @@ import static org.mockito.Mockito.when;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.SystemClock; import android.os.Bundle;
import android.os.UserHandle; import android.os.UserHandle;
import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResult;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -68,7 +77,6 @@ public class AutoCredentialViewModelTest {
@Rule public final MockitoRule mockito = MockitoJUnit.rule(); @Rule public final MockitoRule mockito = MockitoJUnit.rule();
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule(); @Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
@Mock private LifecycleOwner mLifecycleOwner;
@Mock private LockPatternUtils mLockPatternUtils; @Mock private LockPatternUtils mLockPatternUtils;
private TestChallengeGenerator mChallengeGenerator = null; private TestChallengeGenerator mChallengeGenerator = null;
private AutoCredentialViewModel mAutoCredentialViewModel; private AutoCredentialViewModel mAutoCredentialViewModel;
@@ -82,142 +90,222 @@ public class AutoCredentialViewModelTest {
mChallengeGenerator); mChallengeGenerator);
} }
private CredentialModel newCredentialModel(int userId, long challenge, private void setupGenerateChallenge(int userId, int newSensorId, long newChallenge) {
@Nullable byte[] token, long gkPwHandle) {
final Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_USER_ID, userId);
intent.putExtra(EXTRA_KEY_SENSOR_ID, 1);
intent.putExtra(EXTRA_KEY_CHALLENGE, challenge);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
return new CredentialModel(intent, SystemClock.elapsedRealtimeClock());
}
private CredentialModel newValidTokenCredentialModel(int userId) {
return newCredentialModel(userId, 1L, new byte[] { 0 }, 0L);
}
private CredentialModel newInvalidChallengeCredentialModel(int userId) {
return newCredentialModel(userId, INVALID_CHALLENGE, null, 0L);
}
private CredentialModel newGkPwHandleCredentialModel(int userId, long gkPwHandle) {
return newCredentialModel(userId, INVALID_CHALLENGE, null, gkPwHandle);
}
private void verifyNothingHappen() {
assertThat(mAutoCredentialViewModel.getActionLiveData().getValue()).isNull();
}
private void verifyOnlyActionLiveData(@CredentialAction int action) {
final Integer value = mAutoCredentialViewModel.getActionLiveData().getValue();
assertThat(value).isEqualTo(action);
}
private void setupGenerateTokenFlow(long gkPwHandle, int userId, int newSensorId,
long newChallenge) {
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn( when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING); PASSWORD_QUALITY_SOMETHING);
mChallengeGenerator.mUserId = userId; mChallengeGenerator.mUserId = userId;
mChallengeGenerator.mSensorId = newSensorId; mChallengeGenerator.mSensorId = newSensorId;
mChallengeGenerator.mChallenge = newChallenge; mChallengeGenerator.mChallenge = newChallenge;
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
} }
@Test @Test
public void checkCredential_validCredentialCase() { public void testGetCredentialIntentExtra_sameResultFromSavedInstanceOrIntent() {
final Bundle extras = newCredentialModelIntentExtras(12, 33, 1, new byte[] { 2, 3 }, 3L);
AutoCredentialViewModel autoCredentialViewModel2 = new AutoCredentialViewModel(
ApplicationProvider.getApplicationContext(),
mLockPatternUtils,
mChallengeGenerator);
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(extras));
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL, extras);
autoCredentialViewModel2.setCredentialModel(savedInstance, new Intent());
final Bundle bundle1 = mAutoCredentialViewModel.getCredentialIntentExtra();
final Bundle bundle2 = autoCredentialViewModel2.getCredentialIntentExtra();
assertThat(bundle1.getLong(EXTRA_KEY_GK_PW_HANDLE))
.isEqualTo(bundle2.getLong(EXTRA_KEY_GK_PW_HANDLE));
assertThat(bundle1.getLong(Intent.EXTRA_USER_ID))
.isEqualTo(bundle2.getLong(Intent.EXTRA_USER_ID));
assertThat(bundle1.getLong(EXTRA_KEY_CHALLENGE))
.isEqualTo(bundle2.getLong(EXTRA_KEY_CHALLENGE));
assertThat(bundle1.getInt(EXTRA_KEY_SENSOR_ID))
.isEqualTo(bundle2.getInt(EXTRA_KEY_SENSOR_ID));
final byte[] token1 = bundle1.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
final byte[] token2 = bundle2.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
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 testGetCredentialIntentExtra_sameResultFromSavedInstanceOrIntent_invalidValues() {
final Bundle extras = newCredentialModelIntentExtras(UserHandle.USER_NULL,
INVALID_CHALLENGE, INVALID_SENSOR_ID, null, INVALID_GK_PW_HANDLE);
AutoCredentialViewModel autoCredentialViewModel2 = new AutoCredentialViewModel(
ApplicationProvider.getApplicationContext(),
mLockPatternUtils,
mChallengeGenerator);
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(extras));
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL, extras);
autoCredentialViewModel2.setCredentialModel(savedInstance, new Intent());
final Bundle bundle1 = mAutoCredentialViewModel.getCredentialIntentExtra();
final Bundle bundle2 = autoCredentialViewModel2.getCredentialIntentExtra();
assertThat(bundle1.containsKey(EXTRA_KEY_GK_PW_HANDLE)).isFalse();
assertThat(bundle2.containsKey(EXTRA_KEY_GK_PW_HANDLE)).isFalse();
assertThat(bundle1.containsKey(EXTRA_KEY_CHALLENGE_TOKEN)).isFalse();
assertThat(bundle2.containsKey(EXTRA_KEY_CHALLENGE_TOKEN)).isFalse();
assertThat(bundle1.containsKey(EXTRA_KEY_SENSOR_ID)).isTrue();
assertThat(bundle2.containsKey(EXTRA_KEY_SENSOR_ID)).isTrue();
assertThat(bundle1.containsKey(Intent.EXTRA_USER_ID)).isFalse();
assertThat(bundle2.containsKey(Intent.EXTRA_USER_ID)).isFalse();
}
@Test
public void testCheckCredential_validCredentialCase() {
final int userId = 99; final int userId = 99;
mAutoCredentialViewModel.setCredentialModel(newValidTokenCredentialModel(userId)); mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newValidTokenCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn( when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING); PASSWORD_QUALITY_SOMETHING);
// Run credential check // Run credential check
mAutoCredentialViewModel.onCreate(mLifecycleOwner); @CredentialAction final int action = mAutoCredentialViewModel.checkCredential();
verifyNothingHappen(); assertThat(action).isEqualTo(CREDENTIAL_VALID);
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
} }
@Test @Test
public void checkCredential_needToChooseLock() { public void testCheckCredential_needToChooseLock() {
final int userId = 100; final int userId = 100;
mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId)); mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn( when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_UNSPECIFIED); PASSWORD_QUALITY_UNSPECIFIED);
// Run credential check // Run credential check
mAutoCredentialViewModel.onCreate(mLifecycleOwner); @CredentialAction final int action = mAutoCredentialViewModel.checkCredential();
verifyOnlyActionLiveData(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK); assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK);
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
} }
@Test @Test
public void checkCredential_needToConfirmLockFoSomething() { public void testCheckCredential_needToConfirmLockFoSomething() {
final int userId = 101; final int userId = 101;
mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId)); mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn( when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING); PASSWORD_QUALITY_SOMETHING);
// Run credential check // Run credential check
mAutoCredentialViewModel.onCreate(mLifecycleOwner); @CredentialAction final int action = mAutoCredentialViewModel.checkCredential();
verifyOnlyActionLiveData(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK); assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
} }
@Test @Test
public void checkCredential_needToConfirmLockForNumeric() { public void testCheckCredential_needToConfirmLockForNumeric() {
final int userId = 102; final int userId = 102;
mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId)); mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn( when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_NUMERIC); PASSWORD_QUALITY_NUMERIC);
// Run credential check // Run credential check
mAutoCredentialViewModel.onCreate(mLifecycleOwner); @CredentialAction final int action = mAutoCredentialViewModel.checkCredential();
verifyOnlyActionLiveData(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK); assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
} }
@Test @Test
public void checkCredential_needToConfirmLockForAlphabetic() { public void testCheckCredential_needToConfirmLockForAlphabetic() {
final int userId = 103; final int userId = 103;
mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId)); mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn( when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_ALPHABETIC); PASSWORD_QUALITY_ALPHABETIC);
// Run credential check // Run credential check
mAutoCredentialViewModel.onCreate(mLifecycleOwner); @CredentialAction final int action = mAutoCredentialViewModel.checkCredential();
verifyOnlyActionLiveData(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK); assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
} }
@Test @Test
public void checkCredential_generateChallenge() { public void testCheckCredential_generateChallenge() {
final int userId = 104; final int userId = 104;
final long gkPwHandle = 1111L; final long gkPwHandle = 1111L;
final CredentialModel credentialModel = newGkPwHandleCredentialModel(userId, gkPwHandle); mAutoCredentialViewModel.setCredentialModel(null,
mAutoCredentialViewModel.setCredentialModel(credentialModel); new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn( when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING); PASSWORD_QUALITY_SOMETHING);
final int newSensorId = 10; final int newSensorId = 10;
final long newChallenge = 20L; final long newChallenge = 20L;
setupGenerateTokenFlow(gkPwHandle, userId, newSensorId, newChallenge); setupGenerateChallenge(userId, newSensorId, newChallenge);
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
// Run credential check // Run credential check
mAutoCredentialViewModel.onCreate(mLifecycleOwner); @CredentialAction final int action = mAutoCredentialViewModel.checkCredential();
assertThat(mAutoCredentialViewModel.getActionLiveData().getValue()).isNull(); assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
assertThat(credentialModel.getSensorId()).isEqualTo(newSensorId); assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
assertThat(credentialModel.getChallenge()).isEqualTo(newChallenge); final Bundle extras = mAutoCredentialViewModel.getCredentialIntentExtra();
assertThat(CredentialModel.isValidToken(credentialModel.getToken())).isTrue(); assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
assertThat(CredentialModel.isValidGkPwHandle(credentialModel.getGkPwHandle())).isFalse(); assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
assertThat(CredentialModel.isValidToken(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)))
.isTrue();
assertThat(CredentialModel.isValidGkPwHandle(extras.getLong(EXTRA_KEY_GK_PW_HANDLE)))
.isFalse();
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1); assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
} }
@Test @Test
public void testGetUserId() { public void testCheckCredential_generateChallengeFail() {
final int userId = 104;
final long gkPwHandle = 1111L;
mAutoCredentialViewModel.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 = mAutoCredentialViewModel.checkCredential();
assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isTrue();
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
}
@Test
public void testGetUserId_fromIntent() {
final int userId = 106; final int userId = 106;
mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId)); mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
// Get userId
assertThat(mAutoCredentialViewModel.getUserId()).isEqualTo(userId);
}
@Test
public void testGetUserId_fromSavedInstance() {
final int userId = 106;
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL,
newInvalidChallengeCredentialIntentExtras(userId));
mAutoCredentialViewModel.setCredentialModel(savedInstance, new Intent());
// Get userId // Get userId
assertThat(mAutoCredentialViewModel.getUserId()).isEqualTo(userId); assertThat(mAutoCredentialViewModel.getUserId()).isEqualTo(userId);
@@ -227,8 +315,8 @@ public class AutoCredentialViewModelTest {
public void testCheckNewCredentialFromActivityResult_invalidChooseLock() { public void testCheckNewCredentialFromActivityResult_invalidChooseLock() {
final int userId = 107; final int userId = 107;
final long gkPwHandle = 3333L; final long gkPwHandle = 3333L;
mAutoCredentialViewModel.setCredentialModel( mAutoCredentialViewModel.setCredentialModel(null,
newGkPwHandleCredentialModel(userId, gkPwHandle)); new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
final Intent intent = new Intent(); final Intent intent = new Intent();
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
@@ -237,15 +325,15 @@ public class AutoCredentialViewModelTest {
new ActivityResult(ChooseLockPattern.RESULT_FINISHED + 1, intent)); new ActivityResult(ChooseLockPattern.RESULT_FINISHED + 1, intent));
assertThat(ret).isFalse(); assertThat(ret).isFalse();
verifyNothingHappen(); assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
} }
@Test @Test
public void testCheckNewCredentialFromActivityResult_invalidConfirmLock() { public void testCheckNewCredentialFromActivityResult_invalidConfirmLock() {
final int userId = 107; final int userId = 107;
final long gkPwHandle = 3333L; final long gkPwHandle = 3333L;
mAutoCredentialViewModel.setCredentialModel( mAutoCredentialViewModel.setCredentialModel(null,
newGkPwHandleCredentialModel(userId, gkPwHandle)); new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
final Intent intent = new Intent(); final Intent intent = new Intent();
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
@@ -254,62 +342,68 @@ public class AutoCredentialViewModelTest {
new ActivityResult(Activity.RESULT_OK + 1, intent)); new ActivityResult(Activity.RESULT_OK + 1, intent));
assertThat(ret).isFalse(); assertThat(ret).isFalse();
verifyNothingHappen(); assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
} }
@Test @Test
public void testCheckNewCredentialFromActivityResult_nullDataChooseLock() { public void testCheckNewCredentialFromActivityResult_nullDataChooseLock() {
final int userId = 108; final int userId = 108;
final long gkPwHandle = 4444L; final long gkPwHandle = 4444L;
mAutoCredentialViewModel.setCredentialModel( mAutoCredentialViewModel.setCredentialModel(null,
newGkPwHandleCredentialModel(userId, gkPwHandle)); new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
// run checkNewCredentialFromActivityResult() // run checkNewCredentialFromActivityResult()
final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(true, final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(true,
new ActivityResult(ChooseLockPattern.RESULT_FINISHED, null)); new ActivityResult(ChooseLockPattern.RESULT_FINISHED, null));
assertThat(ret).isFalse(); assertThat(ret).isFalse();
verifyNothingHappen(); assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
} }
@Test @Test
public void testCheckNewCredentialFromActivityResult_nullDataConfirmLock() { public void testCheckNewCredentialFromActivityResult_nullDataConfirmLock() {
final int userId = 109; final int userId = 109;
mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId)); mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
// run checkNewCredentialFromActivityResult() // run checkNewCredentialFromActivityResult()
final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(false, final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(false,
new ActivityResult(Activity.RESULT_OK, null)); new ActivityResult(Activity.RESULT_OK, null));
assertThat(ret).isFalse(); assertThat(ret).isFalse();
verifyNothingHappen(); assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
} }
@Test @Test
public void testCheckNewCredentialFromActivityResult_validChooseLock() { public void testCheckNewCredentialFromActivityResult_validChooseLock() {
final int userId = 108; final int userId = 108;
final CredentialModel credentialModel = newInvalidChallengeCredentialModel(userId); mAutoCredentialViewModel.setCredentialModel(null,
mAutoCredentialViewModel.setCredentialModel(credentialModel); new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn( when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING); PASSWORD_QUALITY_SOMETHING);
final long gkPwHandle = 6666L; final long gkPwHandle = 6666L;
final int newSensorId = 50; final int newSensorId = 50;
final long newChallenge = 60L; final long newChallenge = 60L;
setupGenerateTokenFlow(gkPwHandle, userId, newSensorId, newChallenge); setupGenerateChallenge(userId, newSensorId, newChallenge);
final Intent intent = new Intent(); when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle); .thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
// Run checkNewCredentialFromActivityResult() // Run checkNewCredentialFromActivityResult()
final Intent intent = new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
gkPwHandle);
final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(true, final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(true,
new ActivityResult(ChooseLockPattern.RESULT_FINISHED, intent)); new ActivityResult(ChooseLockPattern.RESULT_FINISHED, intent));
assertThat(ret).isTrue(); assertThat(ret).isTrue();
assertThat(mAutoCredentialViewModel.getActionLiveData().getValue()).isNull(); assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
assertThat(credentialModel.getSensorId()).isEqualTo(newSensorId); final Bundle extras = mAutoCredentialViewModel.getCredentialIntentExtra();
assertThat(credentialModel.getChallenge()).isEqualTo(newChallenge); assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
assertThat(CredentialModel.isValidToken(credentialModel.getToken())).isTrue(); assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
assertThat(CredentialModel.isValidGkPwHandle(credentialModel.getGkPwHandle())).isFalse(); assertThat(CredentialModel.isValidToken(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)))
.isTrue();
assertThat(CredentialModel.isValidGkPwHandle(extras.getLong(EXTRA_KEY_GK_PW_HANDLE)))
.isFalse();
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1); assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
} }
@@ -317,28 +411,33 @@ public class AutoCredentialViewModelTest {
@Test @Test
public void testCheckNewCredentialFromActivityResult_validConfirmLock() { public void testCheckNewCredentialFromActivityResult_validConfirmLock() {
final int userId = 109; final int userId = 109;
final CredentialModel credentialModel = newInvalidChallengeCredentialModel(userId); mAutoCredentialViewModel.setCredentialModel(null,
mAutoCredentialViewModel.setCredentialModel(credentialModel); new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn( when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING); PASSWORD_QUALITY_SOMETHING);
final long gkPwHandle = 5555L; final long gkPwHandle = 5555L;
final int newSensorId = 80; final int newSensorId = 80;
final long newChallenge = 90L; final long newChallenge = 90L;
setupGenerateTokenFlow(gkPwHandle, userId, newSensorId, newChallenge); setupGenerateChallenge(userId, newSensorId, newChallenge);
final Intent intent = new Intent(); when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle); .thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
// Run checkNewCredentialFromActivityResult() // Run checkNewCredentialFromActivityResult()
final Intent intent = new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
gkPwHandle);
final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(false, final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(false,
new ActivityResult(Activity.RESULT_OK, intent)); new ActivityResult(Activity.RESULT_OK, intent));
assertThat(ret).isTrue(); assertThat(ret).isTrue();
assertThat(mAutoCredentialViewModel.getActionLiveData().getValue()).isNull(); assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
assertThat(credentialModel.getSensorId()).isEqualTo(newSensorId); final Bundle extras = mAutoCredentialViewModel.getCredentialIntentExtra();
assertThat(credentialModel.getChallenge()).isEqualTo(newChallenge); assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
assertThat(CredentialModel.isValidToken(credentialModel.getToken())).isTrue(); assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
assertThat(CredentialModel.isValidGkPwHandle(credentialModel.getGkPwHandle())).isFalse(); assertThat(CredentialModel.isValidToken(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)))
.isTrue();
assertThat(CredentialModel.isValidGkPwHandle(extras.getLong(EXTRA_KEY_GK_PW_HANDLE)))
.isFalse();
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1); assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
} }
@@ -377,4 +476,12 @@ public class AutoCredentialViewModelTest {
.setGatekeeperHAT(hat) .setGatekeeperHAT(hat)
.build(); .build();
} }
private VerifyCredentialResponse newBadCredential(int timeout) {
if (timeout > 0) {
return VerifyCredentialResponse.fromTimeout(timeout);
} else {
return VerifyCredentialResponse.fromError();
}
}
} }

View File

@@ -46,6 +46,7 @@ import android.hardware.fingerprint.FingerprintManager;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -92,6 +93,18 @@ public class FingerprintEnrollIntroViewModelTest {
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_UNKNOWN); assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_UNKNOWN);
} }
@Test
public void testClearActionLiveData() {
final MutableLiveData<Integer> actionLiveData =
(MutableLiveData<Integer>) mViewModel.getActionLiveData();
actionLiveData.postValue(1);
assertThat(actionLiveData.getValue()).isEqualTo(1);
mViewModel.clearActionLiveData();
assertThat(actionLiveData.getValue()).isNull();
}
@Test @Test
public void testGetEnrollmentRequest() { public void testGetEnrollmentRequest() {
final EnrollmentRequest request = newAllFalseRequest(mApplication); final EnrollmentRequest request = newAllFalseRequest(mApplication);