Merge "Launch settings for clicking fingerprint unlock"
This commit is contained in:
committed by
Android (Google) Code Review
commit
7dfe918785
@@ -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;
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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