Retry fingerprint or face unlock
When IllegalStateException happens during user enters `Fingerprint
Unlock` or `Face Unlock` page through `Face and Fingerprint Unlock`
page, show ConfirmLock for user to re-enter credential again.
If user fails to pass credential page, finish activity and back to
`Security` page.
If user success to pass credential page, enter `Fingerprint Unlock` or
`Face Unlock` page.
Test: Manully test timeout case on `Face and Fingerprint Unlock` page
Test: robotest for CombinedBiometricProfileSettingsTest
FaceSettingsLockscreenBypassPreferenceControllerTest
Bug: 248165760
Change-Id: I3361c38d09d14461db8ecf2d89a34ba9604dc7e8
diff --git a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
index 404fe6d..4da42d4 100644
--- a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
+++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
@@ -28,11 +28,13 @@
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
+import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import com.android.settings.R;
@@ -50,11 +52,16 @@
*/
public abstract class BiometricsSettingsBase extends DashboardFragment {
- private static final int CONFIRM_REQUEST = 2001;
+ @VisibleForTesting
+ static final int CONFIRM_REQUEST = 2001;
private static final int CHOOSE_LOCK_REQUEST = 2002;
private static final String SAVE_STATE_CONFIRM_CREDETIAL = "confirm_credential";
private static final String DO_NOT_FINISH_ACTIVITY = "do_not_finish_activity";
+ @VisibleForTesting
+ static final String RETRY_PREFERENCE_KEY = "retry_preference_key";
+ @VisibleForTesting
+ static final String RETRY_PREFERENCE_BUNDLE = "retry_preference_bundle";
protected int mUserId;
protected long mGkPwHandle;
@@ -63,6 +70,8 @@
@Nullable private FingerprintManager mFingerprintManager;
// Do not finish() if choosing/confirming credential, or showing fp/face settings
private boolean mDoNotFinishActivity;
+ @Nullable private String mRetryPreferenceKey = null;
+ @Nullable private Bundle mRetryPreferenceExtra = null;
@Override
public void onAttach(Context context) {
@@ -84,6 +93,8 @@
if (savedInstanceState != null) {
mConfirmCredential = savedInstanceState.getBoolean(SAVE_STATE_CONFIRM_CREDETIAL);
mDoNotFinishActivity = savedInstanceState.getBoolean(DO_NOT_FINISH_ACTIVITY);
+ mRetryPreferenceKey = savedInstanceState.getString(RETRY_PREFERENCE_KEY);
+ mRetryPreferenceExtra = savedInstanceState.getBundle(RETRY_PREFERENCE_BUNDLE);
if (savedInstanceState.containsKey(
ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE)) {
mGkPwHandle = savedInstanceState.getLong(
@@ -124,8 +135,7 @@
}
}
- @Override
- public boolean onPreferenceTreeClick(Preference preference) {
+ private boolean onRetryPreferenceTreeClick(Preference preference, final boolean retry) {
final String key = preference.getKey();
final Context context = requireActivity().getApplicationContext();
@@ -134,31 +144,77 @@
if (getFacePreferenceKey().equals(key)) {
mDoNotFinishActivity = true;
mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
- final byte[] token = BiometricUtils.requestGatekeeperHat(context, mGkPwHandle,
- mUserId, challenge);
- final Bundle extras = preference.getExtras();
- 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);
+ try {
+ final byte[] token = requestGatekeeperHat(context, mGkPwHandle, mUserId,
+ challenge);
+ final Bundle extras = preference.getExtras();
+ 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);
+ } catch (IllegalStateException e) {
+ if (retry) {
+ mRetryPreferenceKey = preference.getKey();
+ mRetryPreferenceExtra = preference.getExtras();
+ mConfirmCredential = true;
+ launchChooseOrConfirmLock();
+ } else {
+ Log.e(getLogTag(), "face generateChallenge fail", e);
+ mDoNotFinishActivity = false;
+ }
+ }
});
-
return true;
} else if (getFingerprintPreferenceKey().equals(key)) {
mDoNotFinishActivity = true;
mFingerprintManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
- final byte[] token = BiometricUtils.requestGatekeeperHat(context, mGkPwHandle,
- mUserId, challenge);
- final Bundle extras = preference.getExtras();
- extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
- extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
- super.onPreferenceTreeClick(preference);
+ try {
+ final byte[] token = requestGatekeeperHat(context, mGkPwHandle, mUserId,
+ challenge);
+ final Bundle extras = preference.getExtras();
+ extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
+ extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
+ super.onPreferenceTreeClick(preference);
+ } catch (IllegalStateException e) {
+ if (retry) {
+ mRetryPreferenceKey = preference.getKey();
+ mRetryPreferenceExtra = preference.getExtras();
+ mConfirmCredential = true;
+ launchChooseOrConfirmLock();
+ } else {
+ Log.e(getLogTag(), "fingerprint generateChallenge fail", e);
+ mDoNotFinishActivity = false;
+ }
+ }
});
-
return true;
}
+ return false;
+ }
- return super.onPreferenceTreeClick(preference);
+ @VisibleForTesting
+ protected byte[] requestGatekeeperHat(@NonNull Context context, long gkPwHandle, int userId,
+ long challenge) {
+ return BiometricUtils.requestGatekeeperHat(context, gkPwHandle, userId, challenge);
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(Preference preference) {
+ return onRetryPreferenceTreeClick(preference, true)
+ || super.onPreferenceTreeClick(preference);
+ }
+
+ private void retryPreferenceKey(@NonNull String key, @Nullable Bundle extras) {
+ final Preference preference = findPreference(key);
+ if (preference == null) {
+ Log.w(getLogTag(), ".retryPreferenceKey, fail to find " + key);
+ return;
+ }
+
+ if (extras != null) {
+ preference.getExtras().putAll(extras);
+ }
+ onRetryPreferenceTreeClick(preference, false);
}
@Override
@@ -169,6 +225,10 @@
if (mGkPwHandle != 0L) {
outState.putLong(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, mGkPwHandle);
}
+ if (!TextUtils.isEmpty(mRetryPreferenceKey)) {
+ outState.putString(RETRY_PREFERENCE_KEY, mRetryPreferenceKey);
+ outState.putBundle(RETRY_PREFERENCE_BUNDLE, mRetryPreferenceExtra);
+ }
}
@Override
@@ -180,6 +240,11 @@
if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) {
if (BiometricUtils.containsGatekeeperPasswordHandle(data)) {
mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data);
+ if (!TextUtils.isEmpty(mRetryPreferenceKey)) {
+ getActivity().overridePendingTransition(R.anim.sud_slide_next_in,
+ R.anim.sud_slide_next_out);
+ retryPreferenceKey(mRetryPreferenceKey, mRetryPreferenceExtra);
+ }
} else {
Log.d(getLogTag(), "Data null or GK PW missing.");
finish();
@@ -188,6 +253,8 @@
Log.d(getLogTag(), "Password not confirmed.");
finish();
}
+ mRetryPreferenceKey = null;
+ mRetryPreferenceExtra = null;
}
}
@@ -211,7 +278,8 @@
*/
public abstract String getUseInAppsPreferenceKey();
- private void launchChooseOrConfirmLock() {
+ @VisibleForTesting
+ protected void launchChooseOrConfirmLock() {
final ChooseLockSettingsHelper.Builder builder =
new ChooseLockSettingsHelper.Builder(getActivity(), this)
.setRequestCode(CONFIRM_REQUEST)
diff --git a/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java b/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java
new file mode 100644
index 0000000..ac8008d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java
@@ -0,0 +1,358 @@
+/*
+ * 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.combination;
+
+import static com.android.settings.biometrics.combination.BiometricsSettingsBase.CONFIRM_REQUEST;
+import static com.android.settings.password.ChooseLockPattern.RESULT_FINISHED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.XmlRes;
+import androidx.fragment.app.FragmentActivity;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.password.ChooseLockSettingsHelper;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.ShadowFragment;
+import com.android.settings.testutils.shadow.ShadowSettingsPreferenceFragment;
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowSettingsPreferenceFragment.class, ShadowUtils.class, ShadowFragment.class})
+public class CombinedBiometricProfileSettingsTest {
+
+ private TestCombinedBiometricProfileSettings mFragment;
+ private Context mContext;
+
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Captor
+ private ArgumentCaptor<Preference> mPreferenceCaptor;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private BiometricSettingsAppPreferenceController mBiometricSettingsAppPreferenceController;
+ @Mock
+ private FaceManager mFaceManager;
+
+ @Before
+ public void setUp() {
+ ShadowUtils.setFingerprintManager(mFingerprintManager);
+ ShadowUtils.setFaceManager(mFaceManager);
+ FakeFeatureFactory.setupForTest();
+
+ FragmentActivity activity = Robolectric.buildActivity(FragmentActivity.class,
+ new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L)).get();
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ mFragment = spy(new TestCombinedBiometricProfileSettings(mContext));
+ doReturn(activity).when(mFragment).getActivity();
+
+ ReflectionHelpers.setField(mFragment, "mDashboardFeatureProvider",
+ FakeFeatureFactory.setupForTest().dashboardFeatureProvider);
+
+ final Map<Class<?>, List<AbstractPreferenceController>> preferenceControllers =
+ ReflectionHelpers.getField(mFragment, "mPreferenceControllers");
+ List<AbstractPreferenceController> controllerList = new ArrayList<>();
+ controllerList.add(mBiometricSettingsAppPreferenceController);
+ preferenceControllers.put(BiometricSettingsAppPreferenceController.class, controllerList);
+
+ doAnswer(invocation -> {
+ final CharSequence key = invocation.getArgument(0);
+ final Preference preference = new Preference(mContext);
+ preference.setKey(key.toString());
+ return preference;
+ }).when(mFragment).findPreference(any());
+ }
+
+ @Test
+ public void testClickFingerprintUnlockWithValidGkPwHandle() {
+ doAnswer(invocation -> {
+ final FingerprintManager.GenerateChallengeCallback callback =
+ invocation.getArgument(1);
+ callback.onChallengeGenerated(0, 0, 1L);
+ return null;
+ }).when(mFingerprintManager).generateChallenge(anyInt(), any());
+ doReturn(new byte[] { 1 }).when(mFragment).requestGatekeeperHat(any(), anyLong(), anyInt(),
+ anyLong());
+
+ // Start fragment
+ mFragment.onAttach(mContext);
+ mFragment.onCreate(null);
+ mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY);
+ mFragment.onResume();
+
+ // User clicks on "Fingerprint Unlock"
+ final Preference preference = new Preference(mContext);
+ preference.setKey(mFragment.getFingerprintPreferenceKey());
+ mFragment.onPreferenceTreeClick(preference);
+
+ verify(mBiometricSettingsAppPreferenceController).handlePreferenceTreeClick(
+ mPreferenceCaptor.capture());
+ List<Preference> capturedPreferences = mPreferenceCaptor.getAllValues();
+
+ assertThat(capturedPreferences.size()).isEqualTo(1);
+ assertThat(capturedPreferences.get(0).getKey())
+ .isEqualTo(mFragment.getFingerprintPreferenceKey());
+ }
+
+ @Test
+ public void testClickFingerprintUnlockIfGkPwHandleTimeout() {
+ doAnswer(invocation -> {
+ final FingerprintManager.GenerateChallengeCallback callback =
+ invocation.getArgument(1);
+ callback.onChallengeGenerated(0, 0, 1L);
+ return null;
+ }).when(mFingerprintManager).generateChallenge(anyInt(), any());
+ doThrow(new IllegalStateException("Test")).when(mFragment).requestGatekeeperHat(any(),
+ anyLong(), anyInt(), anyLong());
+
+ // Start fragment
+ mFragment.onAttach(mContext);
+ mFragment.onCreate(null);
+ mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY);
+ mFragment.onResume();
+
+ // User clicks on "Fingerprint Unlock"
+ final Preference preference = new Preference(mContext);
+ preference.setKey(mFragment.getFingerprintPreferenceKey());
+ mFragment.onPreferenceTreeClick(preference);
+
+ verify(mFragment).launchChooseOrConfirmLock();
+ }
+
+ @Test
+ public void testActivityResultLaunchFingerprintUnlock() {
+ doAnswer(invocation -> {
+ final FingerprintManager.GenerateChallengeCallback callback =
+ invocation.getArgument(1);
+ callback.onChallengeGenerated(0, 0, 1L);
+ return null;
+ }).when(mFingerprintManager).generateChallenge(anyInt(), any());
+ doReturn(new byte[] { 1 }).when(mFragment).requestGatekeeperHat(any(), anyLong(), anyInt(),
+ anyLong());
+
+ // Start fragment
+ mFragment.onAttach(mContext);
+ final Bundle bundle = new Bundle();
+ bundle.putString(BiometricsSettingsBase.RETRY_PREFERENCE_KEY,
+ mFragment.getFingerprintPreferenceKey());
+ final Bundle retryBundle = new Bundle();
+ bundle.putBundle(BiometricsSettingsBase.RETRY_PREFERENCE_BUNDLE, retryBundle);
+ mFragment.onCreate(bundle);
+ mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY);
+ mFragment.onResume();
+
+ // onActivityResult
+ mFragment.onActivityResult(CONFIRM_REQUEST, RESULT_FINISHED,
+ new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L));
+
+ verify(mBiometricSettingsAppPreferenceController).handlePreferenceTreeClick(
+ mPreferenceCaptor.capture());
+ List<Preference> capturedPreferences = mPreferenceCaptor.getAllValues();
+ assertThat(capturedPreferences.size()).isEqualTo(1);
+ assertThat(capturedPreferences.get(0).getKey())
+ .isEqualTo(mFragment.getFingerprintPreferenceKey());
+ }
+
+ @Test
+ public void testClickFaceUnlock() {
+ doAnswer(invocation -> {
+ final FaceManager.GenerateChallengeCallback callback =
+ invocation.getArgument(1);
+ callback.onGenerateChallengeResult(0, 0, 1L);
+ return null;
+ }).when(mFaceManager).generateChallenge(anyInt(), any());
+ doReturn(new byte[] { 1 }).when(mFragment).requestGatekeeperHat(any(), anyLong(), anyInt(),
+ anyLong());
+
+ // Start fragment
+ mFragment.onAttach(mContext);
+ mFragment.onCreate(null);
+ mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY);
+ mFragment.onResume();
+
+ // User clicks on "Face Unlock"
+ final Preference preference = new Preference(mContext);
+ preference.setKey(mFragment.getFacePreferenceKey());
+ mFragment.onPreferenceTreeClick(preference);
+
+ verify(mBiometricSettingsAppPreferenceController).handlePreferenceTreeClick(
+ mPreferenceCaptor.capture());
+ List<Preference> capturedPreferences = mPreferenceCaptor.getAllValues();
+ assertThat(capturedPreferences.size()).isEqualTo(1);
+ assertThat(capturedPreferences.get(0).getKey()).isEqualTo(mFragment.getFacePreferenceKey());
+ }
+
+ @Test
+ public void testClickFaceUnlockIfGkPwHandleTimeout() {
+ doAnswer(invocation -> {
+ final FaceManager.GenerateChallengeCallback callback =
+ invocation.getArgument(1);
+ callback.onGenerateChallengeResult(0, 0, 1L);
+ return null;
+ }).when(mFaceManager).generateChallenge(anyInt(), any());
+ doThrow(new IllegalStateException("Test")).when(mFragment).requestGatekeeperHat(any(),
+ anyLong(), anyInt(), anyLong());
+
+ // Start fragment
+ mFragment.onAttach(mContext);
+ mFragment.onCreate(null);
+ mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY);
+ mFragment.onResume();
+
+ // User clicks on "Face Unlock"
+ final Preference preference = new Preference(mContext);
+ preference.setKey(mFragment.getFacePreferenceKey());
+ mFragment.onPreferenceTreeClick(preference);
+
+ verify(mFragment).launchChooseOrConfirmLock();
+ }
+
+ @Test
+ public void testActivityResultLaunchFaceUnlock() {
+ doAnswer(invocation -> {
+ final FaceManager.GenerateChallengeCallback callback =
+ invocation.getArgument(1);
+ callback.onGenerateChallengeResult(0, 0, 1L);
+ return null;
+ }).when(mFaceManager).generateChallenge(anyInt(), any());
+ doReturn(new byte[] { 1 }).when(mFragment).requestGatekeeperHat(any(), anyLong(), anyInt(),
+ anyLong());
+
+ // Start fragment
+ mFragment.onAttach(mContext);
+ final Bundle bundle = new Bundle();
+ bundle.putString(BiometricsSettingsBase.RETRY_PREFERENCE_KEY,
+ mFragment.getFingerprintPreferenceKey());
+ final Bundle retryBundle = new Bundle();
+ bundle.putBundle(BiometricsSettingsBase.RETRY_PREFERENCE_BUNDLE, retryBundle);
+ mFragment.onCreate(bundle);
+ mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY);
+ mFragment.onResume();
+
+ // User clicks on "Face Unlock"
+ final Preference preference = new Preference(mContext);
+ preference.setKey(mFragment.getFacePreferenceKey());
+ mFragment.onPreferenceTreeClick(preference);
+
+ verify(mBiometricSettingsAppPreferenceController).handlePreferenceTreeClick(
+ mPreferenceCaptor.capture());
+ List<Preference> capturedPreferences = mPreferenceCaptor.getAllValues();
+ assertThat(capturedPreferences.size()).isEqualTo(1);
+ assertThat(capturedPreferences.get(0).getKey()).isEqualTo(mFragment.getFacePreferenceKey());
+ }
+
+ /**
+ * a test fragment that initializes PreferenceScreen for testing.
+ */
+ static class TestCombinedBiometricProfileSettings extends CombinedBiometricProfileSettings {
+
+ private final Context mContext;
+ private final PreferenceManager mPreferenceManager;
+
+ TestCombinedBiometricProfileSettings(Context context) {
+ super();
+ mContext = context;
+ mPreferenceManager = new PreferenceManager(context);
+ mPreferenceManager.setPreferences(mPreferenceManager.createPreferenceScreen(context));
+ setArguments(new Bundle());
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ @Override
+ public int getPreferenceScreenResId() {
+ return R.xml.placeholder_prefs;
+ }
+
+ @Override
+ public PreferenceScreen getPreferenceScreen() {
+ return mPreferenceManager.getPreferenceScreen();
+ }
+
+ @Override
+ public PreferenceManager getPreferenceManager() {
+ return mPreferenceManager;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+ // do nothing
+ }
+
+ @Override
+ public void addPreferencesFromResource(@XmlRes int preferencesResId) {
+ // do nothing
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ protected void launchChooseOrConfirmLock() {
+ // do nothing
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceControllerTest.java
index b9692cf..77a6b02 100644
--- a/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceControllerTest.java
@@ -31,6 +31,7 @@
import android.os.UserManager;
import android.provider.Settings;
+import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedSwitchPreference;
@@ -65,6 +66,9 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ ShadowUtils.setFaceManager(mFaceManager);
+ FakeFeatureFactory.setupForTest();
+
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(eq(Context.FACE_SERVICE))).thenReturn(mFaceManager);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
index 40cb25b..5f8c434 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.UserHandle;
import android.os.UserManager;
@@ -39,6 +40,7 @@
public class ShadowUtils {
private static FingerprintManager sFingerprintManager = null;
+ private static FaceManager sFaceManager = null;
private static boolean sIsUserAMonkey;
private static boolean sIsDemoUser;
private static ComponentName sDeviceOwnerComponentName;
@@ -63,6 +65,15 @@
sFingerprintManager = fingerprintManager;
}
+ @Implementation
+ protected static FaceManager getFaceManagerOrNull(Context context) {
+ return sFaceManager;
+ }
+
+ public static void setFaceManager(FaceManager faceManager) {
+ sFaceManager = faceManager;
+ }
+
public static void reset() {
sFingerprintManager = null;
sIsUserAMonkey = false;