Merge "Fix FingerprintEnrollmentActivity rotaiton crash"

This commit is contained in:
Milton Wu
2022-11-23 02:05:24 +00:00
committed by Android (Google) Code Review
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 android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -80,17 +81,30 @@ public final class CredentialModel {
@Nullable
private Long mClearGkPwHandleMillis = null;
public CredentialModel(@NonNull Intent intent, @NonNull Clock clock) {
mUserId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
mSensorId = intent.getIntExtra(EXTRA_KEY_SENSOR_ID, INVALID_SENSOR_ID);
mChallenge = intent.getLongExtra(EXTRA_KEY_CHALLENGE, INVALID_CHALLENGE);
mToken = intent.getByteArrayExtra(EXTRA_KEY_CHALLENGE_TOKEN);
mGkPwHandle = intent.getLongExtra(EXTRA_KEY_GK_PW_HANDLE,
INVALID_GK_PW_HANDLE);
public CredentialModel(@NonNull Bundle bundle, @NonNull Clock clock) {
mUserId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
mSensorId = bundle.getInt(EXTRA_KEY_SENSOR_ID, INVALID_SENSOR_ID);
mChallenge = bundle.getLong(EXTRA_KEY_CHALLENGE, INVALID_CHALLENGE);
mToken = bundle.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
mGkPwHandle = bundle.getLong(EXTRA_KEY_GK_PW_HANDLE, INVALID_GK_PW_HANDLE);
mClock = clock;
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
*/

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 android.app.Activity;
import android.app.admin.DevicePolicyResourcesManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
@@ -63,9 +63,6 @@ public class FingerprintEnrollIntroFragment extends Fragment {
private static final String TAG = "FingerprintEnrollIntroFragment";
@NonNull private final ViewModelProvider mViewModelProvider;
@Nullable private final DevicePolicyResourcesManager mDevicePolicyMgrRes;
private FingerprintEnrollIntroViewModel mViewModel = null;
private View mView = null;
@@ -75,12 +72,8 @@ public class FingerprintEnrollIntroFragment extends Fragment {
private TextView mFooterMessage6 = null;
@Nullable private PorterDuffColorFilter mIconColorFilter;
public FingerprintEnrollIntroFragment(
@NonNull ViewModelProvider viewModelProvider,
@Nullable DevicePolicyResourcesManager devicePolicyMgrRes) {
public FingerprintEnrollIntroFragment() {
super();
mViewModelProvider = viewModelProvider;
mDevicePolicyMgrRes = devicePolicyMgrRes;
}
@Nullable
@@ -197,7 +190,8 @@ public class FingerprintEnrollIntroFragment extends Fragment {
@Override
public void onAttach(@NonNull Context context) {
mViewModel = mViewModelProvider.get(FingerprintEnrollIntroViewModel.class);
mViewModel = new ViewModelProvider(getActivity())
.get(FingerprintEnrollIntroViewModel.class);
getLifecycle().addObserver(mViewModel);
super.onAttach(context);
}
@@ -232,12 +226,16 @@ public class FingerprintEnrollIntroFragment extends Fragment {
private String getDescriptionDisabledByAdmin(@NonNull Context context) {
final int defaultStrId =
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");
return "";
}
return mDevicePolicyMgrRes.getString(FINGERPRINT_UNLOCK_DISABLED,
() -> context.getString(defaultStrId));
}
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 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_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_DONE_AND_FINISH;
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.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import androidx.activity.result.ActivityResult;
@@ -42,7 +41,6 @@ import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.viewmodel.CreationExtras;
import androidx.lifecycle.viewmodel.MutableCreationExtras;
@@ -51,11 +49,9 @@ import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
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.factory.BiometricsFragmentFactory;
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.viewmodel.AutoCredentialViewModel;
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator;
@@ -102,12 +98,12 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
getLifecycle().addObserver(mViewModel);
mAutoCredentialViewModel = viewModelProvider.get(AutoCredentialViewModel.class);
mAutoCredentialViewModel.setCredentialModel(new CredentialModel(getIntent(),
SystemClock.elapsedRealtimeClock()));
getLifecycle().addObserver(mAutoCredentialViewModel);
mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent());
mAutoCredentialViewModel.getGenerateChallengeFailLiveData().observe(this,
this::onGenerateChallengeFail);
mViewModel.getSetResultLiveData().observe(this, this::onSetActivityResult);
mAutoCredentialViewModel.getActionLiveData().observe(this, this::onCredentialAction);
checkCredential();
// Theme
setTheme(mViewModel.getRequest().getTheme());
@@ -116,21 +112,29 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
// fragment
setContentView(R.layout.biometric_enrollment_container);
final FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.setFragmentFactory(
new BiometricsFragmentFactory(getApplication(), viewModelProvider));
final FingerprintEnrollIntroViewModel fingerprintEnrollIntroViewModel =
viewModelProvider.get(FingerprintEnrollIntroViewModel.class);
fingerprintEnrollIntroViewModel.setEnrollmentRequest(mViewModel.getRequest());
fingerprintEnrollIntroViewModel.setUserId(mAutoCredentialViewModel.getUserId());
// Clear ActionLiveData in FragmentViewModel to prevent getting previous action when
// recreate
fingerprintEnrollIntroViewModel.clearActionLiveData();
fingerprintEnrollIntroViewModel.getActionLiveData().observe(
this, this::observeIntroAction);
final String tag = "FingerprintEnrollIntroFragment";
fragmentManager.beginTransaction()
.setReorderingAllowed(true)
.add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null, tag)
.commit();
if (savedInstanceState == null) {
final String tag = "FingerprintEnrollIntroFragment";
getSupportFragmentManager().beginTransaction()
.setReorderingAllowed(true)
.add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null,
tag)
.commit();
}
}
private void onGenerateChallengeFail(@NonNull Boolean isFail) {
onSetActivityResult(new ActivityResult(RESULT_CANCELED, null));
}
private void onSetActivityResult(@NonNull ActivityResult result) {
@@ -141,8 +145,8 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
finish();
}
private void onCredentialAction(@NonNull Integer action) {
switch (action) {
private void checkCredential() {
switch (mAutoCredentialViewModel.checkCredential()) {
case CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK: {
final Intent intent = mAutoCredentialViewModel.getChooseLockIntent(this,
mViewModel.getRequest().isSuw(), mViewModel.getRequest().getSuwExtras());
@@ -168,12 +172,9 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
}
return;
}
case CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE: {
Log.w(TAG, "observeCredentialLiveData, finish with action:" + action);
if (mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()) {
setResult(Activity.RESULT_CANCELED);
}
finish();
case CREDENTIAL_VALID:
case CREDENTIAL_IS_GENERATING_CHALLENGE: {
// Do nothing
}
}
}
@@ -186,10 +187,15 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
if (mAutoCredentialViewModel.checkNewCredentialFromActivityResult(
isChooseLock, activityResult)) {
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) {
case FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH: {
onSetActivityResult(
@@ -207,9 +213,9 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
Log.w(TAG, "startNext, isSuw:" + isSuw + ", fail to set isWaiting flag");
}
final Intent intent = new Intent(this, isSuw
? SetupFingerprintEnrollEnrolling.class
? SetupFingerprintEnrollFindSensor.class
: FingerprintEnrollFindSensor.class);
intent.putExtras(mAutoCredentialViewModel.getCredentialBundle());
intent.putExtras(mAutoCredentialViewModel.getCredentialIntentExtra());
intent.putExtras(mViewModel.getNextActivityBaseIntentExtras());
mNextActivityLauncher.launch(intent);
}
@@ -272,5 +278,6 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.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.Intent;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import androidx.activity.result.ActivityResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
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.ui.model.CredentialModel;
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
* 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 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
*/
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
*/
public static final int CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK = 2;
/**
* Fail to use challenge from hardware generateChallenge(), shall finish activity with proper
* error code
*/
public static final int CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE = 3;
public static final int CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK = 3;
@IntDef(prefix = { "CREDENTIAL_" }, value = {
CREDENTIAL_VALID,
CREDENTIAL_IS_GENERATING_CHALLENGE,
CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK,
CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK,
CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE
CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK
})
@Retention(RetentionPolicy.SOURCE)
public @interface CredentialAction {}
@@ -157,11 +167,10 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default
}
}
@NonNull private final LockPatternUtils mLockPatternUtils;
@NonNull private final ChallengeGenerator mChallengeGenerator;
private CredentialModel mCredentialModel = null;
@NonNull private final MutableLiveData<Integer> mActionLiveData =
@NonNull private final MutableLiveData<Boolean> mGenerateChallengeFailLiveData =
new MutableLiveData<>();
public AutoCredentialViewModel(
@@ -173,51 +182,63 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default
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
* activity
* Handle onSaveInstanceState from activity
*/
@NonNull
public LiveData<Integer> getActionLiveData() {
return mActionLiveData;
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBundle(KEY_CREDENTIAL_MODEL, mCredentialModel.getBundle());
}
@Override
public void onCreate(@NonNull LifecycleOwner owner) {
checkCredential();
@NonNull
public LiveData<Boolean> getGenerateChallengeFailLiveData() {
return mGenerateChallengeFailLiveData;
}
/**
* Check credential status for biometric enrollment.
*/
private void checkCredential() {
@CredentialAction
public int checkCredential() {
if (isValidCredential()) {
return;
return CREDENTIAL_VALID;
}
final long gkPwHandle = mCredentialModel.getGkPwHandle();
if (isUnspecifiedPassword()) {
mActionLiveData.postValue(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK);
return CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
} else if (CredentialModel.isValidGkPwHandle(gkPwHandle)) {
generateChallenge(gkPwHandle);
return CREDENTIAL_IS_GENERATING_CHALLENGE;
} else {
mActionLiveData.postValue(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
return CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
}
}
private void generateChallenge(long gkPwHandle) {
mChallengeGenerator.setCallback((sensorId, userId, challenge) -> {
mCredentialModel.setSensorId(sensorId);
mCredentialModel.setChallenge(challenge);
try {
final byte[] newToken = requestGatekeeperHat(gkPwHandle, challenge, userId);
mCredentialModel.setSensorId(sensorId);
mCredentialModel.setChallenge(challenge);
mCredentialModel.setToken(newToken);
} catch (IllegalStateException e) {
Log.e(TAG, "generateChallenge, IllegalStateException", e);
mActionLiveData.postValue(CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE);
mGenerateChallengeFailLiveData.postValue(true);
return;
}
@@ -231,7 +252,7 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default
// Check credential again
if (!isValidCredential()) {
Log.w(TAG, "generateChallenge, invalid Credential");
mActionLiveData.postValue(CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE);
mGenerateChallengeFailLiveData.postValue(true);
}
});
mChallengeGenerator.generateChallenge(getUserId());
@@ -282,16 +303,16 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default
final VerifyCredentialResponse response = mLockPatternUtils
.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId);
if (!response.isMatched()) {
throw new IllegalStateException("Unable to request Gatekeeper HAT");
throw new GatekeeperCredentialNotMatchException("Unable to request Gatekeeper HAT");
}
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
public Bundle getCredentialBundle() {
public Bundle getCredentialIntentExtra() {
final Bundle retBundle = new Bundle();
final long gkPwHandle = mCredentialModel.getGkPwHandle();
if (CredentialModel.isValidGkPwHandle(gkPwHandle)) {

View File

@@ -144,6 +144,13 @@ public class FingerprintEnrollIntroViewModel extends AndroidViewModel
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)
*/

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_SENSOR_ID;
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.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_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.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;
@@ -38,12 +48,11 @@ import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.Activity;
import android.content.Intent;
import android.os.SystemClock;
import android.os.Bundle;
import android.os.UserHandle;
import androidx.activity.result.ActivityResult;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -68,7 +77,6 @@ public class AutoCredentialViewModelTest {
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
@Mock private LifecycleOwner mLifecycleOwner;
@Mock private LockPatternUtils mLockPatternUtils;
private TestChallengeGenerator mChallengeGenerator = null;
private AutoCredentialViewModel mAutoCredentialViewModel;
@@ -82,142 +90,222 @@ public class AutoCredentialViewModelTest {
mChallengeGenerator);
}
private CredentialModel newCredentialModel(int userId, long challenge,
@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) {
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;
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
}
@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;
mAutoCredentialViewModel.setCredentialModel(newValidTokenCredentialModel(userId));
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newValidTokenCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
// Run credential check
mAutoCredentialViewModel.onCreate(mLifecycleOwner);
@CredentialAction final int action = mAutoCredentialViewModel.checkCredential();
verifyNothingHappen();
assertThat(action).isEqualTo(CREDENTIAL_VALID);
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
}
@Test
public void checkCredential_needToChooseLock() {
public void testCheckCredential_needToChooseLock() {
final int userId = 100;
mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId));
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_UNSPECIFIED);
// 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
public void checkCredential_needToConfirmLockFoSomething() {
public void testCheckCredential_needToConfirmLockFoSomething() {
final int userId = 101;
mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId));
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
// 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
public void checkCredential_needToConfirmLockForNumeric() {
public void testCheckCredential_needToConfirmLockForNumeric() {
final int userId = 102;
mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId));
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_NUMERIC);
// 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
public void checkCredential_needToConfirmLockForAlphabetic() {
public void testCheckCredential_needToConfirmLockForAlphabetic() {
final int userId = 103;
mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId));
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_ALPHABETIC);
// 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
public void checkCredential_generateChallenge() {
public void testCheckCredential_generateChallenge() {
final int userId = 104;
final long gkPwHandle = 1111L;
final CredentialModel credentialModel = newGkPwHandleCredentialModel(userId, gkPwHandle);
mAutoCredentialViewModel.setCredentialModel(credentialModel);
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;
setupGenerateTokenFlow(gkPwHandle, userId, newSensorId, newChallenge);
setupGenerateChallenge(userId, newSensorId, newChallenge);
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
// Run credential check
mAutoCredentialViewModel.onCreate(mLifecycleOwner);
@CredentialAction final int action = mAutoCredentialViewModel.checkCredential();
assertThat(mAutoCredentialViewModel.getActionLiveData().getValue()).isNull();
assertThat(credentialModel.getSensorId()).isEqualTo(newSensorId);
assertThat(credentialModel.getChallenge()).isEqualTo(newChallenge);
assertThat(CredentialModel.isValidToken(credentialModel.getToken())).isTrue();
assertThat(CredentialModel.isValidGkPwHandle(credentialModel.getGkPwHandle())).isFalse();
assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
final Bundle extras = mAutoCredentialViewModel.getCredentialIntentExtra();
assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
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);
}
@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;
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
assertThat(mAutoCredentialViewModel.getUserId()).isEqualTo(userId);
@@ -227,8 +315,8 @@ public class AutoCredentialViewModelTest {
public void testCheckNewCredentialFromActivityResult_invalidChooseLock() {
final int userId = 107;
final long gkPwHandle = 3333L;
mAutoCredentialViewModel.setCredentialModel(
newGkPwHandleCredentialModel(userId, gkPwHandle));
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
final Intent intent = new Intent();
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
@@ -237,15 +325,15 @@ public class AutoCredentialViewModelTest {
new ActivityResult(ChooseLockPattern.RESULT_FINISHED + 1, intent));
assertThat(ret).isFalse();
verifyNothingHappen();
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
}
@Test
public void testCheckNewCredentialFromActivityResult_invalidConfirmLock() {
final int userId = 107;
final long gkPwHandle = 3333L;
mAutoCredentialViewModel.setCredentialModel(
newGkPwHandleCredentialModel(userId, gkPwHandle));
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
final Intent intent = new Intent();
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
@@ -254,62 +342,68 @@ public class AutoCredentialViewModelTest {
new ActivityResult(Activity.RESULT_OK + 1, intent));
assertThat(ret).isFalse();
verifyNothingHappen();
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
}
@Test
public void testCheckNewCredentialFromActivityResult_nullDataChooseLock() {
final int userId = 108;
final long gkPwHandle = 4444L;
mAutoCredentialViewModel.setCredentialModel(
newGkPwHandleCredentialModel(userId, gkPwHandle));
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle)));
// run checkNewCredentialFromActivityResult()
final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(true,
new ActivityResult(ChooseLockPattern.RESULT_FINISHED, null));
assertThat(ret).isFalse();
verifyNothingHappen();
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
}
@Test
public void testCheckNewCredentialFromActivityResult_nullDataConfirmLock() {
final int userId = 109;
mAutoCredentialViewModel.setCredentialModel(newInvalidChallengeCredentialModel(userId));
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
// run checkNewCredentialFromActivityResult()
final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(false,
new ActivityResult(Activity.RESULT_OK, null));
assertThat(ret).isFalse();
verifyNothingHappen();
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
}
@Test
public void testCheckNewCredentialFromActivityResult_validChooseLock() {
final int userId = 108;
final CredentialModel credentialModel = newInvalidChallengeCredentialModel(userId);
mAutoCredentialViewModel.setCredentialModel(credentialModel);
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
final long gkPwHandle = 6666L;
final int newSensorId = 50;
final long newChallenge = 60L;
setupGenerateTokenFlow(gkPwHandle, userId, newSensorId, newChallenge);
final Intent intent = new Intent();
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
setupGenerateChallenge(userId, newSensorId, newChallenge);
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
// Run checkNewCredentialFromActivityResult()
final Intent intent = new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
gkPwHandle);
final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(true,
new ActivityResult(ChooseLockPattern.RESULT_FINISHED, intent));
assertThat(ret).isTrue();
assertThat(mAutoCredentialViewModel.getActionLiveData().getValue()).isNull();
assertThat(credentialModel.getSensorId()).isEqualTo(newSensorId);
assertThat(credentialModel.getChallenge()).isEqualTo(newChallenge);
assertThat(CredentialModel.isValidToken(credentialModel.getToken())).isTrue();
assertThat(CredentialModel.isValidGkPwHandle(credentialModel.getGkPwHandle())).isFalse();
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
final Bundle extras = mAutoCredentialViewModel.getCredentialIntentExtra();
assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
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);
}
@@ -317,28 +411,33 @@ public class AutoCredentialViewModelTest {
@Test
public void testCheckNewCredentialFromActivityResult_validConfirmLock() {
final int userId = 109;
final CredentialModel credentialModel = newInvalidChallengeCredentialModel(userId);
mAutoCredentialViewModel.setCredentialModel(credentialModel);
mAutoCredentialViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
final long gkPwHandle = 5555L;
final int newSensorId = 80;
final long newChallenge = 90L;
setupGenerateTokenFlow(gkPwHandle, userId, newSensorId, newChallenge);
final Intent intent = new Intent();
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
setupGenerateChallenge(userId, newSensorId, newChallenge);
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
// Run checkNewCredentialFromActivityResult()
final Intent intent = new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
gkPwHandle);
final boolean ret = mAutoCredentialViewModel.checkNewCredentialFromActivityResult(false,
new ActivityResult(Activity.RESULT_OK, intent));
assertThat(ret).isTrue();
assertThat(mAutoCredentialViewModel.getActionLiveData().getValue()).isNull();
assertThat(credentialModel.getSensorId()).isEqualTo(newSensorId);
assertThat(credentialModel.getChallenge()).isEqualTo(newChallenge);
assertThat(CredentialModel.isValidToken(credentialModel.getToken())).isTrue();
assertThat(CredentialModel.isValidGkPwHandle(credentialModel.getGkPwHandle())).isFalse();
assertThat(mAutoCredentialViewModel.getGenerateChallengeFailLiveData().getValue()).isNull();
final Bundle extras = mAutoCredentialViewModel.getCredentialIntentExtra();
assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
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);
}
@@ -377,4 +476,12 @@ public class AutoCredentialViewModelTest {
.setGatekeeperHAT(hat)
.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.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -92,6 +93,18 @@ public class FingerprintEnrollIntroViewModelTest {
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
public void testGetEnrollmentRequest() {
final EnrollmentRequest request = newAllFalseRequest(mApplication);