From 60984c6c321f2e339a89555f924ddaed8f14fdec Mon Sep 17 00:00:00 2001 From: Kevin Chyn Date: Mon, 16 Nov 2020 16:44:44 -0800 Subject: [PATCH] Clear BiometricScheduler when biometric HAL dies Operations cannot be expected to complete if the HAL dies. The easiest solution is to clear any existing operation, as well as any pending operations. It's dangerous if we leave pending operations in the queue, since for some HALs, subsequent operations depend on preceeding operations. For example, updateActiveUser + authenticate. Fixes: 172683967 Test: atest com.android.server.biometrics Change-Id: I50b5234d4b382b08767dc359c4a13404e73897c2 --- .../sensors/BiometricScheduler.java | 13 ++ .../biometrics/sensors/ClientMonitor.java | 2 +- .../sensors/face/aidl/FaceProvider.java | 7 +- .../biometrics/sensors/face/hidl/Face10.java | 15 +- .../fingerprint/aidl/FingerprintProvider.java | 7 +- .../fingerprint/hidl/Fingerprint21.java | 5 +- .../sensors/face/aidl/FaceProviderTest.java | 125 +++++++++++++++++ .../sensors/face/{ => hidl}/Face10Test.java | 18 ++- .../aidl/FingerprintProviderTest.java | 128 ++++++++++++++++++ .../fingerprint/hidl/Fingerprint21Test.java | 95 +++++++++++++ 10 files changed, 401 insertions(+), 14 deletions(-) create mode 100644 services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java rename services/tests/servicestests/src/com/android/server/biometrics/sensors/face/{ => hidl}/Face10Test.java (81%) create mode 100644 services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java create mode 100644 services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index 2784f46a96c2a..242abf36f4100 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -543,6 +543,10 @@ public class BiometricScheduler { return mCurrentOperation.clientMonitor; } + public int getCurrentPendingCount() { + return mPendingOperations.size(); + } + public void recordCrashState() { if (mCrashStates.size() >= CrashState.NUM_ENTRIES) { mCrashStates.removeFirst(); @@ -568,4 +572,13 @@ public class BiometricScheduler { pw.println("Crash State " + crashState); } } + + /** + * Clears the scheduler of anything work-related. This should be used for example when the + * HAL dies. + */ + public void reset() { + mPendingOperations.clear(); + mCurrentOperation = null; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java index 0dee81681fc48..bbd652357888e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java @@ -219,7 +219,7 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinde return mSensorId; } - public final T getFreshDaemon() { + public T getFreshDaemon() { return mLazyDaemon.getDaemon(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 01c16fd1f4a6c..ad91d0cc83cce 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -41,6 +41,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.ClientMonitor; @@ -69,7 +70,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull private final Context mContext; @NonNull private final String mHalInstanceName; - @NonNull private final SparseArray mSensors; // Map of sensors that this HAL supports + @NonNull @VisibleForTesting + final SparseArray mSensors; // Map of sensors that this HAL supports @NonNull private final ClientMonitor.LazyDaemon mLazyDaemon; @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @@ -585,8 +587,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mHandler.post(() -> { mDaemon = null; for (int i = 0; i < mSensors.size(); i++) { + final Sensor sensor = mSensors.valueAt(i); final int sensorId = mSensors.keyAt(i); PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount(); + sensor.getScheduler().recordCrashState(); + sensor.getScheduler().reset(); } }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 27ca33de415dd..4cd1d05cbb650 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -333,16 +333,17 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { } @VisibleForTesting - public Face10(@NonNull Context context, int sensorId, + Face10(@NonNull Context context, int sensorId, @BiometricManager.Authenticators.Types int strength, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - boolean supportsSelfIllumination, int maxTemplatesAllowed) { + boolean supportsSelfIllumination, int maxTemplatesAllowed, + @NonNull BiometricScheduler scheduler) { mSensorProperties = new FaceSensorPropertiesInternal(sensorId, Utils.authenticatorStrengthToPropertyStrength(strength), maxTemplatesAllowed, false /* supportsFaceDetect */, supportsSelfIllumination); mContext = context; mSensorId = sensorId; - mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */); + mScheduler = scheduler; mHandler = new Handler(Looper.getMainLooper()); mUsageStats = new UsageStats(context); mAuthenticatorIds = new HashMap<>(); @@ -369,7 +370,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull LockoutResetDispatcher lockoutResetDispatcher) { this(context, sensorId, strength, lockoutResetDispatcher, context.getResources().getBoolean(R.bool.config_faceAuthSupportsSelfIllumination), - context.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser)); + context.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser), + new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */)); } @Override @@ -388,12 +390,13 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mScheduler.recordCrashState(); - FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, BiometricsProtoEnums.MODALITY_FACE, BiometricsProtoEnums.ISSUE_HAL_DEATH); } + + mScheduler.recordCrashState(); + mScheduler.reset(); }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 5f3be488c12ca..db34d1444650a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -41,6 +41,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.ClientMonitor; @@ -67,7 +68,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull private final Context mContext; @NonNull private final String mHalInstanceName; - @NonNull private final SparseArray mSensors; // Map of sensors that this HAL supports + @NonNull @VisibleForTesting + final SparseArray mSensors; // Map of sensors that this HAL supports @NonNull private final ClientMonitor.LazyDaemon mLazyDaemon; @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @@ -607,8 +609,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mDaemon = null; for (int i = 0; i < mSensors.size(); i++) { + final Sensor sensor = mSensors.valueAt(i); final int sensorId = mSensors.keyAt(i); PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount(); + sensor.getScheduler().recordCrashState(); + sensor.getScheduler().reset(); } }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 9924d47104366..11372a30599df 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -386,12 +386,13 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mScheduler.recordCrashState(); - FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ISSUE_HAL_DEATH); } + + mScheduler.recordCrashState(); + mScheduler.reset(); }); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java new file mode 100644 index 0000000000000..efdbda38c01c0 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2020 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.server.biometrics.sensors.face.aidl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.biometrics.common.CommonProps; +import android.hardware.biometrics.face.SensorProps; +import android.os.UserManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.ClientMonitor; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@Presubmit +@SmallTest +public class FaceProviderTest { + + private static final String TAG = "FaceProviderTest"; + + @Mock + private Context mContext; + @Mock + private UserManager mUserManager; + + private SensorProps[] mSensorProps; + private LockoutResetDispatcher mLockoutResetDispatcher; + private FaceProvider mFaceProvider; + + private static void waitForIdle() { + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>()); + + final SensorProps sensor1 = new SensorProps(); + sensor1.commonProps = new CommonProps(); + sensor1.commonProps.sensorId = 0; + final SensorProps sensor2 = new SensorProps(); + sensor2.commonProps = new CommonProps(); + sensor2.commonProps.sensorId = 1; + + mSensorProps = new SensorProps[] {sensor1, sensor2}; + + mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); + + mFaceProvider = new FaceProvider(mContext, mSensorProps, TAG, + mLockoutResetDispatcher); + } + + @SuppressWarnings("rawtypes") + @Test + public void halServiceDied_resetsAllSchedulers() { + assertEquals(mSensorProps.length, mFaceProvider.getSensorProperties().size()); + + // Schedule N operations on each sensor + final int numFakeOperations = 10; + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + for (int i = 0; i < numFakeOperations; i++) { + final ClientMonitor testMonitor = mock(ClientMonitor.class); + when(testMonitor.getFreshDaemon()).thenReturn(new Object()); + scheduler.scheduleClientMonitor(testMonitor); + } + } + + waitForIdle(); + // The right amount of pending and current operations are scheduled + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount()); + assertNotNull(scheduler.getCurrentClient()); + } + + // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke + // serviceDied directly. + mFaceProvider.binderDied(); + waitForIdle(); + + // No pending operations, no current operation. + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + assertNull(scheduler.getCurrentClient()); + assertEquals(0, scheduler.getCurrentPendingCount()); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java similarity index 81% rename from services/tests/servicestests/src/com/android/server/biometrics/sensors/face/Face10Test.java rename to services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java index 35fc7f09c0578..99aab5c7a6af8 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/Face10Test.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.android.server.biometrics.sensors.face; +package com.android.server.biometrics.sensors.face.hidl; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -28,8 +29,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.LockoutResetDispatcher; -import com.android.server.biometrics.sensors.face.hidl.Face10; import org.junit.Before; import org.junit.Test; @@ -49,6 +50,8 @@ public class Face10Test { private Context mContext; @Mock private UserManager mUserManager; + @Mock + private BiometricScheduler mScheduler; private LockoutResetDispatcher mLockoutResetDispatcher; private com.android.server.biometrics.sensors.face.hidl.Face10 mFace10; @@ -68,7 +71,7 @@ public class Face10Test { mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); mFace10 = new Face10(mContext, SENSOR_ID, BiometricManager.Authenticators.BIOMETRIC_STRONG, mLockoutResetDispatcher, false /* supportsSelfIllumination */, - 1 /* maxTemplatesAllowed */); + 1 /* maxTemplatesAllowed */, mScheduler); mBinder = new Binder(); } @@ -78,4 +81,13 @@ public class Face10Test { 0 /* challenge */); waitForIdle(); } + + @Test + public void halServiceDied_resetsScheduler() { + // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke + // serviceDied directly. + mFace10.serviceDied(0 /* cookie */); + waitForIdle(); + verify(mScheduler).reset(); + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java new file mode 100644 index 0000000000000..624775b775fcc --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2020 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.server.biometrics.sensors.fingerprint.aidl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.biometrics.common.CommonProps; +import android.hardware.biometrics.fingerprint.SensorProps; +import android.os.UserManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.ClientMonitor; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@Presubmit +@SmallTest +public class FingerprintProviderTest { + + private static final String TAG = "FingerprintProviderTest"; + + @Mock + private Context mContext; + @Mock + private UserManager mUserManager; + @Mock + private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; + + private SensorProps[] mSensorProps; + private LockoutResetDispatcher mLockoutResetDispatcher; + private FingerprintProvider mFingerprintProvider; + + private static void waitForIdle() { + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>()); + + final SensorProps sensor1 = new SensorProps(); + sensor1.commonProps = new CommonProps(); + sensor1.commonProps.sensorId = 0; + final SensorProps sensor2 = new SensorProps(); + sensor2.commonProps = new CommonProps(); + sensor2.commonProps.sensorId = 1; + + mSensorProps = new SensorProps[] {sensor1, sensor2}; + + mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); + + mFingerprintProvider = new FingerprintProvider(mContext, mSensorProps, TAG, + mLockoutResetDispatcher, mGestureAvailabilityDispatcher); + } + + @SuppressWarnings("rawtypes") + @Test + public void halServiceDied_resetsAllSchedulers() { + assertEquals(mSensorProps.length, mFingerprintProvider.getSensorProperties().size()); + + // Schedule N operations on each sensor + final int numFakeOperations = 10; + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + for (int i = 0; i < numFakeOperations; i++) { + final ClientMonitor testMonitor = mock(ClientMonitor.class); + when(testMonitor.getFreshDaemon()).thenReturn(new Object()); + scheduler.scheduleClientMonitor(testMonitor); + } + } + + waitForIdle(); + // The right amount of pending and current operations are scheduled + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount()); + assertNotNull(scheduler.getCurrentClient()); + } + + // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke + // serviceDied directly. + mFingerprintProvider.binderDied(); + waitForIdle(); + + // No pending operations, no current operation. + for (SensorProps prop : mSensorProps) { + final BiometricScheduler scheduler = + mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler(); + assertNull(scheduler.getCurrentClient()); + assertEquals(0, scheduler.getCurrentPendingCount()); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java new file mode 100644 index 0000000000000..b2aeb33039f5f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 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.server.biometrics.sensors.fingerprint.hidl; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.hardware.biometrics.BiometricManager; +import android.os.Handler; +import android.os.Looper; +import android.os.UserManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import com.android.internal.R; +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@Presubmit +@SmallTest +public class Fingerprint21Test { + + private static final String TAG = "Fingerprint21Test"; + private static final int SENSOR_ID = 1; + + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private UserManager mUserManager; + @Mock + Fingerprint21.HalResultController mHalResultController; + @Mock + private BiometricScheduler mScheduler; + + private LockoutResetDispatcher mLockoutResetDispatcher; + private Fingerprint21 mFingerprint21; + + private static void waitForIdle() { + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>()); + when(mContext.getResources()).thenReturn(mResources); + when(mResources.getInteger(eq(R.integer.config_fingerprintMaxTemplatesPerUser))) + .thenReturn(5); + + mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); + mFingerprint21 = new Fingerprint21(mContext, mScheduler, + new Handler(Looper.getMainLooper()), SENSOR_ID, + BiometricManager.Authenticators.BIOMETRIC_WEAK, mLockoutResetDispatcher, + mHalResultController); + } + + @Test + public void halServiceDied_resetsScheduler() { + // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke + // serviceDied directly. + mFingerprint21.serviceDied(0 /* cookie */); + waitForIdle(); + verify(mScheduler).reset(); + } +}