1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.biometrics; 18 19 import static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FACE_REENROLL_DIALOG; 20 import static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.eq; 26 import static org.mockito.Mockito.never; 27 import static org.mockito.Mockito.verify; 28 import static org.mockito.Mockito.when; 29 30 import android.app.Notification; 31 import android.app.NotificationManager; 32 import android.hardware.biometrics.BiometricFaceConstants; 33 import android.hardware.biometrics.BiometricSourceType; 34 import android.hardware.biometrics.BiometricStateListener; 35 import android.hardware.face.FaceManager; 36 import android.hardware.fingerprint.FingerprintManager; 37 import android.os.Handler; 38 import android.os.UserHandle; 39 import android.testing.AndroidTestingRunner; 40 import android.testing.TestableLooper; 41 42 import androidx.test.filters.SmallTest; 43 44 import com.android.keyguard.KeyguardUpdateMonitor; 45 import com.android.keyguard.KeyguardUpdateMonitorCallback; 46 import com.android.systemui.SysuiTestCase; 47 import com.android.systemui.statusbar.policy.KeyguardStateController; 48 49 import org.junit.Before; 50 import org.junit.Rule; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.mockito.ArgumentCaptor; 54 import org.mockito.Mock; 55 import org.mockito.junit.MockitoJUnit; 56 import org.mockito.junit.MockitoRule; 57 58 import java.util.Optional; 59 60 @SmallTest 61 @RunWith(AndroidTestingRunner.class) 62 @TestableLooper.RunWithLooper(setAsMainLooper = true) 63 public class BiometricNotificationServiceTest extends SysuiTestCase { 64 @Rule 65 public MockitoRule rule = MockitoJUnit.rule(); 66 67 @Mock 68 KeyguardUpdateMonitor mKeyguardUpdateMonitor; 69 @Mock 70 KeyguardStateController mKeyguardStateController; 71 @Mock 72 NotificationManager mNotificationManager; 73 @Mock 74 Optional<FingerprintReEnrollNotification> mFingerprintReEnrollNotificationOptional; 75 @Mock 76 FingerprintReEnrollNotification mFingerprintReEnrollNotification; 77 @Mock 78 FingerprintManager mFingerprintManager; 79 @Mock 80 FaceManager mFaceManager; 81 82 private static final String TAG = "BiometricNotificationService"; 83 private static final int FACE_NOTIFICATION_ID = 1; 84 private static final int FINGERPRINT_NOTIFICATION_ID = 2; 85 private static final long SHOW_NOTIFICATION_DELAY_MS = 5_000L; // 5 seconds 86 private static final int FINGERPRINT_ACQUIRED_RE_ENROLL = 0; 87 88 private final ArgumentCaptor<Notification> mNotificationArgumentCaptor = 89 ArgumentCaptor.forClass(Notification.class); 90 private BiometricNotificationService mBiometricNotificationService; 91 private TestableLooper mLooper; 92 private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; 93 private KeyguardStateController.Callback mKeyguardStateControllerCallback; 94 private BiometricStateListener mFaceStateListener; 95 private BiometricStateListener mFingerprintStateListener; 96 97 @Before setUp()98 public void setUp() { 99 when(mFingerprintReEnrollNotificationOptional.orElse(any())) 100 .thenReturn(mFingerprintReEnrollNotification); 101 when(mFingerprintReEnrollNotification.isFingerprintReEnrollRequired( 102 FINGERPRINT_ACQUIRED_RE_ENROLL)).thenReturn(true); 103 104 mLooper = TestableLooper.get(this); 105 Handler handler = new Handler(mLooper.getLooper()); 106 BiometricNotificationDialogFactory dialogFactory = new BiometricNotificationDialogFactory(); 107 BiometricNotificationBroadcastReceiver broadcastReceiver = 108 new BiometricNotificationBroadcastReceiver(mContext, dialogFactory); 109 mBiometricNotificationService = 110 new BiometricNotificationService(mContext, 111 mKeyguardUpdateMonitor, mKeyguardStateController, handler, 112 mNotificationManager, 113 broadcastReceiver, 114 mFingerprintReEnrollNotificationOptional, 115 mFingerprintManager, 116 mFaceManager); 117 mBiometricNotificationService.start(); 118 119 ArgumentCaptor<KeyguardUpdateMonitorCallback> updateMonitorCallbackArgumentCaptor = 120 ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); 121 ArgumentCaptor<KeyguardStateController.Callback> stateControllerCallbackArgumentCaptor = 122 ArgumentCaptor.forClass(KeyguardStateController.Callback.class); 123 ArgumentCaptor<BiometricStateListener> faceStateListenerArgumentCaptor = 124 ArgumentCaptor.forClass(BiometricStateListener.class); 125 ArgumentCaptor<BiometricStateListener> fingerprintStateListenerArgumentCaptor = 126 ArgumentCaptor.forClass(BiometricStateListener.class); 127 128 verify(mKeyguardUpdateMonitor).registerCallback( 129 updateMonitorCallbackArgumentCaptor.capture()); 130 verify(mKeyguardStateController).addCallback( 131 stateControllerCallbackArgumentCaptor.capture()); 132 verify(mFaceManager).registerBiometricStateListener( 133 faceStateListenerArgumentCaptor.capture()); 134 verify(mFingerprintManager).registerBiometricStateListener( 135 fingerprintStateListenerArgumentCaptor.capture()); 136 137 mFaceStateListener = faceStateListenerArgumentCaptor.getValue(); 138 mFingerprintStateListener = fingerprintStateListenerArgumentCaptor.getValue(); 139 mKeyguardUpdateMonitorCallback = updateMonitorCallbackArgumentCaptor.getValue(); 140 mKeyguardStateControllerCallback = stateControllerCallbackArgumentCaptor.getValue(); 141 } 142 143 @Test testShowFingerprintReEnrollNotification_onAcquiredReEnroll()144 public void testShowFingerprintReEnrollNotification_onAcquiredReEnroll() { 145 when(mKeyguardStateController.isShowing()).thenReturn(false); 146 147 mKeyguardUpdateMonitorCallback.onBiometricHelp( 148 FINGERPRINT_ACQUIRED_RE_ENROLL, 149 "Testing Fingerprint Re-enrollment" /* errString */, 150 BiometricSourceType.FINGERPRINT 151 ); 152 mKeyguardStateControllerCallback.onKeyguardShowingChanged(); 153 154 mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS); 155 mLooper.processAllMessages(); 156 157 verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FINGERPRINT_NOTIFICATION_ID), 158 mNotificationArgumentCaptor.capture(), any()); 159 160 Notification fingerprintNotification = mNotificationArgumentCaptor.getValue(); 161 162 assertThat(fingerprintNotification.contentIntent.getIntent().getAction()) 163 .isEqualTo(ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG); 164 } 165 @Test testShowFaceReEnrollNotification_onErrorReEnroll()166 public void testShowFaceReEnrollNotification_onErrorReEnroll() { 167 when(mKeyguardStateController.isShowing()).thenReturn(false); 168 169 mKeyguardUpdateMonitorCallback.onBiometricError( 170 BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL, 171 "Testing Face Re-enrollment" /* errString */, 172 BiometricSourceType.FACE 173 ); 174 mKeyguardStateControllerCallback.onKeyguardShowingChanged(); 175 176 mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS); 177 mLooper.processAllMessages(); 178 179 verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID), 180 mNotificationArgumentCaptor.capture(), any()); 181 182 Notification fingerprintNotification = mNotificationArgumentCaptor.getValue(); 183 184 assertThat(fingerprintNotification.contentIntent.getIntent().getAction()) 185 .isEqualTo(ACTION_SHOW_FACE_REENROLL_DIALOG); 186 } 187 188 @Test testCancelReEnrollmentNotification_onFaceEnrollmentStateChange()189 public void testCancelReEnrollmentNotification_onFaceEnrollmentStateChange() { 190 when(mKeyguardStateController.isShowing()).thenReturn(false); 191 192 mKeyguardUpdateMonitorCallback.onBiometricError( 193 BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL, 194 "Testing Face Re-enrollment" /* errString */, 195 BiometricSourceType.FACE 196 ); 197 mKeyguardStateControllerCallback.onKeyguardShowingChanged(); 198 199 mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS); 200 mLooper.processAllMessages(); 201 202 verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID), 203 mNotificationArgumentCaptor.capture(), any()); 204 205 mFaceStateListener.onEnrollmentsChanged(0 /* userId */, 0 /* sensorId */, 206 false /* hasEnrollments */); 207 208 verify(mNotificationManager).cancelAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID), 209 eq(UserHandle.CURRENT)); 210 } 211 212 @Test testCancelReEnrollmentNotification_onFingerprintEnrollmentStateChange()213 public void testCancelReEnrollmentNotification_onFingerprintEnrollmentStateChange() { 214 when(mKeyguardStateController.isShowing()).thenReturn(false); 215 216 mKeyguardUpdateMonitorCallback.onBiometricHelp( 217 FINGERPRINT_ACQUIRED_RE_ENROLL, 218 "Testing Fingerprint Re-enrollment" /* errString */, 219 BiometricSourceType.FINGERPRINT 220 ); 221 mKeyguardStateControllerCallback.onKeyguardShowingChanged(); 222 223 mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS); 224 mLooper.processAllMessages(); 225 226 verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FINGERPRINT_NOTIFICATION_ID), 227 mNotificationArgumentCaptor.capture(), any()); 228 229 mFingerprintStateListener.onEnrollmentsChanged(0 /* userId */, 0 /* sensorId */, 230 false /* hasEnrollments */); 231 232 verify(mNotificationManager).cancelAsUser(eq(TAG), eq(FINGERPRINT_NOTIFICATION_ID), 233 eq(UserHandle.CURRENT)); 234 } 235 236 @Test testResetFaceUnlockReEnroll_onStart()237 public void testResetFaceUnlockReEnroll_onStart() { 238 when(mKeyguardStateController.isShowing()).thenReturn(false); 239 240 mKeyguardUpdateMonitorCallback.onBiometricError( 241 BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL, 242 "Testing Face Re-enrollment" /* errString */, 243 BiometricSourceType.FACE 244 ); 245 246 mBiometricNotificationService.start(); 247 mKeyguardStateControllerCallback.onKeyguardShowingChanged(); 248 249 mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS); 250 mLooper.processAllMessages(); 251 252 verify(mNotificationManager, never()).notifyAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID), 253 mNotificationArgumentCaptor.capture(), any()); 254 } 255 256 } 257