Launch settings for clicking fingerprint unlock
Launch FingerprintSettings directly when user clicks "Fingerprint Unlock". Let Fingerprint settings peform confirmLock() or chooseLock(). And to have smoothly animation, instead of generating challenge in FingerprintSettings::onActivityResult(), challenge is generated in next visible activity, and pass it back through next activity result. Bug: 197717071 Test: atest GatekeeperPasswordProviderTest CredentialModelTest Test: atest AutoCredentialViewModelTest FingerprintStatusUtilsTest Test: RunSettingsRoboTests2 FingerprintEnrollIntroductionTest Test: Manually test fingerprint enroll in settings or suw Change-Id: Ie27c3c493ea475f6b53cb6bb3f2d45d555f47cb3
This commit is contained in:
@@ -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")) {
|
||||
|
@@ -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);
|
||||
|
@@ -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");
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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 + "}"
|
||||
+ " }";
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user