Update confirming credential flow for biometric settings
The combined biometrics settings has been integrating face and
fingerprint authentication together, so the confirming credential flow
has to be invoked before entering biometrics settings page.
Bug: 183449119
Test: manual
Change-Id: I3c0d059241cb10a254868c2e388c4d3b20305b10
diff --git a/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java b/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
index 2a3bfb2..70f0baf 100644
--- a/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
@@ -122,6 +122,9 @@
final String clazz = hasEnrolledBiometrics() ? getSettingsClassName()
: getEnrollClassName();
intent.setClassName(SETTINGS_PACKAGE_NAME, clazz);
+ if (!preference.getExtras().isEmpty()) {
+ intent.putExtras(preference.getExtras());
+ }
intent.putExtra(Intent.EXTRA_USER_ID, userId);
intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true);
context.startActivity(intent);
diff --git a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
new file mode 100644
index 0000000..4260c7c
--- /dev/null
+++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2021 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.combination;
+
+import static android.app.Activity.RESULT_OK;
+
+import static com.android.settings.password.ChooseLockPattern.RESULT_FINISHED;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.biometrics.BiometricEnrollBase;
+import com.android.settings.biometrics.BiometricUtils;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.password.ChooseLockGeneric;
+import com.android.settings.password.ChooseLockSettingsHelper;
+
+/**
+ * Base fragment with the confirming credential functionality for combined biometrics settings.
+ */
+public abstract class BiometricsSettingsBase extends DashboardFragment {
+
+ private static final int CONFIRM_REQUEST = 2001;
+ private static final int CHOOSE_LOCK_REQUEST = 2002;
+
+ private static final String SAVE_STATE_CONFIRM_CREDETIAL = "confirm_credential";
+
+ protected int mUserId;
+ protected long mFaceChallenge;
+ protected long mFingerprintChallenge;
+ protected int mFaceSensorId;
+ protected int mFingerprintSensorId;
+ protected long mGkPwHandle;
+ private boolean mConfirmCredential;
+ @Nullable private FaceManager mFaceManager;
+ @Nullable private FingerprintManager mFingerprintManager;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mUserId = getActivity().getIntent().getIntExtra(Intent.EXTRA_USER_ID,
+ UserHandle.myUserId());
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mFaceManager = Utils.getFaceManagerOrNull(getActivity());
+ mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity());
+
+ if (BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
+ mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(getIntent());
+ }
+
+ if (savedInstanceState != null) {
+ mConfirmCredential = savedInstanceState.getBoolean(SAVE_STATE_CONFIRM_CREDETIAL);
+ if (savedInstanceState.containsKey(
+ ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE)) {
+ mGkPwHandle = savedInstanceState.getLong(
+ ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE);
+ }
+ }
+
+ if (mGkPwHandle == 0L && !mConfirmCredential) {
+ mConfirmCredential = true;
+ launchChooseOrConfirmLock();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (getActivity().isFinishing()) {
+ mFaceManager.revokeChallenge(mFaceSensorId, mUserId, mFaceChallenge);
+ mFingerprintManager.revokeChallenge(mUserId, mFingerprintChallenge);
+ BiometricUtils.removeGatekeeperPasswordHandle(getActivity(), mGkPwHandle);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(Preference preference) {
+ final String key = preference.getKey();
+ if (getFacePreferenceKey().equals(key)) {
+ final byte[] token = BiometricUtils.requestGatekeeperHat(getActivity(), mGkPwHandle,
+ mUserId, mFaceChallenge);
+ final Bundle extras = preference.getExtras();
+ extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
+ extras.putInt(BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, mFaceSensorId);
+ extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, mFaceChallenge);
+ } else if (getFingerprintPreferenceKey().equals(key)) {
+ final byte[] token = BiometricUtils.requestGatekeeperHat(getActivity(), mGkPwHandle,
+ mUserId, mFingerprintChallenge);
+ final Bundle extras = preference.getExtras();
+ extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
+ extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, mFingerprintChallenge);
+ }
+ return super.onPreferenceTreeClick(preference);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVE_STATE_CONFIRM_CREDETIAL, mConfirmCredential);
+ if (mGkPwHandle != 0L) {
+ outState.putLong(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, mGkPwHandle);
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == CONFIRM_REQUEST || requestCode == CHOOSE_LOCK_REQUEST) {
+ mConfirmCredential = false;
+ if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) {
+ if (data != null && BiometricUtils.containsGatekeeperPasswordHandle(data)) {
+ mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data);
+ mFaceManager.generateChallenge((sensorId, challenge) -> {
+ mFaceSensorId = sensorId;
+ mFaceChallenge = challenge;
+ });
+ mFingerprintManager.generateChallenge(mUserId, (sensorId, challenge) -> {
+ mFingerprintSensorId = sensorId;
+ mFingerprintChallenge = challenge;
+ });
+ } else {
+ Log.d(getLogTag(), "Data null or GK PW missing.");
+ finish();
+ }
+ } else {
+ Log.d(getLogTag(), "Password not confirmed.");
+ finish();
+ }
+ }
+ }
+
+ /**
+ * Get the preference key of face for passing through credential data to face settings.
+ */
+ public abstract String getFacePreferenceKey();
+
+ /**
+ * Get the preference key of face for passing through credential data to face settings.
+ */
+ public abstract String getFingerprintPreferenceKey();
+
+ private void launchChooseOrConfirmLock() {
+ final ChooseLockSettingsHelper.Builder builder =
+ new ChooseLockSettingsHelper.Builder(getActivity(), this)
+ .setRequestCode(CONFIRM_REQUEST)
+ .setTitle(getString(R.string.security_settings_biometric_preference_title))
+ .setRequestGatekeeperPasswordHandle(true)
+ .setForegroundOnly(true)
+ .setReturnCredentials(true);
+ if (mUserId != UserHandle.USER_NULL) {
+ builder.setUserId(mUserId);
+ }
+ final boolean launched = builder.show();
+
+ if (!launched) {
+ Intent intent = BiometricUtils.getChooseLockIntent(getActivity(), getIntent());
+ intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
+ DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+ intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
+ intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
+ intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, true);
+
+ if (mUserId != UserHandle.USER_NULL) {
+ intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+ }
+ startActivityForResult(intent, CHOOSE_LOCK_REQUEST);
+ }
+ }
+}
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettings.java b/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettings.java
index f4d1c39..08c934d 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettings.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettings.java
@@ -17,26 +17,21 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.content.Intent;
-import android.os.UserHandle;
import com.android.settings.R;
-import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
/**
* Settings screen for multiple biometrics used in work profile.
*/
-public class CombinedBiometricProfileSettings extends DashboardFragment {
+public class CombinedBiometricProfileSettings extends BiometricsSettingsBase {
private static final String TAG = "BiometricProfileSetting";
-
- private int mUserId;
+ private static final String KEY_FACE_SETTINGS = "biometric_face_settings_profile";
+ private static final String KEY_FINGERPRINT_SETTINGS = "biometric_fingerprint_settings_profile";
@Override
public void onAttach(Context context) {
super.onAttach(context);
- mUserId = getActivity().getIntent().getIntExtra(Intent.EXTRA_USER_ID,
- UserHandle.myUserId());
use(BiometricSettingsAppPreferenceController.class).setUserId(mUserId);
}
@@ -46,6 +41,16 @@
}
@Override
+ public String getFacePreferenceKey() {
+ return KEY_FACE_SETTINGS;
+ }
+
+ @Override
+ public String getFingerprintPreferenceKey() {
+ return KEY_FINGERPRINT_SETTINGS;
+ }
+
+ @Override
protected String getLogTag() {
return TAG;
}
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java b/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
index 843d935..9ebb62f 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
@@ -17,11 +17,8 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.content.Intent;
-import android.os.UserHandle;
import com.android.settings.R;
-import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
@@ -29,16 +26,14 @@
* Settings screen for multiple biometrics.
*/
@SearchIndexable
-public class CombinedBiometricSettings extends DashboardFragment {
+public class CombinedBiometricSettings extends BiometricsSettingsBase {
private static final String TAG = "BiometricSettings";
-
- private int mUserId;
+ private static final String KEY_FACE_SETTINGS = "biometric_face_settings";
+ private static final String KEY_FINGERPRINT_SETTINGS = "biometric_fingerprint_settings";
@Override
public void onAttach(Context context) {
super.onAttach(context);
- mUserId = getActivity().getIntent().getIntExtra(Intent.EXTRA_USER_ID,
- UserHandle.myUserId());
use(BiometricSettingsKeyguardPreferenceController.class).setUserId(mUserId);
use(BiometricSettingsAppPreferenceController.class).setUserId(mUserId);
}
@@ -49,6 +44,16 @@
}
@Override
+ public String getFacePreferenceKey() {
+ return KEY_FACE_SETTINGS;
+ }
+
+ @Override
+ public String getFingerprintPreferenceKey() {
+ return KEY_FINGERPRINT_SETTINGS;
+ }
+
+ @Override
protected String getLogTag() {
return TAG;
}