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

Commit 2f22b558 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

Update IFingerprint providers for CTS

1) Adds proto dump for FingerprintProvider
2) Moves HAL ISessionCallback to static class
3) Adds AIDL-side implementation for TestApi
4) Rename TestSession to BiometricTestSessionImpl, since AIDL interface
   also has a notion of session (ISession), which is unrelated.
5) Add missing start() method for FingerprintGetAuthenticatorIdClient

Fixes: 170518383
Test: atest FingerprintServiceTest
Change-Id: I530f45d794fb792fc2a64a403376b9ce2fc5f626
parent a524fc37
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -396,6 +396,8 @@ public class FingerprintService extends SystemService {
                    for (ServiceProvider provider : mServiceProviders) {
                        for (FingerprintSensorPropertiesInternal props
                                : provider.getSensorProperties()) {
                            pw.println("Dumping for sensorId: " + props.sensorId
                                    + ", provider: " + provider.getClass().getSimpleName());
                            provider.dumpInternal(props.sensorId, pw);
                        }
                    }
@@ -613,7 +615,7 @@ public class FingerprintService extends SystemService {
                try {
                    final SensorProps[] props = fp.getSensorProps();
                    final FingerprintProvider provider =
                            new FingerprintProvider(getContext(), props, fqName,
                            new FingerprintProvider(getContext(), props, instance,
                                    mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
                    mServiceProviders.add(provider);
                } catch (RemoteException e) {
+186 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.biometrics.sensors.fingerprint.aidl;

import static android.Manifest.permission.TEST_BIOMETRIC;

import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.util.Slog;

import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

/**
 * A test session implementation for {@link FingerprintProvider}. See
 * {@link android.hardware.biometrics.BiometricTestSession}.
 */
class BiometricTestSessionImpl extends ITestSession.Stub {

    private static final String TAG = "BiometricTestSessionImpl";

    @NonNull private final Context mContext;
    private final int mSensorId;
    @NonNull private final FingerprintProvider mProvider;
    @NonNull private final Sensor mSensor;
    @NonNull private final Set<Integer> mEnrollmentIds;
    @NonNull private final Random mRandom;

    /**
     * Internal receiver currently only used for enroll. Results do not need to be forwarded to the
     * test, since enrollment is a platform-only API. The authentication path is tested through
     * the public FingerprintManager APIs and does not use this receiver.
     */
    private final IFingerprintServiceReceiver mReceiver = new IFingerprintServiceReceiver.Stub() {
        @Override
        public void onEnrollResult(Fingerprint fp, int remaining) {

        }

        @Override
        public void onAcquired(int acquiredInfo, int vendorCode) {

        }

        @Override
        public void onAuthenticationSucceeded(Fingerprint fp, int userId,
                boolean isStrongBiometric) {

        }

        @Override
        public void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) {

        }

        @Override
        public void onAuthenticationFailed() {

        }

        @Override
        public void onError(int error, int vendorCode) {

        }

        @Override
        public void onRemoved(Fingerprint fp, int remaining) {

        }

        @Override
        public void onChallengeGenerated(int sensorId, long challenge) {

        }
    };

    BiometricTestSessionImpl(@NonNull Context context, int sensorId,
            @NonNull FingerprintProvider provider, @NonNull Sensor sensor) {
        mContext = context;
        mSensorId = sensorId;
        mProvider = provider;
        mSensor = sensor;
        mEnrollmentIds = new HashSet<>();
        mRandom = new Random();
    }

    @Override
    public void setTestHalEnabled(boolean enabled) {
        Utils.checkPermission(mContext, TEST_BIOMETRIC);

        mProvider.setTestHalEnabled(enabled);
        mSensor.setTestHalEnabled(enabled);
    }

    @Override
    public void startEnroll(int userId) {
        Utils.checkPermission(mContext, TEST_BIOMETRIC);

        mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
                mContext.getOpPackageName(), null /* surface */);
    }

    @Override
    public void finishEnroll(int userId) {
        Utils.checkPermission(mContext, TEST_BIOMETRIC);

        int nextRandomId = mRandom.nextInt();
        while (mEnrollmentIds.contains(nextRandomId)) {
            nextRandomId = mRandom.nextInt();
        }

        mEnrollmentIds.add(nextRandomId);
        mSensor.getSessionForUser(userId).mHalSessionCallback
                .onEnrollmentProgress(nextRandomId, 0 /* remaining */);
    }

    @Override
    public void acceptAuthentication(int userId)  {
        Utils.checkPermission(mContext, TEST_BIOMETRIC);

        // Fake authentication with any of the existing fingers
        List<Fingerprint> fingerprints = FingerprintUtils.getInstance()
                .getBiometricsForUser(mContext, userId);
        if (fingerprints.isEmpty()) {
            Slog.w(TAG, "No fingerprints, returning");
            return;
        }
        final int fid = fingerprints.get(0).getBiometricId();
        mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationSucceeded(fid,
                HardwareAuthTokenUtils.toHardwareAuthToken(new byte[69]));
    }

    @Override
    public void rejectAuthentication(int userId)  {
        Utils.checkPermission(mContext, TEST_BIOMETRIC);

        mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFailed();
    }

    @Override
    public void notifyAcquired(int userId, int acquireInfo)  {
        Utils.checkPermission(mContext, TEST_BIOMETRIC);

        mSensor.getSessionForUser(userId).mHalSessionCallback
                .onAcquired((byte) acquireInfo, 0 /* vendorCode */);
    }

    @Override
    public void notifyError(int userId, int errorCode)  {
        Utils.checkPermission(mContext, TEST_BIOMETRIC);

        mSensor.getSessionForUser(userId).mHalSessionCallback.onError((byte) errorCode,
                0 /* vendorCode */);
    }

    @Override
    public void cleanupInternalState(int userId)  {
        Utils.checkPermission(mContext, TEST_BIOMETRIC);

        mProvider.scheduleInternalCleanup(mSensorId, userId);
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -47,6 +47,11 @@ class FingerprintGetAuthenticatorIdClient extends ClientMonitor<ISession> {
        // Nothing to do here
    }

    public void start(@NonNull Callback callback) {
        super.start(callback);
        startHalOperation();
    }

    @Override
    protected void startHalOperation() {
        try {
+20 −4
Original line number Diff line number Diff line
@@ -64,6 +64,8 @@ import java.util.List;
@SuppressWarnings("deprecation")
public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider {

    private boolean mTestHalEnabled;

    @NonNull private final Context mContext;
    @NonNull private final String mHalInstanceName;
    @NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
@@ -134,7 +136,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
                            prop.commonProps.maxEnrollmentsPerUser,
                            prop.sensorType,
                            true /* resetLockoutRequiresHardwareAuthToken */);
            final Sensor sensor = new Sensor(getTag() + "/" + sensorId, mContext, mHandler,
            final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
                    internalProp, gestureAvailabilityDispatcher);

            mSensors.put(sensorId, sensor);
@@ -148,6 +150,13 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi

    @Nullable
    private synchronized IFingerprint getHalInstance() {
        if (mTestHalEnabled) {
            // Enabling the test HAL for a single sensor in a multi-sensor HAL currently enables
            // the test HAL for all sensors under that HAL. This can be updated in the future if
            // necessary.
            return new TestHal();
        }

        if (mDaemon != null) {
            return mDaemon;
        }
@@ -155,7 +164,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
        Slog.d(getTag(), "Daemon was null, reconnecting");

        mDaemon = IFingerprint.Stub.asInterface(
                ServiceManager.waitForDeclaredService(mHalInstanceName));
                ServiceManager.waitForDeclaredService(IFingerprint.DESCRIPTOR
                        + "/" + mHalInstanceName));
        if (mDaemon == null) {
            Slog.e(getTag(), "Unable to get daemon");
            return null;
@@ -564,7 +574,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi

    @Override
    public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {

        if (mSensors.contains(sensorId)) {
            mSensors.get(sensorId).dumpProtoState(sensorId, proto);
        }
    }

    @Override
@@ -580,7 +592,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
    @NonNull
    @Override
    public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
        return null;
        return mSensors.get(sensorId).createTestSession();
    }

    @Override
@@ -595,4 +607,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
            }
        });
    }

    void setTestHalEnabled(boolean enabled) {
        mTestHalEnabled = enabled;
    }
}
+358 −269
Original line number Diff line number Diff line
@@ -19,7 +19,9 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.fingerprint.Error;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.ISession;
@@ -31,11 +33,16 @@ import android.hardware.keymaster.HardwareAuthToken;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;

import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.fingerprint.FingerprintServiceStateProto;
import com.android.server.biometrics.fingerprint.SensorStateProto;
import com.android.server.biometrics.fingerprint.UserStateProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -48,9 +55,7 @@ import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
@@ -59,7 +64,11 @@ import java.util.Map;
 */
@SuppressWarnings("deprecation")
class Sensor implements IBinder.DeathRecipient {

    private boolean mTestHalEnabled;

    @NonNull private final String mTag;
    @NonNull private final FingerprintProvider mProvider;
    @NonNull private final Context mContext;
    @NonNull private final Handler mHandler;
    @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
@@ -91,51 +100,54 @@ class Sensor implements IBinder.DeathRecipient {
        });
    }

    private static class Session {
    static class Session {
        @NonNull private final String mTag;
        @NonNull private final ISession mSession;
        private final int mUserId;
        private final ISessionCallback mSessionCallback;
        @NonNull final HalSessionCallback mHalSessionCallback;

        Session(@NonNull String tag, @NonNull ISession session, int userId,
                @NonNull ISessionCallback sessionCallback) {
                @NonNull HalSessionCallback halSessionCallback) {
            mTag = tag;
            mSession = session;
            mUserId = userId;
            mSessionCallback = sessionCallback;
            mHalSessionCallback = halSessionCallback;
            Slog.d(mTag, "New session created for user: " + userId);
        }
    }

    Sensor(@NonNull String tag, @NonNull Context context, @NonNull Handler handler,
            @NonNull FingerprintSensorPropertiesInternal sensorProperties,
            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
        mTag = tag;
        mContext = context;
        mHandler = handler;
        mSensorProperties = sensorProperties;
        mScheduler = new BiometricScheduler(tag, gestureAvailabilityDispatcher);
        mLockoutCache = new LockoutCache();
        mAuthenticatorIds = new HashMap<>();
        mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null;
    }
    static class HalSessionCallback extends ISessionCallback.Stub {

    @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
        return mLazySession;
        /**
         * Interface to sends results to the HalSessionCallback's owner.
         */
        public interface Callback {
            /**
             * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
             */
            void onHardwareUnavailable();
        }

    @NonNull FingerprintSensorPropertiesInternal getSensorProperties() {
        return mSensorProperties;
    }
        @NonNull private final Context mContext;
        @NonNull private final Handler mHandler;
        @NonNull private final String mTag;
        @NonNull private final BiometricScheduler mScheduler;
        private final int mSensorId;
        private final int mUserId;
        @NonNull private final Callback mCallback;

    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    boolean hasSessionForUser(int userId) {
        return mCurrentSession != null && mCurrentSession.mUserId == userId;
        HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
                @NonNull BiometricScheduler scheduler, int sensorId, int userId,
                @NonNull Callback callback) {
            mContext = context;
            mHandler = handler;
            mTag = tag;
            mScheduler = scheduler;
            mSensorId = sensorId;
            mUserId = userId;
            mCallback = callback;
        }

    void createNewSession(@NonNull IFingerprint daemon, int sensorId, int userId)
            throws RemoteException {
        final ISessionCallback callback = new ISessionCallback.Stub() {
        @Override
        public void onStateChanged(int cookie, byte state) {
            // TODO(b/162973174)
@@ -153,7 +165,7 @@ class Sensor implements IBinder.DeathRecipient {

                final FingerprintGenerateChallengeClient generateChallengeClient =
                        (FingerprintGenerateChallengeClient) client;
                    generateChallengeClient.onChallengeGenerated(sensorId, userId, challenge);
                generateChallengeClient.onChallengeGenerated(mSensorId, mUserId, challenge);
            });
        }

@@ -169,7 +181,7 @@ class Sensor implements IBinder.DeathRecipient {

                final FingerprintRevokeChallengeClient revokeChallengeClient =
                        (FingerprintRevokeChallengeClient) client;
                    revokeChallengeClient.onChallengeRevoked(sensorId, userId, challenge);
                revokeChallengeClient.onChallengeRevoked(mSensorId, mUserId, challenge);
            });
        }

@@ -206,8 +218,7 @@ class Sensor implements IBinder.DeathRecipient {
                interruptable.onError(error, vendorCode);

                if (error == Error.HW_UNAVAILABLE) {
                        Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
                        mCurrentSession = null;
                    mCallback.onHardwareUnavailable();
                }
            });
        }
@@ -223,9 +234,9 @@ class Sensor implements IBinder.DeathRecipient {
                }

                final int currentUserId = client.getTargetUserId();
                    final CharSequence name = FingerprintUtils.getInstance(sensorId)
                final CharSequence name = FingerprintUtils.getInstance(mSensorId)
                        .getUniqueName(mContext, currentUserId);
                    final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, sensorId);
                final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, mSensorId);

                final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
                enrollClient.onEnrollResult(fingerprint, remaining);
@@ -244,7 +255,7 @@ class Sensor implements IBinder.DeathRecipient {

                final AuthenticationConsumer authenticationConsumer =
                        (AuthenticationConsumer) client;
                    final Fingerprint fp = new Fingerprint("", enrollmentId, sensorId);
                final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId);
                final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
                final ArrayList<Byte> byteList = new ArrayList<>();
                for (byte b : byteArray) {
@@ -267,7 +278,7 @@ class Sensor implements IBinder.DeathRecipient {

                final AuthenticationConsumer authenticationConsumer =
                        (AuthenticationConsumer) client;
                    final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, sensorId);
                final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, mSensorId);
                authenticationConsumer
                        .onAuthenticated(fp, false /* authenticated */, null /* hat */);
            });
@@ -349,7 +360,7 @@ class Sensor implements IBinder.DeathRecipient {
                        (EnumerateConsumer) client;
                if (enrollmentIds.length > 0) {
                    for (int i = 0; i < enrollmentIds.length; i++) {
                            final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId);
                        final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
                        enumerateConsumer.onEnumerationResult(fp, enrollmentIds.length - i - 1);
                    }
                } else {
@@ -371,7 +382,7 @@ class Sensor implements IBinder.DeathRecipient {
                final RemovalConsumer removalConsumer = (RemovalConsumer) client;
                if (enrollmentIds.length > 0) {
                    for (int i  = 0; i < enrollmentIds.length; i++) {
                            final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId);
                        final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
                        removalConsumer.onRemoved(fp, enrollmentIds.length - i - 1);
                    }
                } else {
@@ -400,11 +411,66 @@ class Sensor implements IBinder.DeathRecipient {
        public void onAuthenticatorIdInvalidated() {
            // TODO(159667191)
        }
    }

    Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
            @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
        mTag = tag;
        mProvider = provider;
        mContext = context;
        mHandler = handler;
        mSensorProperties = sensorProperties;
        mScheduler = new BiometricScheduler(tag, gestureAvailabilityDispatcher);
        mLockoutCache = new LockoutCache();
        mAuthenticatorIds = new HashMap<>();
        mLazySession = () -> {
            if (mTestHalEnabled) {
                return new TestSession(mCurrentSession.mHalSessionCallback);
            } else {
                return mCurrentSession != null ? mCurrentSession.mSession : null;
            }
        };
    }

    @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
        return mLazySession;
    }

    @NonNull FingerprintSensorPropertiesInternal getSensorProperties() {
        return mSensorProperties;
    }

    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    boolean hasSessionForUser(int userId) {
        return mCurrentSession != null && mCurrentSession.mUserId == userId;
    }

    @Nullable Session getSessionForUser(int userId) {
        if (mCurrentSession != null && mCurrentSession.mUserId == userId) {
            return mCurrentSession;
        } else {
            return null;
        }
    }

    @NonNull ITestSession createTestSession() {
        return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, mProvider, this);
    }

    void createNewSession(@NonNull IFingerprint daemon, int sensorId, int userId)
            throws RemoteException {

        final HalSessionCallback.Callback callback = () -> {
            Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
            mCurrentSession = null;
        };
        final HalSessionCallback resultController = new HalSessionCallback(mContext, mHandler,
                mTag, mScheduler, sensorId, userId, callback);

        final ISession newSession = daemon.createSession(sensorId, userId, callback);
        final ISession newSession = daemon.createSession(sensorId, userId, resultController);
        newSession.asBinder().linkToDeath(this, 0 /* flags */);
        mCurrentSession = new Session(mTag, newSession, userId, callback);
        mCurrentSession = new Session(mTag, newSession, userId, resultController);
    }

    @NonNull BiometricScheduler getScheduler() {
@@ -418,4 +484,27 @@ class Sensor implements IBinder.DeathRecipient {
    @NonNull Map<Integer, Long> getAuthenticatorIds() {
        return mAuthenticatorIds;
    }

    void setTestHalEnabled(boolean enabled) {
        mTestHalEnabled = enabled;
    }

    void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
        final long sensorToken = proto.start(FingerprintServiceStateProto.SENSOR_STATES);

        proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
        proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);

        for (UserInfo user : UserManager.get(mContext).getUsers()) {
            final int userId = user.getUserHandle().getIdentifier();

            final long userToken = proto.start(SensorStateProto.USER_STATES);
            proto.write(UserStateProto.USER_ID, userId);
            proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getInstance()
                    .getBiometricsForUser(mContext, userId).size());
            proto.end(userToken);
        }

        proto.end(sensorToken);
    }
}
Loading