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(); + } +}