Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e26e891a authored by Hao Dong's avatar Hao Dong
Browse files

Fix wrong calling user id issue for enrollment status list.

getCallingUserId() should be used instead of myUserId(). This CL adds a
parameter userId in internal AuthService API to enable internal clients
to call with a different userId.

Bug: 434128940
Test: atest AuthServiceTest
Flag: android.hardware.biometrics.move_fm_api_to_bm

Change-Id: I5cef4945bf75dbf6977e2c70f95635940fc02ce8
parent e591d195
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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();
+1 −1
Original line number Diff line number Diff line
@@ -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);
+11 −3
Original line number Diff line number Diff line
@@ -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();
+93 −27
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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;
@@ -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,
@@ -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() {