1 /* 2 * Copyright (C) 2019 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.server.biometrics; 18 19 import static android.Manifest.permission.MANAGE_BIOMETRIC; 20 import static android.Manifest.permission.TEST_BIOMETRIC; 21 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; 22 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE; 23 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED; 24 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS; 25 26 import static junit.framework.Assert.assertEquals; 27 28 import static org.junit.Assert.assertThrows; 29 import static org.mockito.ArgumentMatchers.any; 30 import static org.mockito.ArgumentMatchers.anyInt; 31 import static org.mockito.ArgumentMatchers.anyString; 32 import static org.mockito.ArgumentMatchers.eq; 33 import static org.mockito.Mockito.doNothing; 34 import static org.mockito.Mockito.doThrow; 35 import static org.mockito.Mockito.mock; 36 import static org.mockito.Mockito.never; 37 import static org.mockito.Mockito.verify; 38 import static org.mockito.Mockito.when; 39 40 import android.app.AppOpsManager; 41 import android.content.Context; 42 import android.content.pm.PackageManager; 43 import android.content.res.Resources; 44 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; 45 import android.hardware.biometrics.IBiometricService; 46 import android.hardware.biometrics.IBiometricServiceReceiver; 47 import android.hardware.biometrics.PromptInfo; 48 import android.hardware.face.FaceSensorPropertiesInternal; 49 import android.hardware.face.IFaceService; 50 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; 51 import android.hardware.fingerprint.IFingerprintService; 52 import android.hardware.iris.IIrisService; 53 import android.os.Binder; 54 import android.os.UserHandle; 55 import android.platform.test.annotations.Presubmit; 56 57 import androidx.test.InstrumentationRegistry; 58 import androidx.test.filters.SmallTest; 59 60 import com.android.internal.R; 61 import com.android.server.LocalServices; 62 import com.android.server.companion.virtual.VirtualDeviceManagerInternal; 63 64 import org.junit.Before; 65 import org.junit.Rule; 66 import org.junit.Test; 67 import org.mockito.ArgumentCaptor; 68 import org.mockito.Captor; 69 import org.mockito.Mock; 70 import org.mockito.junit.MockitoJUnit; 71 import org.mockito.junit.MockitoRule; 72 import org.mockito.stubbing.Stubber; 73 74 import java.util.List; 75 76 @Presubmit 77 @SmallTest 78 public class AuthServiceTest { 79 80 private static final String TEST_OP_PACKAGE_NAME = "test_package"; 81 82 private AuthService mAuthService; 83 84 @Rule 85 public MockitoRule mockitorule = MockitoJUnit.rule(); 86 87 @Mock 88 private Context mContext; 89 @Mock 90 private Resources mResources; 91 @Mock 92 private PackageManager mPackageManager; 93 @Mock 94 IBiometricServiceReceiver mReceiver; 95 @Mock 96 AuthService.Injector mInjector; 97 @Mock 98 IBiometricService mBiometricService; 99 @Mock 100 IFingerprintService mFingerprintService; 101 @Mock 102 IIrisService mIrisService; 103 @Mock 104 IFaceService mFaceService; 105 @Mock 106 AppOpsManager mAppOpsManager; 107 @Mock 108 private VirtualDeviceManagerInternal mVdmInternal; 109 @Captor 110 private ArgumentCaptor<List<FingerprintSensorPropertiesInternal>> mFingerprintPropsCaptor; 111 @Captor 112 private ArgumentCaptor<List<FaceSensorPropertiesInternal>> mFacePropsCaptor; 113 114 @Before setUp()115 public void setUp() { 116 // Placeholder test config 117 final String[] config = { 118 "0:2:15", // ID0:Fingerprint:Strong 119 "1:4:15", // ID1:Iris:Strong 120 "2:8:15", // ID2:Face:Strong 121 }; 122 LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class); 123 LocalServices.addService(VirtualDeviceManagerInternal.class, mVdmInternal); 124 125 when(mResources.getIntArray(eq(R.array.config_udfps_sensor_props))).thenReturn(new int[0]); 126 when(mResources.getBoolean(eq(R.bool.config_is_powerbutton_fps))).thenReturn(false); 127 when(mResources.getInteger(eq(R.integer.config_fingerprintMaxTemplatesPerUser))).thenReturn( 128 1); 129 when(mResources.getBoolean(eq(R.bool.config_faceAuthSupportsSelfIllumination))).thenReturn( 130 false); 131 when(mResources.getInteger(eq(R.integer.config_faceMaxTemplatesPerUser))).thenReturn(1); 132 133 when(mContext.getPackageManager()).thenReturn(mPackageManager); 134 when(mContext.getResources()).thenReturn(mResources); 135 when(mInjector.getBiometricService()).thenReturn(mBiometricService); 136 when(mInjector.getConfiguration(any())).thenReturn(config); 137 when(mInjector.getFingerprintService()).thenReturn(mFingerprintService); 138 when(mInjector.getFaceService()).thenReturn(mFaceService); 139 when(mInjector.getIrisService()).thenReturn(mIrisService); 140 when(mInjector.getAppOps(any())).thenReturn(mAppOpsManager); 141 when(mInjector.isHidlDisabled(any())).thenReturn(false); 142 143 setInternalAndTestBiometricPermissions(mContext, false /* hasPermission */); 144 } 145 146 @Test testRegisterNullService_doesNotRegister()147 public void testRegisterNullService_doesNotRegister() throws Exception { 148 setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */); 149 150 // Config contains Fingerprint, Iris, Face, but services are all null 151 152 when(mInjector.getFingerprintService()).thenReturn(null); 153 when(mInjector.getFaceService()).thenReturn(null); 154 when(mInjector.getIrisService()).thenReturn(null); 155 156 mAuthService = new AuthService(mContext, mInjector); 157 mAuthService.onStart(); 158 159 verify(mBiometricService, never()).registerAuthenticator( 160 anyInt(), 161 anyInt(), 162 anyInt(), 163 any()); 164 } 165 166 @Test testRegisterAuthenticator_registerAuthenticators()167 public void testRegisterAuthenticator_registerAuthenticators() throws Exception { 168 final int fingerprintId = 0; 169 final int fingerprintStrength = 15; 170 171 final int faceId = 1; 172 final int faceStrength = 4095; 173 174 final String[] config = { 175 // ID0:Fingerprint:Strong 176 String.format("%d:2:%d", fingerprintId, fingerprintStrength), 177 // ID2:Face:Convenience 178 String.format("%d:8:%d", faceId, faceStrength) 179 }; 180 181 when(mInjector.getConfiguration(any())).thenReturn(config); 182 183 mAuthService = new AuthService(mContext, mInjector); 184 mAuthService.onStart(); 185 186 verify(mFingerprintService).registerAuthenticators(mFingerprintPropsCaptor.capture()); 187 final FingerprintSensorPropertiesInternal fingerprintProp = 188 mFingerprintPropsCaptor.getValue().get(0); 189 assertEquals(fingerprintProp.sensorId, fingerprintId); 190 assertEquals(fingerprintProp.sensorStrength, 191 Utils.authenticatorStrengthToPropertyStrength(fingerprintStrength)); 192 193 verify(mFaceService).registerAuthenticators(mFacePropsCaptor.capture()); 194 final FaceSensorPropertiesInternal faceProp = mFacePropsCaptor.getValue().get(0); 195 assertEquals(faceProp.sensorId, faceId); 196 assertEquals(faceProp.sensorStrength, 197 Utils.authenticatorStrengthToPropertyStrength(faceStrength)); 198 } 199 200 201 // TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId 202 @Test testAuthenticate_appOpsOk_callsBiometricServiceAuthenticate()203 public void testAuthenticate_appOpsOk_callsBiometricServiceAuthenticate() throws Exception { 204 when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_USE_BIOMETRIC), anyInt(), any(), any(), 205 any())).thenReturn(AppOpsManager.MODE_ALLOWED); 206 mAuthService = new AuthService(mContext, mInjector); 207 mAuthService.onStart(); 208 209 final Binder token = new Binder(); 210 final PromptInfo promptInfo = new PromptInfo(); 211 final long sessionId = 0; 212 final int userId = 0; 213 214 mAuthService.mImpl.authenticate( 215 token, 216 sessionId, 217 userId, 218 mReceiver, 219 TEST_OP_PACKAGE_NAME, 220 promptInfo); 221 waitForIdle(); 222 verify(mBiometricService).authenticate( 223 eq(token), 224 eq(sessionId), 225 eq(userId), 226 eq(mReceiver), 227 eq(TEST_OP_PACKAGE_NAME), 228 eq(promptInfo)); 229 } 230 231 @Test testAuthenticate_appOpsDenied_doesNotCallBiometricService()232 public void testAuthenticate_appOpsDenied_doesNotCallBiometricService() throws Exception { 233 when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_USE_BIOMETRIC), anyInt(), any(), any(), 234 any())).thenReturn(AppOpsManager.MODE_ERRORED); 235 mAuthService = new AuthService(mContext, mInjector); 236 mAuthService.onStart(); 237 238 final Binder token = new Binder(); 239 final PromptInfo promptInfo = new PromptInfo(); 240 final long sessionId = 0; 241 final int userId = 0; 242 243 mAuthService.mImpl.authenticate( 244 token, 245 sessionId, 246 userId, 247 mReceiver, 248 TEST_OP_PACKAGE_NAME, 249 promptInfo); 250 waitForIdle(); 251 verify(mBiometricService, never()).authenticate( 252 eq(token), 253 eq(sessionId), 254 eq(userId), 255 eq(mReceiver), 256 eq(TEST_OP_PACKAGE_NAME), 257 eq(promptInfo)); 258 verify(mReceiver).onError(eq(TYPE_NONE), eq(BIOMETRIC_ERROR_CANCELED), anyInt()); 259 } 260 261 @Test testAuthenticate_missingRequiredParam()262 public void testAuthenticate_missingRequiredParam() throws Exception { 263 mAuthService = new AuthService(mContext, mInjector); 264 mAuthService.onStart(); 265 266 final PromptInfo promptInfo = new PromptInfo(); 267 final long sessionId = 0; 268 final int userId = 0; 269 270 mAuthService.mImpl.authenticate( 271 null /* token */, 272 sessionId, 273 userId, 274 mReceiver, 275 TEST_OP_PACKAGE_NAME, 276 promptInfo); 277 waitForIdle(); 278 verify(mReceiver).onError(eq(TYPE_NONE), eq(BIOMETRIC_ERROR_CANCELED), anyInt()); 279 } 280 281 @Test testAuthenticate_noVdmInternalService_noCrash()282 public void testAuthenticate_noVdmInternalService_noCrash() throws Exception { 283 LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class); 284 mAuthService = new AuthService(mContext, mInjector); 285 mAuthService.onStart(); 286 287 final Binder token = new Binder(); 288 289 // This should not crash 290 mAuthService.mImpl.authenticate( 291 token, 292 0, /* sessionId */ 293 0, /* userId */ 294 mReceiver, 295 TEST_OP_PACKAGE_NAME, 296 new PromptInfo()); 297 waitForIdle(); 298 } 299 300 @Test testAuthenticate_callsVirtualDeviceManagerOnAuthenticationPrompt()301 public void testAuthenticate_callsVirtualDeviceManagerOnAuthenticationPrompt() 302 throws Exception { 303 mAuthService = new AuthService(mContext, mInjector); 304 mAuthService.onStart(); 305 306 final Binder token = new Binder(); 307 308 mAuthService.mImpl.authenticate( 309 token, 310 0, /* sessionId */ 311 0, /* userId */ 312 mReceiver, 313 TEST_OP_PACKAGE_NAME, 314 new PromptInfo()); 315 waitForIdle(); 316 317 ArgumentCaptor<Integer> uidCaptor = ArgumentCaptor.forClass(Integer.class); 318 verify(mVdmInternal).onAuthenticationPrompt(uidCaptor.capture()); 319 assertEquals((int) (uidCaptor.getValue()), Binder.getCallingUid()); 320 } 321 322 @Test testAuthenticate_throwsWhenUsingTestConfigurations()323 public void testAuthenticate_throwsWhenUsingTestConfigurations() { 324 final PromptInfo promptInfo = mock(PromptInfo.class); 325 when(promptInfo.containsPrivateApiConfigurations()).thenReturn(false); 326 when(promptInfo.containsTestConfigurations()).thenReturn(true); 327 328 testAuthenticate_throwsWhenUsingTestConfigurations(promptInfo); 329 } 330 331 @Test testAuthenticate_throwsWhenUsingPrivateApis()332 public void testAuthenticate_throwsWhenUsingPrivateApis() { 333 final PromptInfo promptInfo = mock(PromptInfo.class); 334 when(promptInfo.containsPrivateApiConfigurations()).thenReturn(true); 335 when(promptInfo.containsTestConfigurations()).thenReturn(false); 336 337 testAuthenticate_throwsWhenUsingTestConfigurations(promptInfo); 338 } 339 testAuthenticate_throwsWhenUsingTestConfigurations(PromptInfo promptInfo)340 private void testAuthenticate_throwsWhenUsingTestConfigurations(PromptInfo promptInfo) { 341 mAuthService = new AuthService(mContext, mInjector); 342 mAuthService.onStart(); 343 344 assertThrows(SecurityException.class, () -> { 345 mAuthService.mImpl.authenticate( 346 null /* token */, 347 10 /* sessionId */, 348 2 /* userId */, 349 mReceiver, 350 TEST_OP_PACKAGE_NAME, 351 promptInfo); 352 waitForIdle(); 353 }); 354 } 355 356 @Test testCanAuthenticate_callsBiometricServiceCanAuthenticate()357 public void testCanAuthenticate_callsBiometricServiceCanAuthenticate() throws Exception { 358 mAuthService = new AuthService(mContext, mInjector); 359 mAuthService.onStart(); 360 361 final int userId = 0; 362 final int expectedResult = BIOMETRIC_SUCCESS; 363 final int authenticators = 0; 364 when(mBiometricService.canAuthenticate(anyString(), anyInt(), anyInt(), anyInt())) 365 .thenReturn(expectedResult); 366 367 final int result = mAuthService.mImpl 368 .canAuthenticate(TEST_OP_PACKAGE_NAME, userId, authenticators); 369 370 assertEquals(expectedResult, result); 371 waitForIdle(); 372 verify(mBiometricService).canAuthenticate( 373 eq(TEST_OP_PACKAGE_NAME), 374 eq(userId), 375 eq(UserHandle.getCallingUserId()), 376 eq(authenticators)); 377 } 378 379 @Test testHasEnrolledBiometrics_callsBiometricServiceHasEnrolledBiometrics()380 public void testHasEnrolledBiometrics_callsBiometricServiceHasEnrolledBiometrics() 381 throws Exception { 382 setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */); 383 384 mAuthService = new AuthService(mContext, mInjector); 385 mAuthService.onStart(); 386 387 final int userId = 0; 388 final boolean expectedResult = true; 389 when(mBiometricService.hasEnrolledBiometrics(anyInt(), anyString())).thenReturn( 390 expectedResult); 391 392 final boolean result = mAuthService.mImpl.hasEnrolledBiometrics(userId, 393 TEST_OP_PACKAGE_NAME); 394 395 assertEquals(expectedResult, result); 396 waitForIdle(); 397 verify(mBiometricService).hasEnrolledBiometrics( 398 eq(userId), 399 eq(TEST_OP_PACKAGE_NAME)); 400 } 401 402 403 @Test testRegisterKeyguardCallback_callsBiometricServiceRegisterKeyguardCallback()404 public void testRegisterKeyguardCallback_callsBiometricServiceRegisterKeyguardCallback() 405 throws Exception { 406 setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */); 407 408 mAuthService = new AuthService(mContext, mInjector); 409 mAuthService.onStart(); 410 411 final IBiometricEnabledOnKeyguardCallback callback = 412 new IBiometricEnabledOnKeyguardCallback.Default(); 413 414 mAuthService.mImpl.registerEnabledOnKeyguardCallback(callback); 415 416 waitForIdle(); 417 verify(mBiometricService).registerEnabledOnKeyguardCallback( 418 eq(callback)); 419 } 420 setInternalAndTestBiometricPermissions( Context context, boolean hasPermission)421 private static void setInternalAndTestBiometricPermissions( 422 Context context, boolean hasPermission) { 423 for (String p : List.of(TEST_BIOMETRIC, MANAGE_BIOMETRIC, USE_BIOMETRIC_INTERNAL)) { 424 when(context.checkCallingPermission(eq(p))).thenReturn(hasPermission 425 ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); 426 when(context.checkCallingOrSelfPermission(eq(p))).thenReturn(hasPermission 427 ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); 428 final Stubber doPermCheck = 429 hasPermission ? doNothing() : doThrow(SecurityException.class); 430 doPermCheck.when(context).enforceCallingPermission(eq(p), any()); 431 doPermCheck.when(context).enforceCallingOrSelfPermission(eq(p), any()); 432 } 433 } 434 waitForIdle()435 private static void waitForIdle() { 436 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 437 } 438 } 439