Pass Timeout back to upper biometric preference am: 52a46d0a85

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/22379152

Change-Id: I19bc33b58f8b036f7b52b2820e7154d3e8955379
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Milton Wu
2023-04-13 09:12:13 +00:00
committed by Automerger Merge Worker
6 changed files with 130 additions and 12 deletions

View File

@@ -26,6 +26,9 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.Nullable;
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settingslib.RestrictedLockUtils;
@@ -49,15 +52,23 @@ public class BiometricNavigationUtils {
*
* @param className The class name of Settings screen to launch.
* @param extras Extras to put into the launching {@link Intent}.
* @param launcher Launcher to launch activity if non-quiet mode
* @return true if the Settings screen is launching.
*/
public boolean launchBiometricSettings(Context context, String className, Bundle extras) {
public boolean launchBiometricSettings(Context context, String className, Bundle extras,
@Nullable ActivityResultLauncher<Intent> launcher) {
final Intent quietModeDialogIntent = getQuietModeDialogIntent(context);
if (quietModeDialogIntent != null) {
context.startActivity(quietModeDialogIntent);
return false;
}
context.startActivity(getSettingsPageIntent(className, extras));
final Intent settingsPageIntent = getSettingsPageIntent(className, extras);
if (launcher != null) {
launcher.launch(settingsPageIntent);
} else {
context.startActivity(settingsPageIntent);
}
return true;
}

View File

@@ -17,10 +17,14 @@
package com.android.settings.biometrics;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.android.internal.widget.LockPatternUtils;
@@ -29,6 +33,8 @@ import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import java.lang.ref.WeakReference;
public abstract class BiometricStatusPreferenceController extends BasePreferenceController {
protected final UserManager mUm;
@@ -39,6 +45,8 @@ public abstract class BiometricStatusPreferenceController extends BasePreference
private final BiometricNavigationUtils mBiometricNavigationUtils;
private final ActiveUnlockStatusUtils mActiveUnlockStatusUtils;
@NonNull private WeakReference<ActivityResultLauncher<Intent>> mLauncherWeakReference =
new WeakReference<>(null);
/**
* @return true if the controller should be shown exclusively.
@@ -118,14 +126,31 @@ public abstract class BiometricStatusPreferenceController extends BasePreference
preference.setSummary(getSummaryText());
}
/**
* Set ActivityResultLauncher that will be used later during handlePreferenceTreeClick()
*
* @param preference the preference being compared
* @param launcher the ActivityResultLauncher
* @return {@code true} if matched preference.
*/
public boolean setPreferenceTreeClickLauncher(@NonNull Preference preference,
@Nullable ActivityResultLauncher<Intent> launcher) {
if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
return false;
}
mLauncherWeakReference = new WeakReference<>(launcher);
return true;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
return super.handlePreferenceTreeClick(preference);
}
return mBiometricNavigationUtils.launchBiometricSettings(
preference.getContext(), getSettingsClassName(), preference.getExtras());
return mBiometricNavigationUtils.launchBiometricSettings(preference.getContext(),
getSettingsClassName(), preference.getExtras(), mLauncherWeakReference.get());
}
protected int getUserId() {

View File

@@ -33,6 +33,9 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@@ -42,14 +45,19 @@ 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.BiometricStatusPreferenceController;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.transition.SettingsTransitionHelper;
import java.util.Collection;
import java.util.List;
/**
* Base fragment with the confirming credential functionality for combined biometrics settings.
*/
@@ -78,6 +86,18 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
@Nullable private String mRetryPreferenceKey = null;
@Nullable private Bundle mRetryPreferenceExtra = null;
private final ActivityResultLauncher<Intent> mFaceOrFingerprintPreferenceLauncher =
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
this::onFaceOrFingerprintPreferenceResult);
private void onFaceOrFingerprintPreferenceResult(@Nullable ActivityResult result) {
if (result != null && result.getResultCode() == BiometricEnrollBase.RESULT_TIMEOUT) {
// When "Face Unlock" or "Fingerprint Unlock" is closed due to entering onStop(),
// "Face & Fingerprint Unlock" shall also close itself and back to "Security" page.
finish();
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
@@ -165,7 +185,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
extras.putInt(BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, sensorId);
extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
super.onPreferenceTreeClick(preference);
onFaceOrFingerprintPreferenceTreeClick(preference);
} catch (IllegalStateException e) {
if (retry) {
mRetryPreferenceKey = preference.getKey();
@@ -200,7 +220,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
final Bundle extras = preference.getExtras();
extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
super.onPreferenceTreeClick(preference);
onFaceOrFingerprintPreferenceTreeClick(preference);
} catch (IllegalStateException e) {
if (retry) {
mRetryPreferenceKey = preference.getKey();
@@ -224,6 +244,33 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
return BiometricUtils.requestGatekeeperHat(context, gkPwHandle, userId, challenge);
}
/**
* Handle preference tree click action for "Face Unlock" or "Fingerprint Unlock" with a launcher
* because "Face & Fingerprint Unlock" has to close itself when it gets a specific activity
* error code.
*
* @param preference "Face Unlock" or "Fingerprint Unlock" preference.
*/
private void onFaceOrFingerprintPreferenceTreeClick(@NonNull Preference preference) {
Collection<List<AbstractPreferenceController>> controllers = getPreferenceControllers();
for (List<AbstractPreferenceController> controllerList : controllers) {
for (AbstractPreferenceController controller : controllerList) {
if (controller instanceof BiometricStatusPreferenceController) {
final BiometricStatusPreferenceController biometricController =
(BiometricStatusPreferenceController) controller;
if (biometricController.setPreferenceTreeClickLauncher(preference,
mFaceOrFingerprintPreferenceLauncher)) {
if (biometricController.handlePreferenceTreeClick(preference)) {
writePreferenceClickMetric(preference);
}
biometricController.setPreferenceTreeClickLauncher(preference, null);
return;
}
}
}
}
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
return onRetryPreferenceTreeClick(preference, true)

View File

@@ -325,6 +325,8 @@ public class FaceSettings extends DashboardFragment {
mFaceManager.revokeChallenge(mSensorId, mUserId, mChallenge);
mToken = null;
}
// Let parent "Face & Fingerprint Unlock" can use this error code to close itself.
setResult(RESULT_TIMEOUT);
finish();
}
}

View File

@@ -669,6 +669,7 @@ public class FingerprintSettings extends SubSettings {
public void onStop() {
super.onStop();
if (!getActivity().isChangingConfigurations() && !mLaunchedConfirm && !mIsEnrolling) {
setResult(RESULT_TIMEOUT);
getActivity().finish();
}
}
@@ -874,6 +875,12 @@ public class FingerprintSettings extends SubSettings {
} 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);
if (resultCode == BiometricEnrollBase.RESULT_TIMEOUT) {
// If "Fingerprint Unlock" is closed because of timeout, notify result code
// back because "Face & Fingerprint Unlock" has to close itself for timeout
// case.
setResult(resultCode);
}
finish();
return;
}

View File

@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -33,6 +34,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import androidx.activity.result.ActivityResultLauncher;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -55,6 +57,8 @@ public class BiometricNavigationUtilsTest {
@Mock
private UserManager mUserManager;
@Mock
private ActivityResultLauncher<Intent> mLauncher;
private Context mContext;
private BiometricNavigationUtils mBiometricNavigationUtils;
@@ -72,7 +76,7 @@ public class BiometricNavigationUtilsTest {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
Bundle.EMPTY);
Bundle.EMPTY, null);
assertQuietModeDialogLaunchRequested();
}
@@ -82,7 +86,17 @@ public class BiometricNavigationUtilsTest {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY)).isFalse();
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null)).isFalse();
}
@Test
public void launchBiometricSettings_quietMode_withLauncher_notThroughLauncher() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
Bundle.EMPTY, mLauncher);
verify(mLauncher, never()).launch(any(Intent.class));
}
@Test
@@ -90,7 +104,7 @@ public class BiometricNavigationUtilsTest {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY);
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null);
assertSettingsPageLaunchRequested(false /* shouldContainExtras */);
}
@@ -100,7 +114,7 @@ public class BiometricNavigationUtilsTest {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY)).isTrue();
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null)).isTrue();
}
@Test
@@ -108,17 +122,29 @@ public class BiometricNavigationUtilsTest {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
final Bundle extras = createNotEmptyExtras();
mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME, extras);
mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, extras, null);
assertSettingsPageLaunchRequested(true /* shouldContainExtras */);
}
@Test
public void launchBiometricSettings_noQuietMode_withLauncher_launchesThroughLauncher() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
final Bundle extras = createNotEmptyExtras();
mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, extras, mLauncher);
verify(mLauncher).launch(any(Intent.class));
}
@Test
public void launchBiometricSettings_noQuietMode_withExtras_returnsTrue() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, createNotEmptyExtras())).isTrue();
mContext, SETTINGS_CLASS_NAME, createNotEmptyExtras(), null)).isTrue();
}
@Test