Loading core/java/android/hardware/biometrics/BiometricManager.java +2 −1 Original line number Diff line number Diff line Loading @@ -688,7 +688,8 @@ public class BiometricManager { getEnrollmentStatus() { try { final List<BiometricEnrollmentStatusInternal> statusInternalList = mService.getEnrollmentStatusList(mContext.getOpPackageName()); mService.getEnrollmentStatusList(mContext.getUserId(), mContext.getOpPackageName()); return convertBiometricEnrollmentStatusInternalToMap(statusInternalList); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); Loading core/java/android/hardware/biometrics/IAuthService.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -72,7 +72,7 @@ interface IAuthService { boolean hasEnrolledBiometrics(int userId, String opPackageName); // Return the current biometrics enrollment status. List<BiometricEnrollmentStatusInternal> getEnrollmentStatusList(String opPackageName); List<BiometricEnrollmentStatusInternal> getEnrollmentStatusList(int userId, String opPackageName); // Register callback for when keyguard biometric eligibility changes. void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback); Loading services/core/java/com/android/server/biometrics/AuthService.java +11 −3 Original line number Diff line number Diff line Loading @@ -438,12 +438,20 @@ public class AuthService extends SystemService { } @Override public List<BiometricEnrollmentStatusInternal> getEnrollmentStatusList(String opPackageName) throws RemoteException { public List<BiometricEnrollmentStatusInternal> getEnrollmentStatusList(int userId, String opPackageName) throws RemoteException { checkBiometricAdvancedPermission(); // Only allow internal clients to call getEnrollmentStatusList with a different // userId. final int callingUserId = UserHandle.getCallingUserId(); if (userId != callingUserId) { checkInternalPermission(); } final long identity = Binder.clearCallingIdentity(); try { final int userId = UserHandle.myUserId(); final List<BiometricEnrollmentStatusInternal> enrollmentStatusList = new ArrayList<>(); final IFingerprintService fingerprintService = mInjector.getFingerprintService(); Loading services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java +93 −27 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.biometrics; import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED; import static android.Manifest.permission.TEST_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE; Loading @@ -34,6 +35,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; Loading Loading @@ -106,7 +108,8 @@ public class AuthServiceTest { public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Mock private Context mContext; Loading Loading @@ -522,9 +525,84 @@ public class AuthServiceTest { verify(mBiometricService).getLastAuthenticationTime(eq(mUserId), eq(authenticators)); } @Test public void testGetEnrollmentStatus_throwsSecurityException() throws Exception { setInternalAndTestBiometricPermissions(mContext, false /* hasPermission */); setSensorProperties(); mAuthService = new AuthService(mContext, mInjector); mAuthService.onStart(); assertThrows(SecurityException.class, () -> { mAuthService.mImpl.getEnrollmentStatusList(mUserId, TEST_OP_PACKAGE_NAME); }); } @Test public void testGetEnrollmentStatus_callsFingerprintAndFaceService() throws Exception { setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */); setSensorProperties(); mAuthService = new AuthService(mContext, mInjector); mAuthService.onStart(); final List<BiometricEnrollmentStatusInternal> statusList = mAuthService.mImpl.getEnrollmentStatusList(mUserId, TEST_OP_PACKAGE_NAME); waitForIdle(); assertEquals(BiometricManager.Authenticators.BIOMETRIC_STRONG, statusList.get( 0).getStatus().getStrength()); assertEquals(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE, statusList.get( 1).getStatus().getStrength()); verify(mFaceService).getEnrolledFaces(eq(0), eq(mUserId), eq(TEST_OP_PACKAGE_NAME)); verify(mFingerprintService).getEnrolledFingerprints(eq(mUserId), eq(TEST_OP_PACKAGE_NAME), eq("tag")); } @Test public void testGetEnrollmentStatus_withNonCallingId_throwsSecurityException() throws Exception { setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */); setPermission(mContext, USE_BIOMETRIC_INTERNAL, false /* hasPermission */); final @UserIdInt int nonCallingId = UserHandle.MIN_SECONDARY_USER_ID + 1; setSensorProperties(); mAuthService = new AuthService(mContext, mInjector); mAuthService.onStart(); assertThrows(SecurityException.class, () -> { mAuthService.mImpl.getEnrollmentStatusList(nonCallingId, TEST_OP_PACKAGE_NAME); }); } @Test public void testGetEnrollmentStatus_withNonCallingId_callsFingerprintAndFaceService() throws Exception { setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */); final @UserIdInt int nonCallingId = UserHandle.MIN_SECONDARY_USER_ID + 1; setSensorProperties(); mAuthService = new AuthService(mContext, mInjector); mAuthService.onStart(); final List<BiometricEnrollmentStatusInternal> statusList = mAuthService.mImpl.getEnrollmentStatusList(nonCallingId, TEST_OP_PACKAGE_NAME); waitForIdle(); assertEquals(BiometricManager.Authenticators.BIOMETRIC_STRONG, statusList.get( 0).getStatus().getStrength()); assertEquals(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE, statusList.get( 1).getStatus().getStrength()); verify(mFaceService, times(0)).getEnrolledFaces(eq(0), eq(mUserId), eq(TEST_OP_PACKAGE_NAME)); verify(mFingerprintService, times(0)).getEnrolledFingerprints(eq(mUserId), eq(TEST_OP_PACKAGE_NAME), eq("tag")); verify(mFaceService).getEnrolledFaces(eq(0), eq(nonCallingId), eq(TEST_OP_PACKAGE_NAME)); verify(mFingerprintService).getEnrolledFingerprints(eq(nonCallingId), eq(TEST_OP_PACKAGE_NAME), eq("tag")); } private void setSensorProperties() throws Exception { List<FaceSensorPropertiesInternal> faceProps = List.of(new FaceSensorPropertiesInternal( 0 /* id */, FaceSensorProperties.STRENGTH_CONVENIENCE, Loading @@ -546,37 +624,25 @@ public class AuthServiceTest { when(mFingerprintService.getSensorPropertiesInternal(eq(TEST_OP_PACKAGE_NAME))).thenReturn( fpProps); when(mContext.getAttributionTag()).thenReturn("tag"); mAuthService = new AuthService(mContext, mInjector); mAuthService.onStart(); final List<BiometricEnrollmentStatusInternal> statusList = mAuthService.mImpl.getEnrollmentStatusList(TEST_OP_PACKAGE_NAME); waitForIdle(); assertEquals(BiometricManager.Authenticators.BIOMETRIC_STRONG, statusList.get( 0).getStatus().getStrength()); assertEquals(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE, statusList.get( 1).getStatus().getStrength()); //getStatus().getEnrollmentCount() is tested in BiometricSimpleTests. verify(mFaceService).getEnrolledFaces(eq(0), eq(mUserId), eq(TEST_OP_PACKAGE_NAME)); verify(mFingerprintService).getEnrolledFingerprints(eq(mUserId), eq(TEST_OP_PACKAGE_NAME), eq("tag")); } private static void setInternalAndTestBiometricPermissions( Context context, boolean hasPermission) { for (String p : List.of(TEST_BIOMETRIC, MANAGE_BIOMETRIC, USE_BIOMETRIC_INTERNAL)) { when(context.checkCallingPermission(eq(p))).thenReturn(hasPermission for (String p : List.of(TEST_BIOMETRIC, MANAGE_BIOMETRIC, USE_BIOMETRIC_INTERNAL, SET_BIOMETRIC_DIALOG_ADVANCED)) { setPermission(context, p, hasPermission); } } private static void setPermission(Context context, String permission, boolean hasPermission) { when(context.checkCallingPermission(eq(permission))).thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); when(context.checkCallingOrSelfPermission(eq(p))).thenReturn(hasPermission when(context.checkCallingOrSelfPermission(eq(permission))).thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); final Stubber doPermCheck = hasPermission ? doNothing() : doThrow(SecurityException.class); doPermCheck.when(context).enforceCallingPermission(eq(p), any()); doPermCheck.when(context).enforceCallingOrSelfPermission(eq(p), any()); } doPermCheck.when(context).enforceCallingPermission(eq(permission), any()); doPermCheck.when(context).enforceCallingOrSelfPermission(eq(permission), any()); } private static void waitForIdle() { Loading Loading
core/java/android/hardware/biometrics/BiometricManager.java +2 −1 Original line number Diff line number Diff line Loading @@ -688,7 +688,8 @@ public class BiometricManager { getEnrollmentStatus() { try { final List<BiometricEnrollmentStatusInternal> statusInternalList = mService.getEnrollmentStatusList(mContext.getOpPackageName()); mService.getEnrollmentStatusList(mContext.getUserId(), mContext.getOpPackageName()); return convertBiometricEnrollmentStatusInternalToMap(statusInternalList); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); Loading
core/java/android/hardware/biometrics/IAuthService.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -72,7 +72,7 @@ interface IAuthService { boolean hasEnrolledBiometrics(int userId, String opPackageName); // Return the current biometrics enrollment status. List<BiometricEnrollmentStatusInternal> getEnrollmentStatusList(String opPackageName); List<BiometricEnrollmentStatusInternal> getEnrollmentStatusList(int userId, String opPackageName); // Register callback for when keyguard biometric eligibility changes. void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback); Loading
services/core/java/com/android/server/biometrics/AuthService.java +11 −3 Original line number Diff line number Diff line Loading @@ -438,12 +438,20 @@ public class AuthService extends SystemService { } @Override public List<BiometricEnrollmentStatusInternal> getEnrollmentStatusList(String opPackageName) throws RemoteException { public List<BiometricEnrollmentStatusInternal> getEnrollmentStatusList(int userId, String opPackageName) throws RemoteException { checkBiometricAdvancedPermission(); // Only allow internal clients to call getEnrollmentStatusList with a different // userId. final int callingUserId = UserHandle.getCallingUserId(); if (userId != callingUserId) { checkInternalPermission(); } final long identity = Binder.clearCallingIdentity(); try { final int userId = UserHandle.myUserId(); final List<BiometricEnrollmentStatusInternal> enrollmentStatusList = new ArrayList<>(); final IFingerprintService fingerprintService = mInjector.getFingerprintService(); Loading
services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java +93 −27 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.biometrics; import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED; import static android.Manifest.permission.TEST_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE; Loading @@ -34,6 +35,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; Loading Loading @@ -106,7 +108,8 @@ public class AuthServiceTest { public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Mock private Context mContext; Loading Loading @@ -522,9 +525,84 @@ public class AuthServiceTest { verify(mBiometricService).getLastAuthenticationTime(eq(mUserId), eq(authenticators)); } @Test public void testGetEnrollmentStatus_throwsSecurityException() throws Exception { setInternalAndTestBiometricPermissions(mContext, false /* hasPermission */); setSensorProperties(); mAuthService = new AuthService(mContext, mInjector); mAuthService.onStart(); assertThrows(SecurityException.class, () -> { mAuthService.mImpl.getEnrollmentStatusList(mUserId, TEST_OP_PACKAGE_NAME); }); } @Test public void testGetEnrollmentStatus_callsFingerprintAndFaceService() throws Exception { setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */); setSensorProperties(); mAuthService = new AuthService(mContext, mInjector); mAuthService.onStart(); final List<BiometricEnrollmentStatusInternal> statusList = mAuthService.mImpl.getEnrollmentStatusList(mUserId, TEST_OP_PACKAGE_NAME); waitForIdle(); assertEquals(BiometricManager.Authenticators.BIOMETRIC_STRONG, statusList.get( 0).getStatus().getStrength()); assertEquals(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE, statusList.get( 1).getStatus().getStrength()); verify(mFaceService).getEnrolledFaces(eq(0), eq(mUserId), eq(TEST_OP_PACKAGE_NAME)); verify(mFingerprintService).getEnrolledFingerprints(eq(mUserId), eq(TEST_OP_PACKAGE_NAME), eq("tag")); } @Test public void testGetEnrollmentStatus_withNonCallingId_throwsSecurityException() throws Exception { setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */); setPermission(mContext, USE_BIOMETRIC_INTERNAL, false /* hasPermission */); final @UserIdInt int nonCallingId = UserHandle.MIN_SECONDARY_USER_ID + 1; setSensorProperties(); mAuthService = new AuthService(mContext, mInjector); mAuthService.onStart(); assertThrows(SecurityException.class, () -> { mAuthService.mImpl.getEnrollmentStatusList(nonCallingId, TEST_OP_PACKAGE_NAME); }); } @Test public void testGetEnrollmentStatus_withNonCallingId_callsFingerprintAndFaceService() throws Exception { setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */); final @UserIdInt int nonCallingId = UserHandle.MIN_SECONDARY_USER_ID + 1; setSensorProperties(); mAuthService = new AuthService(mContext, mInjector); mAuthService.onStart(); final List<BiometricEnrollmentStatusInternal> statusList = mAuthService.mImpl.getEnrollmentStatusList(nonCallingId, TEST_OP_PACKAGE_NAME); waitForIdle(); assertEquals(BiometricManager.Authenticators.BIOMETRIC_STRONG, statusList.get( 0).getStatus().getStrength()); assertEquals(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE, statusList.get( 1).getStatus().getStrength()); verify(mFaceService, times(0)).getEnrolledFaces(eq(0), eq(mUserId), eq(TEST_OP_PACKAGE_NAME)); verify(mFingerprintService, times(0)).getEnrolledFingerprints(eq(mUserId), eq(TEST_OP_PACKAGE_NAME), eq("tag")); verify(mFaceService).getEnrolledFaces(eq(0), eq(nonCallingId), eq(TEST_OP_PACKAGE_NAME)); verify(mFingerprintService).getEnrolledFingerprints(eq(nonCallingId), eq(TEST_OP_PACKAGE_NAME), eq("tag")); } private void setSensorProperties() throws Exception { List<FaceSensorPropertiesInternal> faceProps = List.of(new FaceSensorPropertiesInternal( 0 /* id */, FaceSensorProperties.STRENGTH_CONVENIENCE, Loading @@ -546,37 +624,25 @@ public class AuthServiceTest { when(mFingerprintService.getSensorPropertiesInternal(eq(TEST_OP_PACKAGE_NAME))).thenReturn( fpProps); when(mContext.getAttributionTag()).thenReturn("tag"); mAuthService = new AuthService(mContext, mInjector); mAuthService.onStart(); final List<BiometricEnrollmentStatusInternal> statusList = mAuthService.mImpl.getEnrollmentStatusList(TEST_OP_PACKAGE_NAME); waitForIdle(); assertEquals(BiometricManager.Authenticators.BIOMETRIC_STRONG, statusList.get( 0).getStatus().getStrength()); assertEquals(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE, statusList.get( 1).getStatus().getStrength()); //getStatus().getEnrollmentCount() is tested in BiometricSimpleTests. verify(mFaceService).getEnrolledFaces(eq(0), eq(mUserId), eq(TEST_OP_PACKAGE_NAME)); verify(mFingerprintService).getEnrolledFingerprints(eq(mUserId), eq(TEST_OP_PACKAGE_NAME), eq("tag")); } private static void setInternalAndTestBiometricPermissions( Context context, boolean hasPermission) { for (String p : List.of(TEST_BIOMETRIC, MANAGE_BIOMETRIC, USE_BIOMETRIC_INTERNAL)) { when(context.checkCallingPermission(eq(p))).thenReturn(hasPermission for (String p : List.of(TEST_BIOMETRIC, MANAGE_BIOMETRIC, USE_BIOMETRIC_INTERNAL, SET_BIOMETRIC_DIALOG_ADVANCED)) { setPermission(context, p, hasPermission); } } private static void setPermission(Context context, String permission, boolean hasPermission) { when(context.checkCallingPermission(eq(permission))).thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); when(context.checkCallingOrSelfPermission(eq(p))).thenReturn(hasPermission when(context.checkCallingOrSelfPermission(eq(permission))).thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); final Stubber doPermCheck = hasPermission ? doNothing() : doThrow(SecurityException.class); doPermCheck.when(context).enforceCallingPermission(eq(p), any()); doPermCheck.when(context).enforceCallingOrSelfPermission(eq(p), any()); } doPermCheck.when(context).enforceCallingPermission(eq(permission), any()); doPermCheck.when(context).enforceCallingOrSelfPermission(eq(permission), any()); } private static void waitForIdle() { Loading