Merge "Launch settings for clicking fingerprint unlock"

This commit is contained in:
TreeHugger Robot
2023-01-04 05:55:54 +00:00
committed by Android (Google) Code Review
14 changed files with 899 additions and 131 deletions

View File

@@ -31,6 +31,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
@@ -58,6 +59,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
private static final String KEY_CONFIRMING_CREDENTIALS = "confirming_credentials";
private static final String KEY_SCROLLED_TO_BOTTOM = "scrolled";
private GatekeeperPasswordProvider mGatekeeperPasswordProvider;
private UserManager mUserManager;
private boolean mHasPassword;
private boolean mBiometricUnlockDisabledByAdmin;
@@ -178,7 +180,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
mErrorText = getErrorTextView();
mUserManager = UserManager.get(this);
mUserManager = getUserManager();
updatePasswordQuality();
if (!mConfirmingCredentials) {
@@ -253,8 +255,28 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
return super.shouldFinishWhenBackgrounded() && !mConfirmingCredentials && !mNextClicked;
}
@VisibleForTesting
@NonNull
protected GatekeeperPasswordProvider getGatekeeperPasswordProvider() {
if (mGatekeeperPasswordProvider == null) {
mGatekeeperPasswordProvider = new GatekeeperPasswordProvider(getLockPatternUtils());
}
return mGatekeeperPasswordProvider;
}
@VisibleForTesting
protected UserManager getUserManager() {
return UserManager.get(this);
}
@VisibleForTesting
@NonNull
protected LockPatternUtils getLockPatternUtils() {
return new LockPatternUtils(this);
}
private void updatePasswordQuality() {
final int passwordQuality = new LockPatternUtils(this)
final int passwordQuality = getLockPatternUtils()
.getActivePasswordQuality(mUserManager.getCredentialOwnerProfile(mUserId));
mHasPassword = passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
@@ -301,6 +323,14 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
}
/**
* Returns the intent extra data for setResult(), null means nothing need to been sent back
*/
@Nullable
protected Intent getSetResultIntentExtra(@Nullable Intent activityResultIntent) {
return activityResultIntent;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG,
@@ -310,7 +340,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
&& BiometricUtils.isMultiBiometricFingerprintEnrollmentFlow(this);
if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST) {
if (isResultFinished(resultCode)) {
handleBiometricResultSkipOrFinished(resultCode, data);
handleBiometricResultSkipOrFinished(resultCode, getSetResultIntentExtra(data));
} else if (isResultSkipped(resultCode)) {
if (!BiometricUtils.tryStartingNextBiometricEnroll(this,
ENROLL_NEXT_BIOMETRIC_REQUEST, "BIOMETRIC_FIND_SENSOR_SKIPPED")) {

View File

@@ -72,6 +72,8 @@ public class BiometricUtils {
};
/**
* @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
*
* Given the result from confirming or choosing a credential, request Gatekeeper to generate
* a HardwareAuthToken with the Gatekeeper Password together with a biometric challenge.
*
@@ -83,6 +85,7 @@ public class BiometricUtils {
* @throws GatekeeperCredentialNotMatchException if Gatekeeper response is not match
* @throws IllegalStateException if Gatekeeper Password is missing
*/
@Deprecated
public static byte[] requestGatekeeperHat(@NonNull Context context, @NonNull Intent result,
int userId, long challenge) {
if (!containsGatekeeperPasswordHandle(result)) {
@@ -93,6 +96,10 @@ public class BiometricUtils {
return requestGatekeeperHat(context, gatekeeperPasswordHandle, userId, challenge);
}
/**
* @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
*/
@Deprecated
public static byte[] requestGatekeeperHat(@NonNull Context context, long gkPwHandle, int userId,
long challenge) {
final LockPatternUtils utils = new LockPatternUtils(context);
@@ -104,15 +111,25 @@ public class BiometricUtils {
return response.getGatekeeperHAT();
}
/**
* @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
*/
@Deprecated
public static boolean containsGatekeeperPasswordHandle(@Nullable Intent data) {
return data != null && data.hasExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE);
}
/**
* @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
*/
@Deprecated
public static long getGatekeeperPasswordHandle(@NonNull Intent data) {
return data.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
}
/**
* @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
*
* Requests {@link com.android.server.locksettings.LockSettingsService} to remove the
* gatekeeper password associated with a previous
* {@link ChooseLockSettingsHelper.Builder#setRequestGatekeeperPasswordHandle(boolean)}
@@ -120,6 +137,7 @@ public class BiometricUtils {
* @param context Caller's context
* @param data The onActivityResult intent from ChooseLock* or ConfirmLock*
*/
@Deprecated
public static void removeGatekeeperPasswordHandle(@NonNull Context context,
@Nullable Intent data) {
if (data == null) {
@@ -131,6 +149,10 @@ public class BiometricUtils {
removeGatekeeperPasswordHandle(context, getGatekeeperPasswordHandle(data));
}
/**
* @deprecated Use {@link com.android.settings.biometrics.GatekeeperPasswordProvider} instead.
*/
@Deprecated
public static void removeGatekeeperPasswordHandle(@NonNull Context context, long handle) {
final LockPatternUtils utils = new LockPatternUtils(context);
utils.removeGatekeeperPasswordHandle(handle);

View File

@@ -0,0 +1,128 @@
/*
* 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.biometrics;
import static com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.password.ChooseLockSettingsHelper;
/**
* Gatekeeper hat related methods
*/
public class GatekeeperPasswordProvider {
private static final String TAG = "GatekeeperPasswordProvider";
private final LockPatternUtils mLockPatternUtils;
public GatekeeperPasswordProvider(LockPatternUtils lockPatternUtils) {
mLockPatternUtils = lockPatternUtils;
}
/**
* Given the result from confirming or choosing a credential, request Gatekeeper to generate
* a HardwareAuthToken with the Gatekeeper Password together with a biometric challenge.
*
* @param result The onActivityResult intent from ChooseLock* or ConfirmLock*
* @param challenge Unique biometric challenge from FingerprintManager/FaceManager
* @param userId User ID that the credential/biometric operation applies to
* @throws GatekeeperCredentialNotMatchException if Gatekeeper response is not match
* @throws IllegalStateException if Gatekeeper Password is missing
*/
public byte[] requestGatekeeperHat(@NonNull Intent result, long challenge, int userId) {
if (!containsGatekeeperPasswordHandle(result)) {
throw new IllegalStateException("Gatekeeper Password is missing!!");
}
final long gatekeeperPasswordHandle = result.getLongExtra(
ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
return requestGatekeeperHat(gatekeeperPasswordHandle, challenge, userId);
}
/**
* Given the result from confirming or choosing a credential, request Gatekeeper to generate
* a HardwareAuthToken with the Gatekeeper Password together with a biometric challenge.
*
* @param gkPwHandle The Gatekeeper password handle from ChooseLock* or ConfirmLock*
* @param challenge Unique biometric challenge from FingerprintManager/FaceManager
* @param userId User ID that the credential/biometric operation applies to
* @throws GatekeeperCredentialNotMatchException if Gatekeeper response is not match
*/
public byte[] requestGatekeeperHat(long gkPwHandle, long challenge, int userId) {
final VerifyCredentialResponse response = mLockPatternUtils.verifyGatekeeperPasswordHandle(
gkPwHandle, challenge, userId);
if (!response.isMatched()) {
throw new GatekeeperCredentialNotMatchException("Unable to request Gatekeeper HAT");
}
return response.getGatekeeperHAT();
}
/**
* Intent data contains gatekeeper password handle or not
*/
public static boolean containsGatekeeperPasswordHandle(@Nullable Intent data) {
return data != null && data.hasExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE);
}
/**
* Returns the gatekeeper password handle from intent
*/
public static long getGatekeeperPasswordHandle(@NonNull Intent data) {
return data.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
}
/**
* Requests {@link com.android.server.locksettings.LockSettingsService} to remove the
* gatekeeper password associated with a previous
* {@link ChooseLockSettingsHelper.Builder#setRequestGatekeeperPasswordHandle(boolean)}
*
* @param data The onActivityResult intent from ChooseLock* or ConfirmLock*
* @param alsoRemoveItFromIntent set it to true if gkPwHandle needs to be removed from intent
*/
public void removeGatekeeperPasswordHandle(@Nullable Intent data,
boolean alsoRemoveItFromIntent) {
if (data == null) {
return;
}
if (!containsGatekeeperPasswordHandle(data)) {
return;
}
removeGatekeeperPasswordHandle(getGatekeeperPasswordHandle(data));
if (alsoRemoveItFromIntent) {
data.removeExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE);
}
}
/**
* Requests {@link com.android.server.locksettings.LockSettingsService} to remove the
* gatekeeper password associated with a previous
* {@link ChooseLockSettingsHelper.Builder#setRequestGatekeeperPasswordHandle(boolean)}
*
* @param handle The Gatekeeper password handle from ChooseLock* or ConfirmLock*
*/
public void removeGatekeeperPasswordHandle(long handle) {
mLockPatternUtils.removeGatekeeperPasswordHandle(handle);
Log.d(TAG, "Removed handle");
}
}

View File

@@ -43,6 +43,7 @@ import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollIntroduction;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.GatekeeperPasswordProvider;
import com.android.settings.biometrics.MultiBiometricEnrollHelper;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.HelpUtils;
@@ -68,7 +69,7 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
@Override
protected void onCreate(Bundle savedInstanceState) {
mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
mFingerprintManager = getFingerprintManager();
if (mFingerprintManager == null) {
Log.e(TAG, "Null FingerprintManager");
finish();
@@ -127,11 +128,50 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
final ScrollView scrollView = findViewById(R.id.sud_scroll_view);
scrollView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
final Intent intent = getIntent();
if (mFromSettingsSummary
&& GatekeeperPasswordProvider.containsGatekeeperPasswordHandle(intent)) {
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
getNextButton().setEnabled(false);
getChallenge(((sensorId, userId, challenge) -> {
if (isFinishing()) {
// Do nothing if activity is finishing
Log.w(TAG, "activity finished before challenge callback launched.");
return;
}
mSensorId = sensorId;
mChallenge = challenge;
final GatekeeperPasswordProvider provider = getGatekeeperPasswordProvider();
mToken = provider.requestGatekeeperHat(intent, challenge, mUserId);
provider.removeGatekeeperPasswordHandle(intent, true);
getNextButton().setEnabled(true);
}));
}
}
@VisibleForTesting
@Nullable
protected FingerprintManager getFingerprintManager() {
return Utils.getFingerprintManagerOrNull(this);
}
/**
* Returns the intent extra data for setResult(), null means nothing need to been sent back
*/
@Nullable
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
protected Intent getSetResultIntentExtra(@Nullable Intent activityResultIntent) {
Intent intent = super.getSetResultIntentExtra(activityResultIntent);
if (mFromSettingsSummary && mToken != null && mChallenge != -1L) {
if (intent == null) {
intent = new Intent();
}
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
intent.putExtra(EXTRA_KEY_CHALLENGE, mChallenge);
}
return intent;
}
@Override
@@ -295,11 +335,6 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
@Override
protected void getChallenge(GenerateChallengeCallback callback) {
mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
if (mFingerprintManager == null) {
callback.onChallengeGenerated(0, 0, 0L);
return;
}
mFingerprintManager.generateChallenge(mUserId, callback::onChallengeGenerated);
}

View File

@@ -23,6 +23,7 @@ import static android.app.admin.DevicePolicyResources.UNDEFINED;
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE;
import android.app.Activity;
import android.app.Dialog;
@@ -60,11 +61,13 @@ import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.GatekeeperPasswordProvider;
import com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -410,7 +413,7 @@ public class FingerprintSettings extends SubSettings {
launchChooseOrConfirmLock();
} else if (!mHasFirstEnrolled) {
mIsEnrolling = true;
addFirstFingerprint();
addFirstFingerprint(null);
}
}
updateFooterColumns(activity);
@@ -776,7 +779,7 @@ public class FingerprintSettings extends SubSettings {
if (requestCode == CONFIRM_REQUEST || requestCode == CHOOSE_LOCK_GENERIC_REQUEST) {
mLaunchedConfirm = false;
if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) {
if (data != null && BiometricUtils.containsGatekeeperPasswordHandle(data)) {
if (BiometricUtils.containsGatekeeperPasswordHandle(data)) {
if (!mHasFirstEnrolled && !mIsEnrolling) {
final Activity activity = getActivity();
if (activity != null) {
@@ -784,21 +787,34 @@ public class FingerprintSettings extends SubSettings {
activity.overridePendingTransition(R.anim.sud_slide_next_in,
R.anim.sud_slide_next_out);
}
}
// To have smoother animation, change flow to let next visible activity
// to generateChallenge, then pass it back through activity result.
// Token and challenge will be updated later through the activity result
// of AUTO_ADD_FIRST_FINGERPRINT_REQUEST.
mIsEnrolling = true;
addFirstFingerprint(
BiometricUtils.getGatekeeperPasswordHandle(data));
} else {
mFingerprintManager.generateChallenge(mUserId,
(sensorId, userId, challenge) -> {
mToken = BiometricUtils.requestGatekeeperHat(getActivity(),
data,
mUserId, challenge);
mChallenge = challenge;
BiometricUtils.removeGatekeeperPasswordHandle(getActivity(),
data);
updateAddPreference();
if (!mHasFirstEnrolled && !mIsEnrolling) {
mIsEnrolling = true;
addFirstFingerprint();
final Activity activity = getActivity();
if (activity == null || activity.isFinishing()) {
// Stop everything
Log.w(TAG, "activity detach or finishing");
return;
}
final GatekeeperPasswordProvider provider =
new GatekeeperPasswordProvider(
new LockPatternUtils(activity));
mToken = provider.requestGatekeeperHat(data, challenge,
mUserId);
mChallenge = challenge;
provider.removeGatekeeperPasswordHandle(data, false);
updateAddPreference();
});
}
} else {
Log.d(TAG, "Data null or GK PW missing");
finish();
@@ -815,12 +831,29 @@ public class FingerprintSettings extends SubSettings {
activity.finish();
}
} else if (requestCode == AUTO_ADD_FIRST_FINGERPRINT_REQUEST) {
if (resultCode != RESULT_FINISHED || data == null) {
Log.d(TAG, "Add first fingerprint, fail or null data, result:" + resultCode);
finish();
return;
}
mToken = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
if (mToken == null) {
Log.w(TAG, "Add first fingerprint, null token");
finish();
return;
}
mChallenge = data.getLongExtra(EXTRA_KEY_CHALLENGE, -1L);
if (mChallenge == -1L) {
Log.w(TAG, "Add first fingerprint, invalid challenge");
finish();
return;
}
mIsEnrolling = false;
mHasFirstEnrolled = true;
if (resultCode != RESULT_FINISHED) {
Log.d(TAG, "Add first fingerprint fail, result:" + resultCode);
finish();
}
updateAddPreference();
}
}
@@ -892,7 +925,7 @@ public class FingerprintSettings extends SubSettings {
}
}
private void addFirstFingerprint() {
private void addFirstFingerprint(@Nullable Long gkPwHandle) {
Intent intent = new Intent();
intent.setClassName(SETTINGS_PACKAGE_NAME,
FeatureFlagUtils.isEnabled(getActivity(),
@@ -906,7 +939,13 @@ public class FingerprintSettings extends SubSettings {
SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE);
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
if (gkPwHandle != null) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
gkPwHandle.longValue());
} else {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
intent.putExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, mChallenge);
}
startActivityForResult(intent, AUTO_ADD_FIRST_FINGERPRINT_REQUEST);
}

View File

@@ -16,17 +16,13 @@
package com.android.settings.biometrics.fingerprint;
import static android.util.FeatureFlagUtils.SETTINGS_BIOMETRICS2_ENROLLMENT;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.fingerprint.FingerprintManager;
import android.util.FeatureFlagUtils;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.ParentalControlsUtils;
import com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.utils.StringUtil;
@@ -82,11 +78,7 @@ public class FingerprintStatusUtils {
* Returns the class name of the Settings page corresponding to fingerprint settings.
*/
public String getSettingsClassName() {
return !hasEnrolled() && isAvailable()
? (FeatureFlagUtils.isEnabled(mContext, SETTINGS_BIOMETRICS2_ENROLLMENT)
? FingerprintEnrollmentActivity.class.getName()
: FingerprintEnrollIntroductionInternal.class.getName())
: FingerprintSettings.class.getName();
return FingerprintSettings.class.getName();
}
/**

View File

@@ -50,6 +50,7 @@ public final class CredentialModel {
/**
* Default value if GkPwHandle is invalid.
*/
@VisibleForTesting
public static final long INVALID_GK_PW_HANDLE = 0L;
/**
@@ -115,8 +116,8 @@ public final class CredentialModel {
/**
* Check user id is valid or not
*/
public static boolean isValidUserId(int userId) {
return userId != UserHandle.USER_NULL;
public boolean isValidUserId() {
return mUserId != UserHandle.USER_NULL;
}
/**
@@ -134,6 +135,13 @@ public final class CredentialModel {
mChallenge = value;
}
/**
* Check challenge is valid or not
*/
public boolean isValidChallenge() {
return mChallenge != INVALID_CHALLENGE;
}
/**
* Get challenge token
*/
@@ -153,8 +161,8 @@ public final class CredentialModel {
/**
* Check challengeToken is valid or not
*/
public static boolean isValidToken(@Nullable byte[] token) {
return token != null;
public boolean isValidToken() {
return mToken != null;
}
/**
@@ -175,8 +183,8 @@ public final class CredentialModel {
/**
* Check gkPwHandle is valid or not
*/
public static boolean isValidGkPwHandle(long gkPwHandle) {
return gkPwHandle != INVALID_GK_PW_HANDLE;
public boolean isValidGkPwHandle() {
return mGkPwHandle != INVALID_GK_PW_HANDLE;
}
/**
@@ -206,10 +214,10 @@ public final class CredentialModel {
+ ", userId:" + mUserId
+ ", challenge:{len:" + challengeLen
+ ", updateMillis:" + mUpdateChallengeMillis + "}"
+ ", token:{len:" + tokenLen + ", isValid:" + isValidToken(mToken)
+ ", token:{len:" + tokenLen + ", isValid:" + isValidToken()
+ ", updateMillis:" + mUpdateTokenMillis + "}"
+ ", gkPwHandle:{len:" + gkPwHandleLen + ", isValid:"
+ isValidGkPwHandle(mGkPwHandle) + ", clearMillis:" + mClearGkPwHandleMillis + "}"
+ ", gkPwHandle:{len:" + gkPwHandleLen + ", isValid:" + isValidGkPwHandle()
+ ", clearMillis:" + mClearGkPwHandleMillis + "}"
+ ", mSensorId:{id:" + mSensorId + ", updateMillis:" + mUpdateSensorIdMillis + "}"
+ " }";
}

View File

@@ -98,7 +98,6 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
mAutoCredentialViewModel = viewModelProvider.get(AutoCredentialViewModel.class);
mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent());
checkCredential();
// Theme
setTheme(mViewModel.getRequest().getTheme());
@@ -107,12 +106,14 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
// fragment
setContentView(R.layout.biometric_enrollment_container);
final FingerprintEnrollIntroViewModel introViewModel =
viewModelProvider.get(FingerprintEnrollIntroViewModel.class);
introViewModel.setEnrollmentRequest(mViewModel.getRequest());
introViewModel.setUserId(mAutoCredentialViewModel.getUserId());
if (savedInstanceState == null) {
checkCredential();
final String tag = "FingerprintEnrollIntroFragment";
getSupportFragmentManager().beginTransaction()
.setReorderingAllowed(true)
@@ -138,11 +139,34 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
onSetActivityResult(new ActivityResult(RESULT_CANCELED, null));
}
/**
* Get intent which passing back to FingerprintSettings for late generateChallenge()
*/
@Nullable
private Intent createSetResultIntentWithGeneratingChallengeExtra(
@Nullable Intent activityResultIntent) {
if (!mViewModel.getRequest().isFromSettingsSummery()) {
return activityResultIntent;
}
final Bundle extra = mAutoCredentialViewModel.createGeneratingChallengeExtras();
if (extra != null) {
if (activityResultIntent == null) {
activityResultIntent = new Intent();
}
activityResultIntent.putExtras(extra);
}
return activityResultIntent;
}
private void onSetActivityResult(@NonNull ActivityResult result) {
setResult(mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()
final int resultCode = mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()
? RESULT_CANCELED
: result.getResultCode(),
result.getData());
: result.getResultCode();
final Intent intent = resultCode == BiometricEnrollBase.RESULT_FINISHED
? createSetResultIntentWithGeneratingChallengeExtra(result.getData())
: result.getData();
setResult(resultCode, intent);
finish();
}

View File

@@ -65,6 +65,10 @@ public class AutoCredentialViewModel extends AndroidViewModel {
@VisibleForTesting
static final String KEY_CREDENTIAL_MODEL = "credential_model";
@VisibleForTesting
static final String KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL =
"is_generating_challenge_during_checking_credential";
private static final boolean DEBUG = false;
/**
@@ -173,6 +177,9 @@ public class AutoCredentialViewModel extends AndroidViewModel {
@NonNull private final MutableLiveData<Boolean> mGenerateChallengeFailedLiveData =
new MutableLiveData<>();
// flag if token is generating through checkCredential()'s generateChallenge()
private boolean mIsGeneratingChallengeDuringCheckingCredential;
public AutoCredentialViewModel(
@NonNull Application application,
@NonNull LockPatternUtils lockPatternUtils,
@@ -189,10 +196,13 @@ public class AutoCredentialViewModel extends AndroidViewModel {
final Bundle bundle;
if (savedInstanceState != null) {
bundle = savedInstanceState.getBundle(KEY_CREDENTIAL_MODEL);
mIsGeneratingChallengeDuringCheckingCredential = savedInstanceState.getBoolean(
KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL);
} else {
bundle = intent.getExtras();
}
mCredentialModel = new CredentialModel(bundle, SystemClock.elapsedRealtimeClock());
mCredentialModel = new CredentialModel(bundle != null ? bundle : new Bundle(),
SystemClock.elapsedRealtimeClock());
if (DEBUG) {
Log.d(TAG, "setCredentialModel " + mCredentialModel + ", savedInstanceState exist:"
@@ -204,6 +214,8 @@ public class AutoCredentialViewModel extends AndroidViewModel {
* Handle onSaveInstanceState from activity
*/
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL,
mIsGeneratingChallengeDuringCheckingCredential);
outState.putBundle(KEY_CREDENTIAL_MODEL, mCredentialModel.getBundle());
}
@@ -212,6 +224,24 @@ public class AutoCredentialViewModel extends AndroidViewModel {
return mGenerateChallengeFailedLiveData;
}
/**
* Get bundle which passing back to FingerprintSettings for late generateChallenge()
*/
@Nullable
public Bundle createGeneratingChallengeExtras() {
if (!mIsGeneratingChallengeDuringCheckingCredential
|| !mCredentialModel.isValidToken()
|| !mCredentialModel.isValidChallenge()) {
return null;
}
Bundle bundle = new Bundle();
bundle.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
mCredentialModel.getToken());
bundle.putLong(EXTRA_KEY_CHALLENGE, mCredentialModel.getChallenge());
return bundle;
}
/**
* Check credential status for biometric enrollment.
*/
@@ -220,11 +250,11 @@ public class AutoCredentialViewModel extends AndroidViewModel {
if (isValidCredential()) {
return CREDENTIAL_VALID;
}
final long gkPwHandle = mCredentialModel.getGkPwHandle();
if (isUnspecifiedPassword()) {
return CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
} else if (CredentialModel.isValidGkPwHandle(gkPwHandle)) {
generateChallenge(gkPwHandle);
} else if (mCredentialModel.isValidGkPwHandle()) {
generateChallenge(mCredentialModel.getGkPwHandle());
mIsGeneratingChallengeDuringCheckingCredential = true;
return CREDENTIAL_IS_GENERATING_CHALLENGE;
} else {
return CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
@@ -261,8 +291,7 @@ public class AutoCredentialViewModel extends AndroidViewModel {
}
private boolean isValidCredential() {
return !isUnspecifiedPassword()
&& CredentialModel.isValidToken(mCredentialModel.getToken());
return !isUnspecifiedPassword() && mCredentialModel.isValidToken();
}
private boolean isUnspecifiedPassword() {
@@ -316,17 +345,14 @@ public class AutoCredentialViewModel extends AndroidViewModel {
@NonNull
public Bundle createCredentialIntentExtra() {
final Bundle retBundle = new Bundle();
final long gkPwHandle = mCredentialModel.getGkPwHandle();
if (CredentialModel.isValidGkPwHandle(gkPwHandle)) {
retBundle.putLong(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
if (mCredentialModel.isValidGkPwHandle()) {
retBundle.putLong(EXTRA_KEY_GK_PW_HANDLE, mCredentialModel.getGkPwHandle());
}
final byte[] token = mCredentialModel.getToken();
if (CredentialModel.isValidToken(token)) {
retBundle.putByteArray(EXTRA_KEY_CHALLENGE_TOKEN, token);
if (mCredentialModel.isValidToken()) {
retBundle.putByteArray(EXTRA_KEY_CHALLENGE_TOKEN, mCredentialModel.getToken());
}
final int userId = getUserId();
if (CredentialModel.isValidUserId(userId)) {
retBundle.putInt(Intent.EXTRA_USER_ID, userId);
if (mCredentialModel.isValidUserId()) {
retBundle.putInt(Intent.EXTRA_USER_ID, mCredentialModel.getUserId());
}
retBundle.putLong(EXTRA_KEY_CHALLENGE, mCredentialModel.getChallenge());
retBundle.putInt(EXTRA_KEY_SENSOR_ID, mCredentialModel.getSensorId());
@@ -346,9 +372,8 @@ public class AutoCredentialViewModel extends AndroidViewModel {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true);
final int userId = getUserId();
if (CredentialModel.isValidUserId(userId)) {
intent.putExtra(Intent.EXTRA_USER_ID, userId);
if (mCredentialModel.isValidUserId()) {
intent.putExtra(Intent.EXTRA_USER_ID, mCredentialModel.getUserId());
}
return intent;
}
@@ -367,9 +392,8 @@ public class AutoCredentialViewModel extends AndroidViewModel {
.setForegroundOnly(true)
.setReturnCredentials(true);
final int userId = mCredentialModel.getUserId();
if (CredentialModel.isValidUserId(userId)) {
builder.setUserId(userId);
if (mCredentialModel.isValidUserId()) {
builder.setUserId(mCredentialModel.getUserId());
}
return builder.build();
}

View File

@@ -104,7 +104,7 @@ public class FingerprintEnrollmentViewModel extends AndroidViewModel implements
* Handle activity result from FingerprintFindSensor
*/
public void onContinueEnrollActivityResult(@NonNull ActivityResult result, int userId) {
if (mIsWaitingActivityResult.compareAndSet(true, false)) {
if (!mIsWaitingActivityResult.compareAndSet(true, false)) {
Log.w(TAG, "fail to reset isWaiting flag for enrollment");
}
if (result.getResultCode() == RESULT_FINISHED

View File

@@ -16,9 +16,20 @@
package com.android.settings.biometrics.fingerprint;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.content.Intent.EXTRA_USER_ID;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_CHALLENGE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -32,8 +43,15 @@ import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.UserManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.R;
import com.android.settings.biometrics.GatekeeperPasswordProvider;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -42,11 +60,11 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@@ -54,17 +72,25 @@ import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class FingerprintEnrollIntroductionTest {
@Mock private LockPatternUtils mLockPatternUtils;
@Mock private FingerprintManager mFingerprintManager;
@Mock private UserManager mUserManager;
private GatekeeperPasswordProvider mGatekeeperPasswordProvider;
private Context mContext;
private FingerprintEnrollIntroduction mFingerprintEnrollIntroduction;
private TestFingerprintEnrollIntroduction mFingerprintEnrollIntroduction;
private static final int MAX_ENROLLMENTS = 5;
private static final byte[] EXPECTED_TOKEN = new byte[] { 10, 20, 30, 40 };
private static final long EXPECTED_CHALLENGE = 9876L;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mGatekeeperPasswordProvider = new GatekeeperPasswordProvider(mLockPatternUtils);
mContext = spy(RuntimeEnvironment.application.getApplicationContext());
final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
@@ -79,14 +105,34 @@ public class FingerprintEnrollIntroductionTest {
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
props.add(prop);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
when(mUserManager.getCredentialOwnerProfile(anyInt())).thenAnswer(
(Answer<Integer>) invocation -> (int) invocation.getArgument(0));
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(anyLong(), anyLong(), anyInt()))
.thenAnswer((Answer<VerifyCredentialResponse>) invocation ->
newGoodCredential(invocation.getArgument(0), EXPECTED_TOKEN));
doNothing().when(mLockPatternUtils).removeGatekeeperPasswordHandle(anyLong());
}
void setupFingerprintEnrollIntroWith(Intent intent) {
ActivityController<FingerprintEnrollIntroduction> controller =
Robolectric.buildActivity(FingerprintEnrollIntroduction.class, intent);
mFingerprintEnrollIntroduction = spy(controller.get());
ReflectionHelpers.setField(
mFingerprintEnrollIntroduction, "mFingerprintManager", mFingerprintManager);
void setupFingerprintEnrollIntroWith(@NonNull Intent intent) {
final ActivityController<TestFingerprintEnrollIntroduction> controller =
Robolectric.buildActivity(TestFingerprintEnrollIntroduction.class, intent);
mFingerprintEnrollIntroduction = controller.get();
mFingerprintEnrollIntroduction.mMockedFingerprintManager = mFingerprintManager;
mFingerprintEnrollIntroduction.mMockedGatekeeperPasswordProvider =
mGatekeeperPasswordProvider;
mFingerprintEnrollIntroduction.mMockedLockPatternUtils = mLockPatternUtils;
mFingerprintEnrollIntroduction.mMockedUserManager = mUserManager;
mFingerprintEnrollIntroduction.mNewSensorId = 1;
mFingerprintEnrollIntroduction.mNewChallenge = EXPECTED_CHALLENGE;
final int userId = intent.getIntExtra(EXTRA_USER_ID, 0);
when(mLockPatternUtils.getActivePasswordQuality(userId))
.thenReturn(PASSWORD_QUALITY_SOMETHING);
controller.create();
}
@@ -102,7 +148,7 @@ public class FingerprintEnrollIntroductionTest {
@Test
public void intro_CheckCanEnrollNormal() {
setupFingerprintEnrollIntroWith(new Intent());
setupFingerprintEnrollIntroWith(newTokenOnlyIntent());
setFingerprintManagerToHave(3 /* numEnrollments */);
int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
@@ -111,7 +157,7 @@ public class FingerprintEnrollIntroductionTest {
@Test
public void intro_CheckMaxEnrolledNormal() {
setupFingerprintEnrollIntroWith(new Intent());
setupFingerprintEnrollIntroWith(newTokenOnlyIntent());
setFingerprintManagerToHave(7 /* numEnrollments */);
int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
@@ -126,10 +172,7 @@ public class FingerprintEnrollIntroductionTest {
when(resources.getInteger(anyInt())).thenReturn(5);
when(mContext.getResources()).thenReturn(resources);
setupFingerprintEnrollIntroWith(
new Intent()
.putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true)
.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true));
setupFingerprintEnrollIntroWith(newFirstSuwIntent());
setFingerprintManagerToHave(0 /* numEnrollments */);
int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
@@ -144,10 +187,7 @@ public class FingerprintEnrollIntroductionTest {
when(mContext.getResources()).thenReturn(resources);
when(resources.getInteger(anyInt())).thenReturn(1);
setupFingerprintEnrollIntroWith(
new Intent()
.putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true)
.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true));
setupFingerprintEnrollIntroWith(newFirstSuwIntent());
setFingerprintManagerToHave(1 /* numEnrollments */);
int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
@@ -156,8 +196,7 @@ public class FingerprintEnrollIntroductionTest {
@Test
public void intro_CheckCanEnrollDuringDeferred() {
setupFingerprintEnrollIntroWith(
new Intent().putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true));
setupFingerprintEnrollIntroWith(newDeferredSuwIntent());
setFingerprintManagerToHave(2 /* numEnrollments */);
int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
@@ -166,8 +205,7 @@ public class FingerprintEnrollIntroductionTest {
@Test
public void intro_CheckMaxEnrolledDuringDeferred() {
setupFingerprintEnrollIntroWith(
new Intent().putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true));
setupFingerprintEnrollIntroWith(newDeferredSuwIntent());
setFingerprintManagerToHave(6 /* numEnrollments */);
int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
@@ -176,8 +214,7 @@ public class FingerprintEnrollIntroductionTest {
@Test
public void intro_CheckCanEnrollDuringPortal() {
setupFingerprintEnrollIntroWith(
new Intent().putExtra(WizardManagerHelper.EXTRA_IS_PORTAL_SETUP, true));
setupFingerprintEnrollIntroWith(newPortalSuwIntent());
setFingerprintManagerToHave(2 /* numEnrollments */);
int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
@@ -186,11 +223,124 @@ public class FingerprintEnrollIntroductionTest {
@Test
public void intro_CheckMaxEnrolledDuringPortal() {
setupFingerprintEnrollIntroWith(
new Intent().putExtra(WizardManagerHelper.EXTRA_IS_PORTAL_SETUP, true));
setupFingerprintEnrollIntroWith(newPortalSuwIntent());
setFingerprintManagerToHave(6 /* numEnrollments */);
int result = mFingerprintEnrollIntroduction.checkMaxEnrolled();
assertThat(result).isEqualTo(R.string.fingerprint_intro_error_max);
}
@Test
public void intro_CheckGenerateChallenge() {
setupFingerprintEnrollIntroWith(newGkPwHandleAndFromSettingsIntent());
final long challengeField = mFingerprintEnrollIntroduction.getChallengeField();
assertThat(challengeField).isEqualTo(EXPECTED_CHALLENGE);
final byte[] token = mFingerprintEnrollIntroduction.getTokenField();
assertThat(token).isNotNull();
assertThat(token.length).isEqualTo(EXPECTED_TOKEN.length);
for (int i = 0; i < token.length; ++i) {
assertWithMessage("token[" + i + "] is " + token[i] + " not " + EXPECTED_TOKEN[i])
.that(token[i]).isEqualTo(EXPECTED_TOKEN[i]);
}
final Intent resultIntent = mFingerprintEnrollIntroduction.getSetResultIntentExtra(null);
assertThat(resultIntent).isNotNull();
assertThat(resultIntent.getLongExtra(EXTRA_KEY_CHALLENGE, -1L)).isEqualTo(challengeField);
final byte[] token2 = resultIntent.getByteArrayExtra(EXTRA_KEY_CHALLENGE_TOKEN);
assertThat(token2).isNotNull();
assertThat(token2.length).isEqualTo(EXPECTED_TOKEN.length);
for (int i = 0; i < token2.length; ++i) {
assertWithMessage("token2[" + i + "] is " + token2[i] + " not " + EXPECTED_TOKEN[i])
.that(token2[i]).isEqualTo(EXPECTED_TOKEN[i]);
}
}
private Intent newTokenOnlyIntent() {
return new Intent()
.putExtra(EXTRA_KEY_CHALLENGE_TOKEN, new byte[] { 1 });
}
private Intent newFirstSuwIntent() {
return newTokenOnlyIntent()
.putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true)
.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true);
}
private Intent newDeferredSuwIntent() {
return newTokenOnlyIntent()
.putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true);
}
private Intent newPortalSuwIntent() {
return newTokenOnlyIntent()
.putExtra(WizardManagerHelper.EXTRA_IS_PORTAL_SETUP, true);
}
private Intent newGkPwHandleAndFromSettingsIntent() {
return new Intent()
.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true)
.putExtra(EXTRA_KEY_GK_PW_HANDLE, 1L);
}
private VerifyCredentialResponse newGoodCredential(long gkPwHandle, @NonNull byte[] hat) {
return new VerifyCredentialResponse.Builder()
.setGatekeeperPasswordHandle(gkPwHandle)
.setGatekeeperHAT(hat)
.build();
}
public static class TestFingerprintEnrollIntroduction
extends FingerprintEnrollIntroduction {
public FingerprintManager mMockedFingerprintManager;
public GatekeeperPasswordProvider mMockedGatekeeperPasswordProvider;
public LockPatternUtils mMockedLockPatternUtils;
public UserManager mMockedUserManager;
public int mNewSensorId;
public long mNewChallenge;
@Nullable
public byte[] getTokenField() {
return mToken;
}
public long getChallengeField() {
return mChallenge;
}
@Override
protected boolean isDisabledByAdmin() {
return false;
}
@Nullable
@Override
protected FingerprintManager getFingerprintManager() {
return mMockedFingerprintManager;
}
@Override
protected UserManager getUserManager() {
return mMockedUserManager;
}
@NonNull
@Override
protected GatekeeperPasswordProvider getGatekeeperPasswordProvider() {
return mMockedGatekeeperPasswordProvider;
}
@NonNull
@Override
protected LockPatternUtils getLockPatternUtils() {
return mMockedLockPatternUtils;
}
@Override
protected void getChallenge(GenerateChallengeCallback callback) {
callback.onChallengeGenerated(mNewSensorId, mUserId, mNewChallenge);
}
}
}

View File

@@ -0,0 +1,188 @@
/*
* 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.biometrics;
import static com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException;
import static com.android.settings.biometrics.GatekeeperPasswordProvider.containsGatekeeperPasswordHandle;
import static com.android.settings.biometrics.GatekeeperPasswordProvider.getGatekeeperPasswordHandle;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.content.Intent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@RunWith(AndroidJUnit4.class)
public class GatekeeperPasswordProviderTest {
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
@Mock private LockPatternUtils mLockPatternUtils;
private GatekeeperPasswordProvider mGatekeeperPasswordProvider;
@Before
public void setUp() {
mGatekeeperPasswordProvider = new GatekeeperPasswordProvider(mLockPatternUtils);
}
@Test
public void testRequestGatekeeperHatWithHandle_success() {
final long gkPwHandle = 1L;
final long challenge = 2L;
final int userId = 0;
final byte[] expectedToken = new byte[] { 3, 2, 1 };
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, expectedToken));
final byte[] actualToken = mGatekeeperPasswordProvider.requestGatekeeperHat(gkPwHandle,
challenge, userId);
assertThat(actualToken).isNotNull();
assertThat(actualToken.length).isEqualTo(expectedToken.length);
for (int i = 0; i < actualToken.length; ++i) {
assertWithMessage("actualToken[" + i + "] is " + actualToken[i] + " not "
+ expectedToken[i]).that(actualToken[i]).isEqualTo(expectedToken[i]);
}
}
@Test(expected = GatekeeperCredentialNotMatchException.class)
public void testRequestGatekeeperHatWithHandle_GatekeeperCredentialNotMatchException() {
final long gkPwHandle = 10L;
final long challenge = 20L;
final int userId = 300;
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
.thenReturn(newBadCredential(0));
mGatekeeperPasswordProvider.requestGatekeeperHat(gkPwHandle, challenge, userId);
}
@Test
public void testRequestGatekeeperHatWithIntent_success() {
final long gkPwHandle = 11L;
final long challenge = 21L;
final int userId = 145;
final byte[] expectedToken = new byte[] { 4, 5, 6, 7 };
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, expectedToken));
final byte[] actualToken = mGatekeeperPasswordProvider.requestGatekeeperHat(
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle), challenge, userId);
assertThat(actualToken).isNotNull();
assertThat(actualToken.length).isEqualTo(expectedToken.length);
for (int i = 0; i < actualToken.length; ++i) {
assertWithMessage("actualToken[" + i + "] is " + actualToken[i] + " not "
+ expectedToken[i]).that(actualToken[i]).isEqualTo(expectedToken[i]);
}
}
@Test(expected = GatekeeperCredentialNotMatchException.class)
public void testRequestGatekeeperHatWithIntent_GatekeeperCredentialNotMatchException() {
final long gkPwHandle = 12L;
final long challenge = 22L;
final int userId = 0;
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
.thenReturn(newBadCredential(0));
mGatekeeperPasswordProvider.requestGatekeeperHat(
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle), challenge, userId);
}
@Test(expected = IllegalStateException.class)
public void testRequestGatekeeperHatWithIntent_IllegalStateException() {
mGatekeeperPasswordProvider.requestGatekeeperHat(new Intent(), 1L, 0);
}
@Test
public void testContainsGatekeeperPasswordHandle() {
assertThat(containsGatekeeperPasswordHandle(null)).isEqualTo(false);
assertThat(containsGatekeeperPasswordHandle(new Intent())).isEqualTo(false);
assertThat(containsGatekeeperPasswordHandle(
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, 2L))).isEqualTo(true);
}
@Test
public void testGetGatekeeperPasswordHandle() {
assertThat(getGatekeeperPasswordHandle(new Intent())).isEqualTo(0L);
assertThat(getGatekeeperPasswordHandle(
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, 3L))).isEqualTo(3L);
}
@Test
public void testRemoveGatekeeperPasswordHandleAsHandle() {
final long gkPwHandle = 1L;
doNothing().when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
mGatekeeperPasswordProvider.removeGatekeeperPasswordHandle(gkPwHandle);
verify(mLockPatternUtils, only()).removeGatekeeperPasswordHandle(gkPwHandle);
}
@Test
public void testRemoveGatekeeperPasswordHandleAsIntent() {
final long gkPwHandle = 1234L;
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
doNothing().when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
mGatekeeperPasswordProvider.removeGatekeeperPasswordHandle(intent, false);
verify(mLockPatternUtils, only()).removeGatekeeperPasswordHandle(gkPwHandle);
assertThat(intent.getLongExtra(EXTRA_KEY_GK_PW_HANDLE, 0L)).isEqualTo(gkPwHandle);
}
@Test
public void testRemoveGatekeeperPasswordHandleAsIntent_removeKey() {
final long gkPwHandle = 1234L;
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
doNothing().when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
mGatekeeperPasswordProvider.removeGatekeeperPasswordHandle(intent, true);
verify(mLockPatternUtils, only()).removeGatekeeperPasswordHandle(gkPwHandle);
assertThat(intent.hasExtra(EXTRA_KEY_GK_PW_HANDLE)).isEqualTo(false);
}
private VerifyCredentialResponse newGoodCredential(long gkPwHandle, @NonNull byte[] hat) {
return new VerifyCredentialResponse.Builder()
.setGatekeeperPasswordHandle(gkPwHandle)
.setGatekeeperHAT(hat)
.build();
}
private VerifyCredentialResponse newBadCredential(int timeout) {
if (timeout > 0) {
return VerifyCredentialResponse.fromTimeout(timeout);
} else {
return VerifyCredentialResponse.fromError();
}
}
}

View File

@@ -19,6 +19,7 @@ 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.biometrics2.ui.model.CredentialModel.INVALID_GK_PW_HANDLE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
import static com.google.common.truth.Truth.assertThat;
@@ -58,11 +59,13 @@ public class CredentialModelTest {
}
public static Bundle newValidTokenCredentialIntentExtras(int userId) {
return newCredentialModelIntentExtras(userId, 1L, 1, new byte[] { 0 }, 0L);
return newCredentialModelIntentExtras(userId, 1L, 1, new byte[] { 0, 1, 2 },
INVALID_GK_PW_HANDLE);
}
public static Bundle newInvalidChallengeCredentialIntentExtras(int userId) {
return newCredentialModelIntentExtras(userId, INVALID_CHALLENGE, 1, null, 0L);
public static Bundle newOnlySensorValidCredentialIntentExtras(int userId) {
return newCredentialModelIntentExtras(userId, INVALID_CHALLENGE, 1, null,
INVALID_GK_PW_HANDLE);
}
public static Bundle newGkPwHandleCredentialIntentExtras(int userId, long gkPwHandle) {

View File

@@ -26,11 +26,9 @@ import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_SENS
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.isValidGkPwHandle;
import static com.android.settings.biometrics2.ui.model.CredentialModel.isValidToken;
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.newOnlySensorValidCredentialIntentExtras;
import static com.android.settings.biometrics2.ui.model.CredentialModelTest.newValidTokenCredentialIntentExtras;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
@@ -40,10 +38,12 @@ import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewMo
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CredentialAction;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.GenerateChallengeCallback;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.KEY_CREDENTIAL_MODEL;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.when;
@@ -109,7 +109,7 @@ public class AutoCredentialViewModelTest {
mViewModel.setCredentialModel(null, new Intent().putExtras(extras));
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL, extras);
mViewModel.onSaveInstanceState(savedInstance);
viewModel2.setCredentialModel(savedInstance, new Intent());
final Bundle bundle1 = mViewModel.createCredentialIntentExtra();
@@ -144,7 +144,7 @@ public class AutoCredentialViewModelTest {
mViewModel.setCredentialModel(null, new Intent().putExtras(extras));
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL, extras);
mViewModel.onSaveInstanceState(savedInstance);
viewModel2.setCredentialModel(savedInstance, new Intent());
final Bundle bundle1 = mViewModel.createCredentialIntentExtra();
@@ -170,68 +170,118 @@ public class AutoCredentialViewModelTest {
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_VALID);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isFalse();
}
@Test
public void testCheckCredential_needToChooseLock() {
final int userId = 100;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_UNSPECIFIED);
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isFalse();
}
@Test
public void testCheckCredential_needToConfirmLockFoSomething() {
public void testCheckCredential_needToConfirmLockForSomething() {
final int userId = 101;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isFalse();
}
@Test
public void testCheckCredential_needToConfirmLockForNumeric() {
final int userId = 102;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_NUMERIC);
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isFalse();
}
@Test
public void testCheckCredential_needToConfirmLockForAlphabetic() {
final int userId = 103;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_ALPHABETIC);
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isFalse();
}
@Test
@@ -252,14 +302,33 @@ public class AutoCredentialViewModelTest {
// Run credential check
@CredentialAction final int action = mViewModel.checkCredential();
// Check viewModel behavior
assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
// Check data inside CredentialModel
final Bundle extras = mViewModel.createCredentialIntentExtra();
assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
assertThat(isValidToken(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN))).isTrue();
assertThat(isValidGkPwHandle(extras.getLong(EXTRA_KEY_GK_PW_HANDLE))).isFalse();
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
assertThat(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)).isNotNull();
assertThat(extras.getLong(EXTRA_KEY_GK_PW_HANDLE)).isEqualTo(INVALID_GK_PW_HANDLE);
assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isNotEqualTo(INVALID_CHALLENGE);
// Check createGeneratingChallengeExtras()
final Bundle generatingChallengeExtras = mViewModel.createGeneratingChallengeExtras();
assertThat(generatingChallengeExtras).isNotNull();
assertThat(generatingChallengeExtras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
final byte[] tokens = generatingChallengeExtras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
assertThat(tokens).isNotNull();
assertThat(tokens.length).isEqualTo(1);
assertThat(tokens[0]).isEqualTo(1);
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isTrue();
}
@Test
@@ -283,13 +352,22 @@ public class AutoCredentialViewModelTest {
assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isTrue();
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
// Check onSaveInstanceState()
final Bundle actualBundle = new Bundle();
mViewModel.onSaveInstanceState(actualBundle);
assertThat(actualBundle.getBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL))
.isTrue();
}
@Test
public void testGetUserId_fromIntent() {
final int userId = 106;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
// Get userId
assertThat(mViewModel.getUserId()).isEqualTo(userId);
@@ -300,13 +378,61 @@ public class AutoCredentialViewModelTest {
final int userId = 106;
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL,
newInvalidChallengeCredentialIntentExtras(userId));
newOnlySensorValidCredentialIntentExtras(userId));
mViewModel.setCredentialModel(savedInstance, new Intent());
// Get userId
assertThat(mViewModel.getUserId()).isEqualTo(userId);
}
@Test
public void testCreateGeneratingChallengeExtras_generateChallenge() {
final Bundle credentialExtras = newValidTokenCredentialIntentExtras(200);
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL, credentialExtras);
savedInstance.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL, true);
mViewModel.setCredentialModel(savedInstance, new Intent());
// Check createGeneratingChallengeExtras()
final Bundle actualExtras = mViewModel.createGeneratingChallengeExtras();
assertThat(actualExtras).isNotNull();
assertThat(actualExtras.getLong(EXTRA_KEY_CHALLENGE))
.isEqualTo(credentialExtras.getLong(EXTRA_KEY_CHALLENGE));
final byte[] actualToken = actualExtras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
final byte[] expectedToken = credentialExtras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN);
assertThat(actualToken).isNotNull();
assertThat(expectedToken).isNotNull();
assertThat(actualToken.length).isEqualTo(expectedToken.length);
for (int i = 0; i < actualToken.length; ++i) {
assertWithMessage("tokens[" + i + "] not match").that(actualToken[i])
.isEqualTo(expectedToken[i]);
}
}
@Test
public void testCreateGeneratingChallengeExtras_notGenerateChallenge() {
final Bundle credentialExtras = newValidTokenCredentialIntentExtras(201);
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL, credentialExtras);
savedInstance.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL, false);
mViewModel.setCredentialModel(savedInstance, new Intent());
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
}
@Test
public void testCreateGeneratingChallengeExtras_invalidToken() {
final Bundle credentialExtras = newOnlySensorValidCredentialIntentExtras(202);
final Bundle savedInstance = new Bundle();
savedInstance.putBundle(KEY_CREDENTIAL_MODEL, credentialExtras);
savedInstance.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL, true);
mViewModel.setCredentialModel(savedInstance, new Intent());
// Check createGeneratingChallengeExtras()
assertThat(mViewModel.createGeneratingChallengeExtras()).isNull();
}
@Test
public void testCheckNewCredentialFromActivityResult_invalidChooseLock() {
final int userId = 107;
@@ -360,7 +486,7 @@ public class AutoCredentialViewModelTest {
public void testCheckNewCredentialFromActivityResult_nullDataConfirmLock() {
final int userId = 109;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
// run checkNewCredentialFromActivityResult()
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
@@ -374,7 +500,7 @@ public class AutoCredentialViewModelTest {
public void testCheckNewCredentialFromActivityResult_validChooseLock() {
final int userId = 108;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
@@ -395,17 +521,16 @@ public class AutoCredentialViewModelTest {
final Bundle extras = mViewModel.createCredentialIntentExtra();
assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
assertThat(isValidToken(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN))).isTrue();
assertThat(isValidGkPwHandle(extras.getLong(EXTRA_KEY_GK_PW_HANDLE))).isFalse();
assertThat(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)).isNotNull();
assertThat(extras.getLong(EXTRA_KEY_GK_PW_HANDLE)).isEqualTo(INVALID_GK_PW_HANDLE);
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
}
@Test
public void testCheckNewCredentialFromActivityResult_validConfirmLock() {
final int userId = 109;
mViewModel.setCredentialModel(null,
new Intent().putExtras(newInvalidChallengeCredentialIntentExtras(userId)));
new Intent().putExtras(newOnlySensorValidCredentialIntentExtras(userId)));
when(mLockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
PASSWORD_QUALITY_SOMETHING);
@@ -426,8 +551,8 @@ public class AutoCredentialViewModelTest {
final Bundle extras = mViewModel.createCredentialIntentExtra();
assertThat(extras.getInt(EXTRA_KEY_SENSOR_ID)).isEqualTo(newSensorId);
assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isEqualTo(newChallenge);
assertThat(isValidToken(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN))).isTrue();
assertThat(isValidGkPwHandle(extras.getLong(EXTRA_KEY_GK_PW_HANDLE))).isFalse();
assertThat(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)).isNotNull();
assertThat(extras.getLong(EXTRA_KEY_GK_PW_HANDLE)).isEqualTo(INVALID_GK_PW_HANDLE);
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
}