From 86e98080b1a1ede6c2d79f57ecef9500ca465d6f Mon Sep 17 00:00:00 2001 From: Kevin Chyn Date: Mon, 22 Mar 2021 14:27:56 -0700 Subject: [PATCH] 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 --- .../sensors/face/aidl/FaceProvider.java | 287 ++++-------------- .../face/aidl/FaceStartUserClient.java | 67 ++++ .../sensors/face/aidl/FaceStopUserClient.java | 58 ++++ .../biometrics/sensors/face/aidl/Sensor.java | 84 +++-- .../sensors/face/aidl/FaceProviderTest.java | 21 +- 5 files changed, 270 insertions(+), 247 deletions(-) create mode 100644 services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java create mode 100644 services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index fdc3bb172a379..a9be8e1a707b4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -78,7 +78,6 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull private final String mHalInstanceName; @NonNull @VisibleForTesting final SparseArray mSensors; // Map of sensors that this HAL supports - @NonNull private final HalClientMonitor.LazyDaemon 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; - } + final FaceGetAuthenticatorIdClient client = new FaceGetAuthenticatorIdClient( + mContext, mSensors.get(sensorId).getLazySession(), userId, + mContext.getOpPackageName(), sensorId, + mSensors.get(sensorId).getAuthenticatorIds()); - 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); - } + scheduleForSensor(sensorId, client); }); } - private void scheduleInvalidationRequest(int sensorId, int userId) { + void scheduleInvalidationRequest(int sensorId, int userId) { mHandler.post(() -> { final InvalidationRequesterClient 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); - } + final FaceInvalidationClient client = new FaceInvalidationClient(mContext, + mSensors.get(sensorId).getLazySession(), userId, sensorId, + mSensors.get(sensorId).getAuthenticatorIds(), callback); + scheduleForSensor(sensorId, client); }); } @@ -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); - } + final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext, + mSensors.get(sensorId).getLazySession(), token, + new ClientMonitorCallbackConverter(receiver), opPackageName, sensorId); + scheduleForSensor(sensorId, client); }); } @@ -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); - } + final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext, + mSensors.get(sensorId).getLazySession(), token, opPackageName, sensorId, + challenge); + scheduleForSensor(sensorId, client); }); } @@ -398,41 +320,24 @@ 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, - mSensors.get(sensorId).getLazySession(), token, - new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, - opPackageName, FaceUtils.getInstance(sensorId), disabledFeatures, - ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser, - debugConsent); - scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() { - @Override - public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, - boolean success) { - if (success) { - scheduleLoadAuthenticatorIdsForUser(sensorId, userId); - scheduleInvalidationRequest(sensorId, userId); - } + final int maxTemplatesPerUser = mSensors.get( + sensorId).getSensorProperties().maxEnrollmentsPerUser; + final FaceEnrollClient client = new FaceEnrollClient(mContext, + mSensors.get(sensorId).getLazySession(), token, + new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, + opPackageName, FaceUtils.getInstance(sensorId), disabledFeatures, + ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser, + debugConsent); + scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() { + @Override + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, + boolean success) { + if (success) { + scheduleLoadAuthenticatorIdsForUser(sensorId, userId); + scheduleInvalidationRequest(sensorId, userId); } - }); - } catch (RemoteException e) { - Slog.e(getTag(), "Remote exception when scheduling enroll", e); - } + } + }); }); } @@ -447,31 +352,14 @@ 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, - operationId, restricted, opPackageName, cookie, - false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient, - mUsageStats, mSensors.get(sensorId).getLockoutCache(), - allowBackgroundAuthentication); - scheduleForSensor(sensorId, client); - } catch (RemoteException e) { - Slog.e(getTag(), "Remote exception when scheduling authenticate", e); - } + final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); + final FaceAuthenticationClient client = new FaceAuthenticationClient( + mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId, + operationId, restricted, opPackageName, cookie, + false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient, + mUsageStats, mSensors.get(sensorId).getLockoutCache(), + allowBackgroundAuthentication); + scheduleForSensor(sensorId, client); }); } @@ -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); - } + 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); }); } @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; - } + final FaceResetLockoutClient client = new FaceResetLockoutClient( + mContext, mSensors.get(sensorId).getLazySession(), userId, + mContext.getOpPackageName(), sensorId, hardwareAuthToken, + mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher); - 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); - } + scheduleForSensor(sensorId, client); }); } @@ -580,29 +436,14 @@ 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 enrolledList = getEnrolledFaces(sensorId, userId); - final FaceInternalCleanupClient client = - new FaceInternalCleanupClient(mContext, - mSensors.get(sensorId).getLazySession(), userId, - 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); - } + final List enrolledList = getEnrolledFaces(sensorId, userId); + final FaceInternalCleanupClient client = + new FaceInternalCleanupClient(mContext, + mSensors.get(sensorId).getLazySession(), userId, + mContext.getOpPackageName(), sensorId, enrolledList, + FaceUtils.getInstance(sensorId), + mSensors.get(sensorId).getAuthenticatorIds()); + scheduleForSensor(sensorId, client); }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java new file mode 100644 index 0000000000000..c364dbb4d6154 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java @@ -0,0 +1,67 @@ +/* + * 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 { + private static final String TAG = "FaceStartUserClient"; + + @NonNull private final ISessionCallback mSessionCallback; + + public FaceStartUserClient(@NonNull Context context, @NonNull LazyDaemon lazyDaemon, + @Nullable IBinder token, int userId, int sensorId, + @NonNull ISessionCallback sessionCallback, + @NonNull UserStartedCallback 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() { + + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java new file mode 100644 index 0000000000000..8d3853b96b4e5 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java @@ -0,0 +1,58 @@ +/* + * 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 { + private static final String TAG = "FaceStopUserClient"; + + public FaceStopUserClient(@NonNull Context context, @NonNull LazyDaemon 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() { + + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index 3eb4759213047..4baf952063742 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -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 mAuthenticatorIds; @NonNull private final HalClientMonitor.LazyDaemon 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 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; } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java index 04a7122ba426f..0cd6d86a3ec9d 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java @@ -24,10 +24,12 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.hardware.biometrics.common.CommonProps; +import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.SensorProps; import android.os.UserManager; import android.platform.test.annotations.Presubmit; +import androidx.annotation.NonNull; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -56,7 +58,7 @@ public class FaceProviderTest { private SensorProps[] mSensorProps; private LockoutResetDispatcher mLockoutResetDispatcher; - private FaceProvider mFaceProvider; + private TestableFaceProvider mFaceProvider; private static void waitForIdle() { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -80,7 +82,7 @@ public class FaceProviderTest { mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); - mFaceProvider = new FaceProvider(mContext, mSensorProps, TAG, + mFaceProvider = new TestableFaceProvider(mContext, mSensorProps, TAG, mLockoutResetDispatcher); } @@ -123,4 +125,19 @@ public class FaceProviderTest { assertEquals(0, scheduler.getCurrentPendingCount()); } } + + private static class TestableFaceProvider extends FaceProvider { + public TestableFaceProvider(@NonNull Context context, + @NonNull SensorProps[] props, + @NonNull String halInstanceName, + @NonNull LockoutResetDispatcher lockoutResetDispatcher) { + super(context, props, halInstanceName, lockoutResetDispatcher); + } + + @Override + synchronized IFace getHalInstance() { + return mock(IFace.class); + } + } + }