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

Commit d6d23e13 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

Try to ensure tests run sequentially

Asynchronous operations that are performed in close() (such as
cleanupInternalState()) should complete before close() finishes.
Otherwise, we risk subsequent tests starting and causing racey
conditions while cleanup has not finished yet.

Fixes: 180192766
Test: atest CtsBiometricsTestCases
Test: atest com.android.server.biometrics
Change-Id: I3fdb3fe0be59e826d0c14e14a2590ccdde568774
parent b2888343
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -237,7 +237,8 @@ public class BiometricManager {
    public BiometricTestSession createTestSession(int sensorId) {
        try {
            return new BiometricTestSession(mContext, sensorId,
                    mService.createTestSession(sensorId, mContext.getOpPackageName()));
                    (context, sensorId1, callback) -> mService
                            .createTestSession(sensorId1, callback, context.getOpPackageName()));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+66 −9
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.hardware.biometrics;
import static android.Manifest.permission.TEST_BIOMETRIC;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.content.Context;
@@ -27,6 +28,9 @@ import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Common set of interfaces to test biometric-related APIs, including {@link BiometricPrompt} and
 * {@link android.hardware.fingerprint.FingerprintManager}.
@@ -36,22 +40,58 @@ import android.util.Log;
public class BiometricTestSession implements AutoCloseable {
    private static final String TAG = "BiometricTestSession";

    /**
     * @hide
     */
    public interface TestSessionProvider {
        @NonNull
        ITestSession createTestSession(@NonNull Context context, int sensorId,
                @NonNull ITestSessionCallback callback) throws RemoteException;
    }

    private final Context mContext;
    private final int mSensorId;
    private final ITestSession mTestSession;

    // Keep track of users that were tested, which need to be cleaned up when finishing.
    private final ArraySet<Integer> mTestedUsers;
    @NonNull private final ArraySet<Integer> mTestedUsers;

    // Track the users currently cleaning up, and provide a latch that gets notified when all
    // users have finished cleaning up. This is an imperfect system, as there can technically be
    // multiple cleanups per user. Theoretically we should track the cleanup's BaseClientMonitor's
    // unique ID, but it's complicated to plumb it through. This should be fine for now.
    @Nullable private CountDownLatch mCloseLatch;
    @NonNull private final ArraySet<Integer> mUsersCleaningUp;

    private final ITestSessionCallback mCallback = new ITestSessionCallback.Stub() {
        @Override
        public void onCleanupStarted(int userId) {
            Log.d(TAG, "onCleanupStarted, sensor: " + mSensorId + ", userId: " + userId);
        }

        @Override
        public void onCleanupFinished(int userId) {
            Log.d(TAG, "onCleanupFinished, sensor: " + mSensorId
                    + ", userId: " + userId
                    + ", remaining users: " + mUsersCleaningUp.size());
            mUsersCleaningUp.remove(userId);

            if (mUsersCleaningUp.isEmpty() && mCloseLatch != null) {
                mCloseLatch.countDown();
            }
        }
    };

    /**
     * @hide
     */
    public BiometricTestSession(@NonNull Context context, int sensorId,
            @NonNull ITestSession testSession) {
            @NonNull TestSessionProvider testSessionProvider) throws RemoteException {
        mContext = context;
        mSensorId = sensorId;
        mTestSession = testSession;
        mTestSession = testSessionProvider.createTestSession(context, sensorId, mCallback);
        mTestedUsers = new ArraySet<>();
        mUsersCleaningUp = new ArraySet<>();
        setTestHalEnabled(true);
    }

@@ -176,6 +216,11 @@ public class BiometricTestSession implements AutoCloseable {
    @RequiresPermission(TEST_BIOMETRIC)
    public void cleanupInternalState(int userId) {
        try {
            if (mUsersCleaningUp.contains(userId)) {
                Log.w(TAG, "Cleanup already in progress for user: " + userId);
            }

            mUsersCleaningUp.add(userId);
            mTestSession.cleanupInternalState(userId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
@@ -185,12 +230,24 @@ public class BiometricTestSession implements AutoCloseable {
    @Override
    @RequiresPermission(TEST_BIOMETRIC)
    public void close() {
        // Disable the test HAL first, so that enumerate is run on the real HAL, which should have
        // no enrollments. Test-only framework enrollments will be deleted.
        setTestHalEnabled(false);

        // Cleanup can be performed using the test HAL, since it always responds to enumerate with
        // zero enrollments.
        if (!mTestedUsers.isEmpty()) {
            mCloseLatch = new CountDownLatch(1);
            for (int user : mTestedUsers) {
                cleanupInternalState(user);
            }

            try {
                Log.d(TAG, "Awaiting latch...");
                mCloseLatch.await(10, TimeUnit.SECONDS);
                Log.d(TAG, "Finished awaiting");
            } catch (InterruptedException e) {
                Log.e(TAG, "Latch interrupted", e);
            }
        }

        // Disable the test HAL after the sensor becomes idle.
        setTestHalEnabled(false);
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;

@@ -32,7 +33,7 @@ import android.hardware.biometrics.SensorPropertiesInternal;
 */
interface IAuthService {
    // Creates a test session with the specified sensorId
    ITestSession createTestSession(int sensorId, String opPackageName);
    ITestSession createTestSession(int sensorId, ITestSessionCallback callback, String opPackageName);

    // Retrieve static sensor properties for all biometric sensors
    List<SensorPropertiesInternal> getSensorProperties(String opPackageName);
+2 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
@@ -32,7 +33,7 @@ import android.hardware.face.Face;
interface IBiometricAuthenticator {

    // Creates a test session
    ITestSession createTestSession(String opPackageName);
    ITestSession createTestSession(ITestSessionCallback callback, String opPackageName);

    // Retrieve static sensor properties
    SensorPropertiesInternal getSensorProperties(String opPackageName);
+2 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;

@@ -30,7 +31,7 @@ import android.hardware.biometrics.SensorPropertiesInternal;
 */
interface IBiometricService {
    // Creates a test session with the specified sensorId
    ITestSession createTestSession(int sensorId, String opPackageName);
    ITestSession createTestSession(int sensorId, ITestSessionCallback callback, String opPackageName);

    // Retrieve static sensor properties for all biometric sensors
    List<SensorPropertiesInternal> getSensorProperties(String opPackageName);
Loading