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

Commit 4200b7ed authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Enumerate fingerprints/face for all profiles" into main

parents dcb9a0bf 844307de
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -51,3 +51,14 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}


flag {
  name: "internal_cleanup_for_all_profiles"
  namespace: "biometrics_framework"
  description: "Feature flag for scheduling internal cleanup for all profiles"
  bug: "441557507"
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}
+22 −25
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.server.biometrics.sensors.face.aidl;

import static android.hardware.face.FaceSensorConfigurations.getIFace;
import static android.hardware.face.FaceSensorConfigurations.remapFqName;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -90,6 +89,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;

/**
 * Provider for a single instance of the {@link IFace} HAL.
@@ -118,10 +118,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
    private final LockoutResetDispatcher mLockoutResetDispatcher;
    @NonNull
    private final UsageStats mUsageStats;
    @NonNull
    private final ActivityTaskManager mActivityTaskManager;
    @NonNull
    private final BiometricTaskStackListener mTaskStackListener;
    // for requests that do not use biometric prompt
    @NonNull
    private final AtomicLong mRequestCounter = new AtomicLong(0);
@@ -131,6 +127,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
    private final AuthSessionCoordinator mAuthSessionCoordinator;
    @NonNull
    private final BiometricHandlerProvider mBiometricHandlerProvider;
    @NonNull
    private final Function<String, IFace> mGetIFace;
    @Nullable
    private AuthenticationStatsCollector mAuthenticationStatsCollector;
    @Nullable
@@ -179,7 +177,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
            boolean resetLockoutRequiresChallenge) {
        this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName,
                lockoutResetDispatcher, biometricContext, null /* daemon */,
                BiometricHandlerProvider.getInstance(), resetLockoutRequiresChallenge,
                BiometricHandlerProvider.getInstance(), (fqname) -> getIFace(fqname),
                resetLockoutRequiresChallenge,
                false /* testHalEnabled */);
    }

@@ -192,6 +191,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
            @NonNull BiometricContext biometricContext,
            @Nullable IFace daemon,
            @NonNull BiometricHandlerProvider biometricHandlerProvider,
            @NonNull Function<String, IFace> getIFace,
            boolean resetLockoutRequiresChallenge,
            boolean testHalEnabled) {
        mContext = context;
@@ -202,11 +202,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
        mHandler = biometricHandlerProvider.getFaceHandler();
        mUsageStats = new UsageStats(context);
        mLockoutResetDispatcher = lockoutResetDispatcher;
        mActivityTaskManager = ActivityTaskManager.getInstance();
        mTaskStackListener = new BiometricTaskStackListener();
        mBiometricContext = biometricContext;
        mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
        mDaemon = daemon;
        mGetIFace = getIFace;
        mTestHalEnabled = testHalEnabled;
        mBiometricHandlerProvider = biometricHandlerProvider;

@@ -292,14 +291,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
        return TAG + "/" + mHalInstanceName;
    }

    boolean hasHalInstance() {
        if (mTestHalEnabled) {
            return true;
        }
        return ServiceManager.checkService(
                remapFqName(IFace.DESCRIPTOR + "/" + mHalInstanceName)) != null;
    }

    @Nullable
    @VisibleForTesting
    synchronized IFace getHalInstance() {
@@ -335,7 +326,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {

        Slog.d(getTag(), "Daemon was null, reconnecting");

        mDaemon = getIFace(IFace.DESCRIPTOR + "/" + mHalInstanceNameCurrent);
        mDaemon = mGetIFace.apply(IFace.DESCRIPTOR + "/" + mHalInstanceNameCurrent);
        if (mDaemon == null) {
            Slog.e(getTag(), "Unable to get daemon");
            return null;
@@ -349,7 +340,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {

        for (int i = 0; i < mFaceSensors.size(); i++) {
            final int sensorId = mFaceSensors.keyAt(i);
            if (Flags.internalCleanupForAllProfiles()) {
                processFaceForProfiles(sensorId);
            } else {
                scheduleLoadAuthenticatorIds(sensorId);
            }
            scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(),
                    null /* callback */);
        }
@@ -391,6 +386,15 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
        }
    }

    private void processFaceForProfiles(int sensorId) {
        for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
            if (user.id != ActivityManager.getCurrentUser()) {
                scheduleInternalCleanup(sensorId, user.id, null /* callback */);
            }
            scheduleLoadAuthenticatorIdsForUser(sensorId, user.id);
        }
    }

    /**
     * Schedules FaceGetAuthenticatorIdClient for specific sensor and user.
     */
@@ -897,13 +901,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
        mAuthenticationStatsCollector.sendFaceReEnrollNotification();
    }

    /**
     * Sends a fingerprint enroll notification.
     */
    public void sendFingerprintReEnrollNotification() {
        mAuthenticationStatsCollector.sendFingerprintReEnrollNotification();
    }

    /**
     * Return virtual hal AIDL interface if it is used for testing
     *
+21 −16
Original line number Diff line number Diff line
@@ -17,8 +17,6 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;

import static android.hardware.fingerprint.FingerprintManager.SENSOR_ID_ANY;
import static android.hardware.fingerprint.FingerprintSensorConfigurations.getIFingerprint;
import static android.hardware.fingerprint.FingerprintSensorConfigurations.remapFqName;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -43,6 +41,7 @@ import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorConfigurations;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -96,6 +95,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;

/**
 * Provider for a single instance of the {@link IFingerprint} HAL.
@@ -123,13 +123,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
    @NonNull
    private final LockoutResetDispatcher mLockoutResetDispatcher;
    @NonNull
    private final ActivityTaskManager mActivityTaskManager;
    @NonNull
    private final BiometricTaskStackListener mTaskStackListener;
    // for requests that do not use biometric prompt
    @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
    @NonNull private final BiometricContext mBiometricContext;
    @NonNull private final BiometricHandlerProvider mBiometricHandlerProvider;
    @NonNull private final Function<String, IFingerprint> mGetIFingerprint;
    @Nullable private IFingerprint mDaemon;
    @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
    private final AuthSessionCoordinator mAuthSessionCoordinator;
@@ -177,6 +176,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
        this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName,
                lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext,
                null /* daemon */, BiometricHandlerProvider.getInstance(),
                FingerprintSensorConfigurations::getIFingerprint,
                resetLockoutRequiresHardwareAuthToken, false /* testHalEnabled */);
    }

@@ -189,6 +189,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
            @NonNull BiometricContext biometricContext,
            @Nullable IFingerprint daemon,
            @NonNull BiometricHandlerProvider biometricHandlerProvider,
            @NonNull Function<String, IFingerprint> getIFingerprint,
            boolean resetLockoutRequiresHardwareAuthToken,
            boolean testHalEnabled) {
        mContext = context;
@@ -198,11 +199,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
        mFingerprintSensors = new SensorList<>(ActivityManager.getService());
        mHandler = biometricHandlerProvider.getFingerprintHandler();
        mLockoutResetDispatcher = lockoutResetDispatcher;
        mActivityTaskManager = ActivityTaskManager.getInstance();
        mTaskStackListener = new BiometricTaskStackListener();
        mBiometricContext = biometricContext;
        mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
        mDaemon = daemon;
        mGetIFingerprint = getIFingerprint;
        mTestHalEnabled = testHalEnabled;
        mBiometricHandlerProvider = biometricHandlerProvider;

@@ -289,15 +290,6 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
        return TAG + "/" + mHalInstanceName;
    }

    boolean hasHalInstance() {
        if (mTestHalEnabled) {
            return true;
        }
        return (ServiceManager.checkService(
                remapFqName(IFingerprint.DESCRIPTOR + "/" + mHalInstanceName))
                != null);
    }

    @Nullable
    @VisibleForTesting
    synchronized IFingerprint getHalInstance() {
@@ -333,7 +325,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi

        Slog.d(getTag(), "Daemon was null, reconnecting");

        mDaemon = getIFingerprint(IFingerprint.DESCRIPTOR + "/" + mHalInstanceNameCurrent);
        mDaemon = mGetIFingerprint.apply(IFingerprint.DESCRIPTOR + "/" + mHalInstanceNameCurrent);
        if (mDaemon == null) {
            Slog.e(getTag(), "Unable to get daemon");
            return null;
@@ -347,7 +339,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi

        for (int i = 0; i < mFingerprintSensors.size(); i++) {
            final int sensorId = mFingerprintSensors.keyAt(i);
            if (Flags.internalCleanupForAllProfiles()) {
                processFingerprintForProfiles(sensorId);
            } else {
                scheduleLoadAuthenticatorIds(sensorId);
            }
            scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(),
                    null /* callback */);
        }
@@ -418,6 +414,15 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
        }
    }

    private void processFingerprintForProfiles(int sensorId) {
        for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
            if (user.id != ActivityManager.getCurrentUser()) {
                scheduleInternalCleanup(sensorId, user.id, null /* callback */);
            }
            scheduleLoadAuthenticatorIdsForUser(sensorId, user.id);
        }
    }

    /**
     * Schedules FingerprintGetAuthenticatorIdClient for specific sensor and user.
     */
+39 −5
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.common.CommonProps;
@@ -49,6 +50,7 @@ import android.os.RemoteException;
import android.os.UserManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;

@@ -56,6 +58,7 @@ import androidx.test.filters.SmallTest;

import com.android.internal.R;
import com.android.server.biometrics.BiometricHandlerProvider;
import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationStateListeners;
@@ -74,7 +77,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;
import java.util.List;

@Presubmit
@SmallTest
@@ -85,6 +88,7 @@ public class FaceProviderTest {
            DeviceFlagsValueProvider.createCheckFlagsRule();

    private static final String TAG = "FaceProviderTest";
    private static final List<Integer> ALIVE_USERS = List.of(0, 1, 2);

    private static final float FRR_THRESHOLD = 0.2f;

@@ -110,6 +114,8 @@ public class FaceProviderTest {
    private BiometricScheduler<IFace, ISession> mScheduler;
    @Mock
    AuthSessionCoordinator mAuthSessionCoordinator;
    @Mock
    private IBinder mBinder;

    private final TestLooper mLooper = new TestLooper();
    private SensorProps[] mSensorProps;
@@ -121,9 +127,10 @@ public class FaceProviderTest {
        MockitoAnnotations.initMocks(this);

        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
        when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
        when(mUserManager.getAliveUsers()).thenReturn(
                ALIVE_USERS.stream().map(i -> new UserInfo(i, "", 0)).toList());
        when(mDaemon.createSession(anyInt(), anyInt(), any())).thenReturn(mock(ISession.class));

        when(mDaemon.asBinder()).thenReturn(mBinder);
        when(mContext.getResources()).thenReturn(mResources);
        when(mResources.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1))
                .thenReturn(FRR_THRESHOLD);
@@ -146,8 +153,10 @@ public class FaceProviderTest {

        mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback,
                mAuthenticationStateListeners, mSensorProps, TAG, mLockoutResetDispatcher,
                mBiometricContext, mDaemon, mBiometricHandlerProvider,
                mBiometricContext, mDaemon, mBiometricHandlerProvider, (fqName) -> mDaemon,
                false /* resetLockoutRequiresChallenge */, false /* testHalEnabled */);

        waitForIdle();
    }

    @Test
@@ -181,7 +190,8 @@ public class FaceProviderTest {
        mFaceProvider = new FaceProvider(mContext,
                mBiometricStateCallback, mAuthenticationStateListeners, hidlFaceSensorConfig, TAG,
                mLockoutResetDispatcher, mBiometricContext, mDaemon,
                mBiometricHandlerProvider, true /* resetLockoutRequiresChallenge */,
                mBiometricHandlerProvider, (fqName) -> mDaemon,
                true /* resetLockoutRequiresChallenge */,
                true /* testHalEnabled */);

        assertThat(mFaceProvider.mFaceSensors.get(faceId)
@@ -283,6 +293,30 @@ public class FaceProviderTest {
                anyBoolean());
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_INTERNAL_CLEANUP_FOR_ALL_PROFILES)
    public void testGetHalInstance_whenDaemonIsNull() {
        mFaceProvider.setTestHalEnabled(false);
        for (SensorProps sensor: mSensorProps) {
            mFaceProvider.mFaceSensors.get(sensor.commonProps.sensorId).setScheduler(mScheduler);
        }
        //Reset value of FaceProvider#mDaemon
        mFaceProvider.binderDied();

        waitForIdle();

        final IFace daemon = mFaceProvider.getHalInstance();

        waitForIdle();

        assertNotNull(daemon);
        verify(mScheduler, times(ALIVE_USERS.size() * mSensorProps.length))
                .scheduleClientMonitor(any(FaceInternalCleanupClient.class),
                        any(ClientMonitorCallback.class));
        verify(mScheduler, times(ALIVE_USERS.size() * mSensorProps.length))
                .scheduleClientMonitor(any(FaceGetAuthenticatorIdClient.class));
    }

    private void waitForIdle() {
        mLooper.dispatchAll();
    }
+44 −4
Original line number Diff line number Diff line
@@ -34,7 +34,9 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -52,12 +54,14 @@ import android.os.RemoteException;
import android.os.UserManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;

import androidx.test.filters.SmallTest;

import com.android.server.biometrics.BiometricHandlerProvider;
import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationStateListeners;
@@ -78,13 +82,16 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;
import java.util.List;

@Presubmit
@SmallTest
public class FingerprintProviderTest {

    private static final String TAG = "FingerprintProviderTest";
    private static final int CURRENT_USER_ID = ActivityManager.getCurrentUser();
    private static final List<Integer> ALIVE_USERS = List.of(CURRENT_USER_ID,
            CURRENT_USER_ID + 1, CURRENT_USER_ID + 2);

    @Rule
    public final CheckFlagsRule mCheckFlagsRule =
@@ -116,6 +123,8 @@ public class FingerprintProviderTest {
    private AuthSessionCoordinator mAuthSessionCoordinator;
    @Mock
    private BiometricScheduler<IFingerprint, ISession> mScheduler;
    @Mock
    private IBinder mBinder;

    private final TestLooper mLooper = new TestLooper();

@@ -130,8 +139,10 @@ public class FingerprintProviderTest {
        when(mContext.getResources()).thenReturn(mResources);
        when(mResources.obtainTypedArray(anyInt())).thenReturn(mock(TypedArray.class));
        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
        when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
        when(mUserManager.getAliveUsers()).thenReturn(
                ALIVE_USERS.stream().map(i -> new UserInfo(i, "", 0)).toList());
        when(mDaemon.createSession(anyInt(), anyInt(), any())).thenReturn(mock(ISession.class));
        when(mDaemon.asBinder()).thenReturn(mBinder);
        when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
        when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn(
                mBiometricCallbackHandler);
@@ -154,8 +165,10 @@ public class FingerprintProviderTest {
        mFingerprintProvider = new FingerprintProvider(mContext,
                mBiometricStateCallback, mAuthenticationStateListeners, mSensorProps, TAG,
                mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext,
                mDaemon, mBiometricHandlerProvider,
                mDaemon, mBiometricHandlerProvider, (fqName) -> mDaemon,
                false /* resetLockoutRequiresHardwareAuthToken */, true /* testHalEnabled */);

        waitForIdle();
    }

    @Test
@@ -187,6 +200,7 @@ public class FingerprintProviderTest {
                hidlFingerprintSensorConfigs, TAG, mLockoutResetDispatcher,
                mGestureAvailabilityDispatcher, mBiometricContext, mDaemon,
                mBiometricHandlerProvider,
                (fqName) -> mDaemon,
                false /* resetLockoutRequiresHardwareAuthToken */,
                true /* testHalEnabled */);

@@ -301,7 +315,7 @@ public class FingerprintProviderTest {
        mFingerprintProvider = new FingerprintProvider(mContext,
                mBiometricStateCallback, mAuthenticationStateListeners, mSensorProps, TAG,
                mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext,
                mDaemon, mBiometricHandlerProvider,
                mDaemon, mBiometricHandlerProvider, (fqName) -> mDaemon,
                false /* resetLockoutRequiresHardwareAuthToken */, true /* testHalEnabled */);
        waitForIdle();

@@ -313,6 +327,32 @@ public class FingerprintProviderTest {
        verify(mFingerprintHandler).sendMessageDelayed(any(), anyLong());
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_INTERNAL_CLEANUP_FOR_ALL_PROFILES)
    public void testGetHalInstance_whenDaemonIsNull() {
        mFingerprintProvider.setTestHalEnabled(false);
        for (SensorProps sensor : mSensorProps) {
            mFingerprintProvider.mFingerprintSensors.get(sensor.commonProps.sensorId).setScheduler(
                    mScheduler);

        }
        //Reset value of FingerprintProvider#mDaemon
        mFingerprintProvider.binderDied();

        waitForIdle();

        final IFingerprint daemon = mFingerprintProvider.getHalInstance();

        waitForIdle();

        assertNotNull(daemon);
        verify(mScheduler, times(ALIVE_USERS.size() * mSensorProps.length))
                .scheduleClientMonitor(any(FingerprintInternalCleanupClient.class),
                        any(ClientMonitorCallback.class));
        verify(mScheduler, times(ALIVE_USERS.size() * mSensorProps.length))
                .scheduleClientMonitor(any(FingerprintGetAuthenticatorIdClient.class));
    }

    private void waitForIdle() {
        mLooper.dispatchAll();
    }