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