Loading src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java +9 −0 Original line number Diff line number Diff line Loading @@ -23,10 +23,14 @@ import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.settings.biometrics.fingerprint.feature.ChallengeGeneratedInvoker; import com.android.settings.biometrics.fingerprint.feature.FingerprintExtPreferencesProvider; import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature; import com.android.settings.biometrics.fingerprint.feature.SfpsRestToUnlockFeature; import java.util.Collections; import java.util.List; public interface FingerprintFeatureProvider { /** * Gets the feature implementation of SFPS enrollment. Loading Loading @@ -80,4 +84,9 @@ public interface FingerprintFeatureProvider { default FingerprintSettingsFeatureProvider getFingerprintSettingsFeatureProvider() { return FingerprintSettingsFeatureProvider.getInstance(); } @NonNull default List<ChallengeGeneratedInvoker> getChallengeGeneratedInvokers() { return Collections.emptyList(); } } src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +33 −0 Original line number Diff line number Diff line Loading @@ -86,6 +86,7 @@ import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.BiometricUtils; import com.android.settings.biometrics.GatekeeperPasswordProvider; import com.android.settings.biometrics.IdentityCheckBiometricErrorDialog; import com.android.settings.biometrics.fingerprint.feature.ChallengeGeneratedInvoker; import com.android.settings.biometrics.fingerprint.feature.FingerprintExtPreferencesProvider; import com.android.settings.biometrics.fingerprint.feature.PrimarySwitchIntentPreference; import com.android.settings.core.SettingsBaseActivity; Loading Loading @@ -267,6 +268,7 @@ public class FingerprintSettings extends SubSettings { private static final String KEY_HAS_FIRST_ENROLLED = "has_first_enrolled"; private static final String KEY_IS_ENROLLING = "is_enrolled"; private static final String KEY_IS_LAUNCHING_EXT_PREF = "is_launching_ext_pref"; private static final String KEY_HAS_RUN_CHALLENGE_INVOKER = "has_run_challenge_invoker"; @VisibleForTesting static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH = "security_settings_require_screen_on_to_auth"; Loading Loading @@ -334,6 +336,8 @@ public class FingerprintSettings extends SubSettings { @NonNull private String mLaunchedExtPrefKey = ""; /** key list for changing visibility */ @NonNull private final ArrayList<String> mExtPrefKeys = new ArrayList<>(); /** Use to make sure ChallengeGeneratedInvokers have been run */ private boolean mHasRunChallengeInvoker = false; private long mChallenge; Loading Loading @@ -595,6 +599,8 @@ public class FingerprintSettings extends SubSettings { mIsEnrolling = savedInstanceState.getBoolean(KEY_IS_ENROLLING, mIsEnrolling); mLaunchedExtPrefKey = savedInstanceState.getString( KEY_IS_LAUNCHING_EXT_PREF, mLaunchedExtPrefKey); mHasRunChallengeInvoker = savedInstanceState.getBoolean( KEY_HAS_RUN_CHALLENGE_INVOKER, /* defaultValue= */ false); mHasFirstEnrolled = savedInstanceState.getBoolean(KEY_HAS_FIRST_ENROLLED, mHasFirstEnrolled); mBiometricsAuthenticationRequested = savedInstanceState.getBoolean( Loading Loading @@ -1113,6 +1119,7 @@ public class FingerprintSettings extends SubSettings { outState.putBoolean(KEY_IS_ENROLLING, mIsEnrolling); outState.putString(KEY_IS_LAUNCHING_EXT_PREF, mLaunchedExtPrefKey); outState.putBoolean(KEY_HAS_FIRST_ENROLLED, mHasFirstEnrolled); outState.putBoolean(KEY_HAS_RUN_CHALLENGE_INVOKER, mHasRunChallengeInvoker); outState.putBoolean(KEY_BIOMETRICS_AUTHENTICATION_REQUESTED, mBiometricsAuthenticationRequested); } Loading Loading @@ -1265,6 +1272,7 @@ public class FingerprintSettings extends SubSettings { if (requestCode == CONFIRM_REQUEST || requestCode == CHOOSE_LOCK_GENERIC_REQUEST) { mLaunchedConfirm = false; if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) { runChallengeGeneratedInvokers(); if (BiometricUtils.containsGatekeeperPasswordHandle(data)) { if (!mHasFirstEnrolled && !mIsEnrolling) { final Activity activity = getActivity(); Loading Loading @@ -1399,6 +1407,31 @@ public class FingerprintSettings extends SubSettings { } } private void runChallengeGeneratedInvokers() { if (mHasRunChallengeInvoker) { return; } mHasRunChallengeInvoker = true; List<ChallengeGeneratedInvoker> invokers = FeatureFactory.getFeatureFactory() .getFingerprintFeatureProvider().getChallengeGeneratedInvokers(); Log.d(TAG, "Num of ChallengeGeneratedInvoker: " + invokers.size()); for (ChallengeGeneratedInvoker invoker: invokers) { Bundle bundle = getIntent().getBundleExtra(invoker.getIntentKeyForBundle()); if (bundle == null) { continue; } long startTime = System.currentTimeMillis(); boolean result = invoker.invoke(this, bundle); Log.d(TAG, "Invoker for " + invoker.getIntentKeyForBundle() + " run " + (System.currentTimeMillis() - startTime) + "ms, result: " + result); // We shall only have at most one invoker for each launching return; } } @Override public void onDestroy() { super.onDestroy(); Loading src/com/android/settings/biometrics/fingerprint/feature/ChallengeGeneratedInvoker.kt 0 → 100644 +27 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.fingerprint.feature import android.os.Bundle import androidx.preference.PreferenceFragmentCompat interface ChallengeGeneratedInvoker { val intentKeyForBundle: String fun invoke(fragment: PreferenceFragmentCompat, bundle: Bundle): Boolean } Loading
src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java +9 −0 Original line number Diff line number Diff line Loading @@ -23,10 +23,14 @@ import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.settings.biometrics.fingerprint.feature.ChallengeGeneratedInvoker; import com.android.settings.biometrics.fingerprint.feature.FingerprintExtPreferencesProvider; import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature; import com.android.settings.biometrics.fingerprint.feature.SfpsRestToUnlockFeature; import java.util.Collections; import java.util.List; public interface FingerprintFeatureProvider { /** * Gets the feature implementation of SFPS enrollment. Loading Loading @@ -80,4 +84,9 @@ public interface FingerprintFeatureProvider { default FingerprintSettingsFeatureProvider getFingerprintSettingsFeatureProvider() { return FingerprintSettingsFeatureProvider.getInstance(); } @NonNull default List<ChallengeGeneratedInvoker> getChallengeGeneratedInvokers() { return Collections.emptyList(); } }
src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +33 −0 Original line number Diff line number Diff line Loading @@ -86,6 +86,7 @@ import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.BiometricUtils; import com.android.settings.biometrics.GatekeeperPasswordProvider; import com.android.settings.biometrics.IdentityCheckBiometricErrorDialog; import com.android.settings.biometrics.fingerprint.feature.ChallengeGeneratedInvoker; import com.android.settings.biometrics.fingerprint.feature.FingerprintExtPreferencesProvider; import com.android.settings.biometrics.fingerprint.feature.PrimarySwitchIntentPreference; import com.android.settings.core.SettingsBaseActivity; Loading Loading @@ -267,6 +268,7 @@ public class FingerprintSettings extends SubSettings { private static final String KEY_HAS_FIRST_ENROLLED = "has_first_enrolled"; private static final String KEY_IS_ENROLLING = "is_enrolled"; private static final String KEY_IS_LAUNCHING_EXT_PREF = "is_launching_ext_pref"; private static final String KEY_HAS_RUN_CHALLENGE_INVOKER = "has_run_challenge_invoker"; @VisibleForTesting static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH = "security_settings_require_screen_on_to_auth"; Loading Loading @@ -334,6 +336,8 @@ public class FingerprintSettings extends SubSettings { @NonNull private String mLaunchedExtPrefKey = ""; /** key list for changing visibility */ @NonNull private final ArrayList<String> mExtPrefKeys = new ArrayList<>(); /** Use to make sure ChallengeGeneratedInvokers have been run */ private boolean mHasRunChallengeInvoker = false; private long mChallenge; Loading Loading @@ -595,6 +599,8 @@ public class FingerprintSettings extends SubSettings { mIsEnrolling = savedInstanceState.getBoolean(KEY_IS_ENROLLING, mIsEnrolling); mLaunchedExtPrefKey = savedInstanceState.getString( KEY_IS_LAUNCHING_EXT_PREF, mLaunchedExtPrefKey); mHasRunChallengeInvoker = savedInstanceState.getBoolean( KEY_HAS_RUN_CHALLENGE_INVOKER, /* defaultValue= */ false); mHasFirstEnrolled = savedInstanceState.getBoolean(KEY_HAS_FIRST_ENROLLED, mHasFirstEnrolled); mBiometricsAuthenticationRequested = savedInstanceState.getBoolean( Loading Loading @@ -1113,6 +1119,7 @@ public class FingerprintSettings extends SubSettings { outState.putBoolean(KEY_IS_ENROLLING, mIsEnrolling); outState.putString(KEY_IS_LAUNCHING_EXT_PREF, mLaunchedExtPrefKey); outState.putBoolean(KEY_HAS_FIRST_ENROLLED, mHasFirstEnrolled); outState.putBoolean(KEY_HAS_RUN_CHALLENGE_INVOKER, mHasRunChallengeInvoker); outState.putBoolean(KEY_BIOMETRICS_AUTHENTICATION_REQUESTED, mBiometricsAuthenticationRequested); } Loading Loading @@ -1265,6 +1272,7 @@ public class FingerprintSettings extends SubSettings { if (requestCode == CONFIRM_REQUEST || requestCode == CHOOSE_LOCK_GENERIC_REQUEST) { mLaunchedConfirm = false; if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) { runChallengeGeneratedInvokers(); if (BiometricUtils.containsGatekeeperPasswordHandle(data)) { if (!mHasFirstEnrolled && !mIsEnrolling) { final Activity activity = getActivity(); Loading Loading @@ -1399,6 +1407,31 @@ public class FingerprintSettings extends SubSettings { } } private void runChallengeGeneratedInvokers() { if (mHasRunChallengeInvoker) { return; } mHasRunChallengeInvoker = true; List<ChallengeGeneratedInvoker> invokers = FeatureFactory.getFeatureFactory() .getFingerprintFeatureProvider().getChallengeGeneratedInvokers(); Log.d(TAG, "Num of ChallengeGeneratedInvoker: " + invokers.size()); for (ChallengeGeneratedInvoker invoker: invokers) { Bundle bundle = getIntent().getBundleExtra(invoker.getIntentKeyForBundle()); if (bundle == null) { continue; } long startTime = System.currentTimeMillis(); boolean result = invoker.invoke(this, bundle); Log.d(TAG, "Invoker for " + invoker.getIntentKeyForBundle() + " run " + (System.currentTimeMillis() - startTime) + "ms, result: " + result); // We shall only have at most one invoker for each launching return; } } @Override public void onDestroy() { super.onDestroy(); Loading
src/com/android/settings/biometrics/fingerprint/feature/ChallengeGeneratedInvoker.kt 0 → 100644 +27 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.fingerprint.feature import android.os.Bundle import androidx.preference.PreferenceFragmentCompat interface ChallengeGeneratedInvoker { val intentKeyForBundle: String fun invoke(fragment: PreferenceFragmentCompat, bundle: Bundle): Boolean }