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

Commit 86e98080 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

4/n: Add and use Face AIDL start/stop user clients

Bug: 181984005
Test: atest CtsBiometricsTestCases
Test: atest com.android.server.biometrics
Change-Id: I1b62b12d3b99fd572450316168b6cea2c7a06e16
parent bcb85a9a
Loading
Loading
Loading
Loading
+64 −223
Original line number Diff line number Diff line
@@ -78,7 +78,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
    @NonNull private final String mHalInstanceName;
    @NonNull @VisibleForTesting
    final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
    @NonNull private final HalClientMonitor.LazyDaemon<IFace> mLazyDaemon;
    @NonNull private final Handler mHandler;
    @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
    @NonNull private final UsageStats mUsageStats;
@@ -126,7 +125,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
        mContext = context;
        mHalInstanceName = halInstanceName;
        mSensors = new SparseArray<>();
        mLazyDaemon = this::getHalInstance;
        mHandler = new Handler(Looper.getMainLooper());
        mUsageStats = new UsageStats(context);
        mLockoutResetDispatcher = lockoutResetDispatcher;
@@ -163,7 +161,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
    }

    @Nullable
    private synchronized IFace getHalInstance() {
    @VisibleForTesting
    synchronized IFace getHalInstance() {
        if (mTestHalEnabled) {
            return new TestHal();
        }
@@ -214,22 +213,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
        mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client, callback);
    }

    private void createNewSessionWithoutHandler(@NonNull IFace daemon, int sensorId,
            int userId) throws RemoteException {
        // Note that per IFace createSession contract, this method will block until all
        // existing operations are canceled/finished. However, also note that this is fine, since
        // this method "withoutHandler" means it should only ever be invoked from the worker thread,
        // so callers will never be blocked.
        mSensors.get(sensorId).createNewSession(daemon, sensorId, userId);

        if (FaceUtils.getInstance(sensorId).isInvalidationInProgress(mContext, userId)) {
            Slog.w(getTag(), "Scheduling unfinished invalidation request for sensor: " + sensorId
                    + ", user: " + userId);
            scheduleInvalidationRequest(sensorId, userId);
        }
    }


    private void scheduleLoadAuthenticatorIds(int sensorId) {
        for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
            scheduleLoadAuthenticatorIdsForUser(sensorId, user.id);
@@ -238,32 +221,16 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {

    private void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
        mHandler.post(() -> {
            final IFace daemon = getHalInstance();
            if (daemon == null) {
                Slog.e(getTag(), "Null daemon during loadAuthenticatorIds, sensorId: " + sensorId);
                return;
            }

            try {
                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
                    createNewSessionWithoutHandler(daemon, sensorId, userId);
                }

            final FaceGetAuthenticatorIdClient client = new FaceGetAuthenticatorIdClient(
                    mContext, mSensors.get(sensorId).getLazySession(), userId,
                    mContext.getOpPackageName(), sensorId,
                    mSensors.get(sensorId).getAuthenticatorIds());

            scheduleForSensor(sensorId, client);
            } catch (RemoteException e) {
                Slog.e(getTag(), "Remote exception when scheduling loadAuthenticatorId"
                        + ", sensorId: " + sensorId
                        + ", userId: " + userId, e);
            }
        });
    }

    private void scheduleInvalidationRequest(int sensorId, int userId) {
    void scheduleInvalidationRequest(int sensorId, int userId) {
        mHandler.post(() -> {
            final InvalidationRequesterClient<Face> client =
                    new InvalidationRequesterClient<>(mContext, userId, sensorId,
@@ -303,25 +270,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
    public void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
            @NonNull IInvalidationCallback callback) {
        mHandler.post(() -> {
            final IFace daemon = getHalInstance();
            if (daemon == null) {
                Slog.e(getTag(), "Null daemon during scheduleInvalidateAuthenticatorId: "
                        + sensorId);
                return;
            }

            try {
                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
                    createNewSessionWithoutHandler(daemon, sensorId, userId);
                }

            final FaceInvalidationClient client = new FaceInvalidationClient(mContext,
                    mSensors.get(sensorId).getLazySession(), userId, sensorId,
                    mSensors.get(sensorId).getAuthenticatorIds(), callback);
            scheduleForSensor(sensorId, client);
            } catch (RemoteException e) {
                Slog.e(getTag(), "Remote exception", e);
            }
        });
    }

@@ -344,25 +296,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
    public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
            @NonNull IFaceServiceReceiver receiver, String opPackageName) {
        mHandler.post(() -> {
            final IFace daemon = getHalInstance();
            if (daemon == null) {
                Slog.e(getTag(), "Null daemon during generateChallenge, sensorId: " + sensorId);
                return;
            }

            try {
                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
                    createNewSessionWithoutHandler(daemon, sensorId, userId);
                }

            final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
                    mSensors.get(sensorId).getLazySession(), token,
                    new ClientMonitorCallbackConverter(receiver), opPackageName, sensorId);

            scheduleForSensor(sensorId, client);
            } catch (RemoteException e) {
                Slog.e(getTag(), "Remote exception when scheduling generateChallenge", e);
            }
        });
    }

@@ -370,25 +307,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
    public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
            @NonNull String opPackageName, long challenge) {
        mHandler.post(() -> {
            final IFace daemon = getHalInstance();
            if (daemon == null) {
                Slog.e(getTag(), "Null daemon during revokeChallenge, sensorId: " + sensorId);
                return;
            }

            try {
                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
                    createNewSessionWithoutHandler(daemon, sensorId, userId);
                }

            final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
                    mSensors.get(sensorId).getLazySession(), token, opPackageName, sensorId,
                    challenge);

            scheduleForSensor(sensorId, client);
            } catch (RemoteException e) {
                Slog.e(getTag(), "Remote exception when scheduling revokeChallenge", e);
            }
        });
    }

@@ -398,20 +320,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
            @NonNull String opPackageName, @NonNull int[] disabledFeatures,
            @Nullable NativeHandle previewSurface, boolean debugConsent) {
        mHandler.post(() -> {
            final IFace daemon = getHalInstance();
            if (daemon == null) {
                Slog.e(getTag(), "Null daemon during enroll, sensorId: " + sensorId);
                // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
                // this operation. We should not send the callback yet, since the scheduler may
                // be processing something else.
                return;
            }

            try {
                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
                    createNewSessionWithoutHandler(daemon, sensorId, userId);
                }

            final int maxTemplatesPerUser = mSensors.get(
                    sensorId).getSensorProperties().maxEnrollmentsPerUser;
            final FaceEnrollClient client = new FaceEnrollClient(mContext,
@@ -430,9 +338,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
                    }
                }
            });
            } catch (RemoteException e) {
                Slog.e(getTag(), "Remote exception when scheduling enroll", e);
            }
        });
    }

@@ -447,20 +352,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
            @NonNull String opPackageName, boolean restricted, int statsClient,
            boolean allowBackgroundAuthentication) {
        mHandler.post(() -> {
            final IFace daemon = getHalInstance();
            if (daemon == null) {
                Slog.e(getTag(), "Null daemon during authenticate, sensorId: " + sensorId);
                // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
                // this operation. We should not send the callback yet, since the scheduler may
                // be processing something else.
                return;
            }

            try {
                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
                    createNewSessionWithoutHandler(daemon, sensorId, userId);
                }

            final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
            final FaceAuthenticationClient client = new FaceAuthenticationClient(
                    mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId,
@@ -469,9 +360,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
                    mUsageStats, mSensors.get(sensorId).getLockoutCache(),
                    allowBackgroundAuthentication);
            scheduleForSensor(sensorId, client);
            } catch (RemoteException e) {
                Slog.e(getTag(), "Remote exception when scheduling authenticate", e);
            }
        });
    }

@@ -503,56 +391,24 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
    private void scheduleRemoveSpecifiedIds(int sensorId, @NonNull IBinder token, int[] faceIds,
            int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
        mHandler.post(() -> {
            final IFace daemon = getHalInstance();
            if (daemon == null) {
                Slog.e(getTag(), "Null daemon during remove, sensorId: " + sensorId);
                // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
                // this operation. We should not send the callback yet, since the scheduler may
                // be processing something else.
                return;
            }

            try {
                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
                    createNewSessionWithoutHandler(daemon, sensorId, userId);
                }

            final FaceRemovalClient client = new FaceRemovalClient(mContext,
                    mSensors.get(sensorId).getLazySession(), token,
                    new ClientMonitorCallbackConverter(receiver), faceIds, userId,
                    opPackageName, FaceUtils.getInstance(sensorId), sensorId,
                    mSensors.get(sensorId).getAuthenticatorIds());

            scheduleForSensor(sensorId, client);
            } catch (RemoteException e) {
                Slog.e(getTag(), "Remote exception when scheduling remove", e);
            }
        });
    }

    @Override
    public void scheduleResetLockout(int sensorId, int userId, @NonNull byte[] hardwareAuthToken) {
        mHandler.post(() -> {
            final IFace daemon = getHalInstance();
            if (daemon == null) {
                Slog.e(getTag(), "Null daemon during resetLockout, sensorId: " + sensorId);
                return;
            }

            try {
                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
                    createNewSessionWithoutHandler(daemon, sensorId, userId);
                }

            final FaceResetLockoutClient client = new FaceResetLockoutClient(
                    mContext, mSensors.get(sensorId).getLazySession(), userId,
                    mContext.getOpPackageName(), sensorId, hardwareAuthToken,
                    mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);

            scheduleForSensor(sensorId, client);
            } catch (RemoteException e) {
                Slog.e(getTag(), "Remote exception when scheduling resetLockout", e);
            }
        });
    }

@@ -580,17 +436,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
    public void scheduleInternalCleanup(int sensorId, int userId,
            @Nullable BaseClientMonitor.Callback callback) {
        mHandler.post(() -> {
            final IFace daemon = getHalInstance();
            if (daemon == null) {
                Slog.e(getTag(), "Null daemon during internal cleanup, sensorId: " + sensorId);
                return;
            }

            try {
                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
                    createNewSessionWithoutHandler(daemon, sensorId, userId);
                }

            final List<Face> enrolledList = getEnrolledFaces(sensorId, userId);
            final FaceInternalCleanupClient client =
                    new FaceInternalCleanupClient(mContext,
@@ -598,11 +443,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
                            mContext.getOpPackageName(), sensorId, enrolledList,
                            FaceUtils.getInstance(sensorId),
                            mSensors.get(sensorId).getAuthenticatorIds());

            scheduleForSensor(sensorId, client);
            } catch (RemoteException e) {
                Slog.e(getTag(), "Remote exception when scheduling internal cleanup", e);
            }
        });
    }

+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.face.aidl;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.ISessionCallback;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;

import com.android.server.biometrics.sensors.StartUserClient;

public class FaceStartUserClient extends StartUserClient<IFace, ISession> {
    private static final String TAG = "FaceStartUserClient";

    @NonNull private final ISessionCallback mSessionCallback;

    public FaceStartUserClient(@NonNull Context context, @NonNull LazyDaemon<IFace> lazyDaemon,
            @Nullable IBinder token, int userId, int sensorId,
            @NonNull ISessionCallback sessionCallback,
            @NonNull UserStartedCallback<ISession> callback) {
        super(context, lazyDaemon, token, userId, sensorId, callback);
        mSessionCallback = sessionCallback;
    }

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

    @Override
    protected void startHalOperation() {
        try {
            final ISession newSession = getFreshDaemon().createSession(getSensorId(),
                    getTargetUserId(), mSessionCallback);
            mUserStartedCallback.onUserStarted(getTargetUserId(), newSession);
            getCallback().onClientFinished(this, true /* success */);
        } catch (RemoteException e) {
            Slog.e(TAG, "Remote exception", e);
            getCallback().onClientFinished(this, false /* success */);
        }
    }

    @Override
    public void unableToStart() {

    }
}
+58 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.face.aidl;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.face.ISession;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;

import com.android.server.biometrics.sensors.StopUserClient;

public class FaceStopUserClient extends StopUserClient<ISession> {
    private static final String TAG = "FaceStopUserClient";

    public FaceStopUserClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
            @Nullable IBinder token, int userId, int sensorId,
            @NonNull UserStoppedCallback callback) {
        super(context, lazyDaemon, token, userId, sensorId, callback);
    }

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

    @Override
    protected void startHalOperation() {
        try {
            getFreshDaemon().close(mSequentialId);
        } catch (RemoteException e) {
            Slog.e(TAG, "Remote exception", e);
            getCallback().onClientFinished(this, false /* success */);
        }
    }

    @Override
    public void unableToStart() {

    }
}
+62 −22
Original line number Diff line number Diff line
@@ -33,8 +33,11 @@ import android.hardware.face.Face;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -54,6 +57,9 @@ import com.android.server.biometrics.sensors.Interruptable;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
import com.android.server.biometrics.sensors.face.FaceUtils;

import java.util.ArrayList;
@@ -70,9 +76,10 @@ public class Sensor {
    @NonNull private final String mTag;
    @NonNull private final FaceProvider mProvider;
    @NonNull private final Context mContext;
    @NonNull private final IBinder mToken;
    @NonNull private final Handler mHandler;
    @NonNull private final FaceSensorPropertiesInternal mSensorProperties;
    @NonNull private final BiometricScheduler mScheduler;
    @NonNull private final UserAwareBiometricScheduler mScheduler;
    @NonNull private final LockoutCache mLockoutCache;
    @NonNull private final Map<Integer, Long> mAuthenticatorIds;
    @NonNull private final HalClientMonitor.LazyDaemon<ISession> mLazySession;
@@ -427,7 +434,15 @@ public class Sensor {
        @Override
        public void onSessionClosed() {
            mHandler.post(() -> {
              // TODO: implement this.
                final BaseClientMonitor client = mScheduler.getCurrentClient();
                if (!(client instanceof FaceStopUserClient)) {
                    Slog.e(mTag, "onSessionClosed for wrong consumer: "
                            + Utils.getClientName(client));
                    return;
                }

                final FaceStopUserClient stopUserClient = (FaceStopUserClient) client;
                stopUserClient.onUserStopped();
            });
        }
    }
@@ -437,9 +452,53 @@ public class Sensor {
        mTag = tag;
        mProvider = provider;
        mContext = context;
        mToken = new Binder();
        mHandler = handler;
        mSensorProperties = sensorProperties;
        mScheduler = new BiometricScheduler(tag, null /* gestureAvailabilityDispatcher */);
        mScheduler = new UserAwareBiometricScheduler(tag, null /* gestureAvailabilityDispatcher */,
                () -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
                new UserAwareBiometricScheduler.UserSwitchCallback() {
                    @NonNull
                    @Override
                    public StopUserClient<?> getStopUserClient(int userId) {
                        return new FaceStopUserClient(mContext, mLazySession, mToken, userId,
                                mSensorProperties.sensorId, () -> mCurrentSession = null);
                    }

                    @NonNull
                    @Override
                    public StartUserClient<?, ?> getStartUserClient(int newUserId) {
                        final HalSessionCallback.Callback callback = () -> {
                            Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
                            mCurrentSession = null;
                        };

                        final int sensorId = mSensorProperties.sensorId;

                        final HalSessionCallback resultController = new HalSessionCallback(mContext,
                                mHandler, mTag, mScheduler, sensorId, newUserId, callback);

                        final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
                                (userIdStarted, newSession) -> {
                                    mCurrentSession = new Session(mTag, newSession, userIdStarted,
                                            resultController);
                                    if (FaceUtils.getLegacyInstance(sensorId)
                                            .isInvalidationInProgress(mContext, userIdStarted)) {
                                        Slog.w(mTag,
                                                "Scheduling unfinished invalidation request for "
                                                        + "sensor: "
                                                        + sensorId
                                                        + ", user: " + userIdStarted);
                                        provider.scheduleInvalidationRequest(sensorId,
                                                userIdStarted);
                                    }
                                };

                        return new FaceStartUserClient(mContext, provider::getHalInstance,
                                mToken, newUserId, mSensorProperties.sensorId,
                                resultController, userStartedCallback);
                    }
                });
        mLockoutCache = new LockoutCache();
        mAuthenticatorIds = new HashMap<>();
        mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null;
@@ -453,11 +512,6 @@ public class Sensor {
        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;
@@ -471,20 +525,6 @@ public class Sensor {
                mProvider, this);
    }

    void createNewSession(@NonNull IFace 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, resultController);
        mCurrentSession = new Session(mTag, newSession, userId, resultController);
    }

    @NonNull BiometricScheduler getScheduler() {
        return mScheduler;
    }
+19 −2

File changed.

Preview size limit exceeded, changes collapsed.