| /* |
| * 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.face.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.ITestSessionCallback; |
| import android.hardware.biometrics.face.AuthenticationFrame; |
| import android.hardware.biometrics.face.EnrollmentFrame; |
| import android.hardware.biometrics.face.Error; |
| import android.hardware.biometrics.face.IFace; |
| import android.hardware.biometrics.face.ISession; |
| import android.hardware.biometrics.face.ISessionCallback; |
| 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; |
| |
| import com.android.internal.util.FrameworkStatsLog; |
| import com.android.server.biometrics.HardwareAuthTokenUtils; |
| import com.android.server.biometrics.SensorServiceStateProto; |
| import com.android.server.biometrics.SensorStateProto; |
| import com.android.server.biometrics.UserStateProto; |
| import com.android.server.biometrics.Utils; |
| import com.android.server.biometrics.sensors.AuthenticationConsumer; |
| import com.android.server.biometrics.sensors.BaseClientMonitor; |
| import com.android.server.biometrics.sensors.BiometricScheduler; |
| import com.android.server.biometrics.sensors.EnumerateConsumer; |
| import com.android.server.biometrics.sensors.ErrorConsumer; |
| import com.android.server.biometrics.sensors.HalClientMonitor; |
| 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.LockoutResetDispatcher; |
| 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; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * Maintains the state of a single sensor within an instance of the {@link IFace} HAL. |
| */ |
| public class Sensor { |
| |
| private boolean mTestHalEnabled; |
| |
| @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 UserAwareBiometricScheduler mScheduler; |
| @NonNull private final LockoutCache mLockoutCache; |
| @NonNull private final Map<Integer, Long> mAuthenticatorIds; |
| @NonNull private final HalClientMonitor.LazyDaemon<ISession> mLazySession; |
| @Nullable private Session mCurrentSession; |
| |
| static class Session { |
| @NonNull final HalSessionCallback mHalSessionCallback; |
| @NonNull private final String mTag; |
| @NonNull private final ISession mSession; |
| private final int mUserId; |
| |
| Session(@NonNull String tag, @NonNull ISession session, int userId, |
| @NonNull HalSessionCallback halSessionCallback) { |
| mTag = tag; |
| mSession = session; |
| mUserId = userId; |
| mHalSessionCallback = halSessionCallback; |
| Slog.d(mTag, "New session created for user: " + userId); |
| } |
| } |
| |
| static class HalSessionCallback extends ISessionCallback.Stub { |
| /** |
| * Interface to sends results to the HalSessionCallback's owner. |
| */ |
| public interface Callback { |
| /** |
| * Invoked when the HAL sends ERROR_HW_UNAVAILABLE. |
| */ |
| void onHardwareUnavailable(); |
| } |
| |
| @NonNull |
| private final Context mContext; |
| @NonNull |
| private final Handler mHandler; |
| @NonNull |
| private final String mTag; |
| @NonNull |
| private final UserAwareBiometricScheduler mScheduler; |
| private final int mSensorId; |
| private final int mUserId; |
| @NonNull |
| private final LockoutCache mLockoutCache; |
| @NonNull |
| private final LockoutResetDispatcher mLockoutResetDispatcher; |
| @NonNull |
| private final Callback mCallback; |
| |
| HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag, |
| @NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId, |
| @NonNull LockoutCache lockoutTracker, |
| @NonNull LockoutResetDispatcher lockoutResetDispatcher, |
| @NonNull Callback callback) { |
| mContext = context; |
| mHandler = handler; |
| mTag = tag; |
| mScheduler = scheduler; |
| mSensorId = sensorId; |
| mUserId = userId; |
| mLockoutCache = lockoutTracker; |
| mLockoutResetDispatcher = lockoutResetDispatcher; |
| mCallback = callback; |
| } |
| |
| @Override |
| public int getInterfaceVersion() { |
| return this.VERSION; |
| } |
| |
| @Override |
| public String getInterfaceHash() { |
| return this.HASH; |
| } |
| |
| @Override |
| public void onChallengeGenerated(long challenge) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof FaceGenerateChallengeClient)) { |
| Slog.e(mTag, "onChallengeGenerated for wrong client: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final FaceGenerateChallengeClient generateChallengeClient = |
| (FaceGenerateChallengeClient) client; |
| generateChallengeClient.onChallengeGenerated(mSensorId, mUserId, challenge); |
| }); |
| } |
| |
| @Override |
| public void onChallengeRevoked(long challenge) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof FaceRevokeChallengeClient)) { |
| Slog.e(mTag, "onChallengeRevoked for wrong client: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final FaceRevokeChallengeClient revokeChallengeClient = |
| (FaceRevokeChallengeClient) client; |
| revokeChallengeClient.onChallengeRevoked(mSensorId, mUserId, challenge); |
| }); |
| } |
| |
| @Override |
| public void onAuthenticationFrame(AuthenticationFrame frame) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof FaceAuthenticationClient)) { |
| Slog.e(mTag, "onAuthenticationFrame for incompatible client: " |
| + Utils.getClientName(client)); |
| return; |
| |
| } |
| if (frame == null) { |
| Slog.e(mTag, "Received null authentication frame for client: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| ((FaceAuthenticationClient) client).onAuthenticationFrame( |
| AidlConversionUtils.toFrameworkAuthenticationFrame(frame)); |
| }); |
| } |
| |
| @Override |
| public void onEnrollmentFrame(EnrollmentFrame frame) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof FaceEnrollClient)) { |
| Slog.e(mTag, "onEnrollmentFrame for incompatible client: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| if (frame == null) { |
| Slog.e(mTag, "Received null enrollment frame for client: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| ((FaceEnrollClient) client).onEnrollmentFrame( |
| AidlConversionUtils.toFrameworkEnrollmentFrame(frame)); |
| }); |
| } |
| |
| @Override |
| public void onError(byte error, int vendorCode) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| Slog.d(mTag, "onError" |
| + ", client: " + Utils.getClientName(client) |
| + ", error: " + error |
| + ", vendorCode: " + vendorCode); |
| if (!(client instanceof ErrorConsumer)) { |
| Slog.e(mTag, "onError for non-error consumer: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final ErrorConsumer errorConsumer = (ErrorConsumer) client; |
| errorConsumer.onError(AidlConversionUtils.toFrameworkError(error), vendorCode); |
| |
| if (error == Error.HW_UNAVAILABLE) { |
| mCallback.onHardwareUnavailable(); |
| } |
| }); |
| } |
| |
| @Override |
| public void onEnrollmentProgress(int enrollmentId, int remaining) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof FaceEnrollClient)) { |
| Slog.e(mTag, "onEnrollmentProgress for non-enroll client: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final int currentUserId = client.getTargetUserId(); |
| final CharSequence name = FaceUtils.getInstance(mSensorId) |
| .getUniqueName(mContext, currentUserId); |
| final Face face = new Face(name, enrollmentId, mSensorId); |
| |
| final FaceEnrollClient enrollClient = (FaceEnrollClient) client; |
| enrollClient.onEnrollResult(face, remaining); |
| }); |
| } |
| |
| @Override |
| public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof AuthenticationConsumer)) { |
| Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final AuthenticationConsumer authenticationConsumer = |
| (AuthenticationConsumer) client; |
| final Face face = new Face("" /* name */, enrollmentId, mSensorId); |
| final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat); |
| final ArrayList<Byte> byteList = new ArrayList<>(); |
| for (byte b : byteArray) { |
| byteList.add(b); |
| } |
| authenticationConsumer.onAuthenticated(face, true /* authenticated */, byteList); |
| }); |
| } |
| |
| @Override |
| public void onAuthenticationFailed() { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof AuthenticationConsumer)) { |
| Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final AuthenticationConsumer authenticationConsumer = |
| (AuthenticationConsumer) client; |
| final Face face = new Face("" /* name */, 0 /* faceId */, mSensorId); |
| authenticationConsumer.onAuthenticated(face, false /* authenticated */, |
| null /* hat */); |
| }); |
| } |
| |
| @Override |
| public void onLockoutTimed(long durationMillis) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof LockoutConsumer)) { |
| Slog.e(mTag, "onLockoutTimed for non-lockout consumer: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final LockoutConsumer lockoutConsumer = (LockoutConsumer) client; |
| lockoutConsumer.onLockoutTimed(durationMillis); |
| }); |
| } |
| |
| @Override |
| public void onLockoutPermanent() { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof LockoutConsumer)) { |
| Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final LockoutConsumer lockoutConsumer = (LockoutConsumer) client; |
| lockoutConsumer.onLockoutPermanent(); |
| }); |
| } |
| |
| @Override |
| public void onLockoutCleared() { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof FaceResetLockoutClient)) { |
| Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL"); |
| FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId, |
| mLockoutCache, mLockoutResetDispatcher); |
| } else { |
| Slog.d(mTag, "onLockoutCleared after resetLockout"); |
| final FaceResetLockoutClient resetLockoutClient = |
| (FaceResetLockoutClient) client; |
| resetLockoutClient.onLockoutCleared(); |
| } |
| }); |
| } |
| |
| @Override |
| public void onInteractionDetected() { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof FaceDetectClient)) { |
| Slog.e(mTag, "onInteractionDetected for wrong client: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final FaceDetectClient detectClient = (FaceDetectClient) client; |
| detectClient.onInteractionDetected(); |
| }); |
| } |
| |
| @Override |
| public void onEnrollmentsEnumerated(int[] enrollmentIds) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof EnumerateConsumer)) { |
| Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final EnumerateConsumer enumerateConsumer = |
| (EnumerateConsumer) client; |
| if (enrollmentIds.length > 0) { |
| for (int i = 0; i < enrollmentIds.length; ++i) { |
| final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId); |
| enumerateConsumer.onEnumerationResult(face, enrollmentIds.length - i - 1); |
| } |
| } else { |
| enumerateConsumer.onEnumerationResult(null /* identifier */, 0 /* remaining */); |
| } |
| }); |
| } |
| |
| @Override |
| public void onFeaturesRetrieved(byte[] features) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof FaceGetFeatureClient)) { |
| Slog.e(mTag, "onFeaturesRetrieved for non-get feature consumer: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| final FaceGetFeatureClient faceGetFeatureClient = (FaceGetFeatureClient) client; |
| faceGetFeatureClient.onFeatureGet(true /* success */, features); |
| }); |
| |
| } |
| |
| @Override |
| public void onFeatureSet(byte feature) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof FaceSetFeatureClient)) { |
| Slog.e(mTag, "onFeatureSet for non-set consumer: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final FaceSetFeatureClient faceSetFeatureClient = (FaceSetFeatureClient) client; |
| faceSetFeatureClient.onFeatureSet(true /* success */); |
| }); |
| } |
| |
| @Override |
| public void onEnrollmentsRemoved(int[] enrollmentIds) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof RemovalConsumer)) { |
| Slog.e(mTag, "onRemoved for non-removal consumer: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final RemovalConsumer removalConsumer = (RemovalConsumer) client; |
| if (enrollmentIds.length > 0) { |
| for (int i = 0; i < enrollmentIds.length; i++) { |
| final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId); |
| removalConsumer.onRemoved(face, enrollmentIds.length - i - 1); |
| } |
| } else { |
| removalConsumer.onRemoved(null /* identifier */, 0 /* remaining */); |
| } |
| }); |
| } |
| |
| @Override |
| public void onAuthenticatorIdRetrieved(long authenticatorId) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof FaceGetAuthenticatorIdClient)) { |
| Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final FaceGetAuthenticatorIdClient getAuthenticatorIdClient = |
| (FaceGetAuthenticatorIdClient) client; |
| getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId); |
| }); |
| } |
| |
| @Override |
| public void onAuthenticatorIdInvalidated(long newAuthenticatorId) { |
| mHandler.post(() -> { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (!(client instanceof FaceInvalidationClient)) { |
| Slog.e(mTag, "onAuthenticatorIdInvalidated for wrong consumer: " |
| + Utils.getClientName(client)); |
| return; |
| } |
| |
| final FaceInvalidationClient invalidationClient = (FaceInvalidationClient) client; |
| invalidationClient.onAuthenticatorIdInvalidated(newAuthenticatorId); |
| }); |
| } |
| |
| @Override |
| public void onSessionClosed() { |
| mHandler.post(mScheduler::onUserStopped); |
| } |
| } |
| |
| Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, |
| @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties, |
| @NonNull LockoutResetDispatcher lockoutResetDispatcher) { |
| mTag = tag; |
| mProvider = provider; |
| mContext = context; |
| mToken = new Binder(); |
| mHandler = handler; |
| mSensorProperties = sensorProperties; |
| mScheduler = new UserAwareBiometricScheduler(tag, |
| BiometricScheduler.SENSOR_TYPE_FACE, 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, mLockoutCache, |
| lockoutResetDispatcher, 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; |
| } |
| |
| @NonNull HalClientMonitor.LazyDaemon<ISession> getLazySession() { |
| return mLazySession; |
| } |
| |
| @NonNull FaceSensorPropertiesInternal getSensorProperties() { |
| return mSensorProperties; |
| } |
| |
| @Nullable Session getSessionForUser(int userId) { |
| if (mCurrentSession != null && mCurrentSession.mUserId == userId) { |
| return mCurrentSession; |
| } else { |
| return null; |
| } |
| } |
| |
| @NonNull ITestSession createTestSession(@NonNull ITestSessionCallback callback) { |
| return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, callback, |
| mProvider, this); |
| } |
| |
| @NonNull BiometricScheduler getScheduler() { |
| return mScheduler; |
| } |
| |
| @NonNull LockoutCache getLockoutCache() { |
| return mLockoutCache; |
| } |
| |
| @NonNull Map<Integer, Long> getAuthenticatorIds() { |
| return mAuthenticatorIds; |
| } |
| |
| void setTestHalEnabled(boolean enabled) { |
| Slog.w(mTag, "setTestHalEnabled: " + enabled); |
| if (enabled != mTestHalEnabled) { |
| // The framework should retrieve a new session from the HAL. |
| try { |
| if (mCurrentSession != null && mCurrentSession.mSession != null) { |
| // TODO(181984005): This should be scheduled instead of directly invoked |
| Slog.d(mTag, "Closing old session"); |
| mCurrentSession.mSession.close(); |
| } |
| } catch (RemoteException e) { |
| Slog.e(mTag, "RemoteException", e); |
| } |
| mCurrentSession = null; |
| } |
| mTestHalEnabled = enabled; |
| } |
| |
| void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto, |
| boolean clearSchedulerBuffer) { |
| final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES); |
| |
| proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId); |
| proto.write(SensorStateProto.MODALITY, SensorStateProto.FACE); |
| proto.write(SensorStateProto.CURRENT_STRENGTH, |
| Utils.getCurrentStrength(mSensorProperties.sensorId)); |
| proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer)); |
| |
| 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, |
| FaceUtils.getInstance(mSensorProperties.sensorId) |
| .getBiometricsForUser(mContext, userId).size()); |
| proto.end(userToken); |
| } |
| |
| proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN, |
| mSensorProperties.resetLockoutRequiresHardwareAuthToken); |
| proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE, |
| mSensorProperties.resetLockoutRequiresChallenge); |
| |
| proto.end(sensorToken); |
| } |
| |
| public void onBinderDied() { |
| final BaseClientMonitor client = mScheduler.getCurrentClient(); |
| if (client instanceof Interruptable) { |
| Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); |
| final ErrorConsumer errorConsumer = (ErrorConsumer) client; |
| errorConsumer.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, |
| 0 /* vendorCode */); |
| |
| FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, |
| BiometricsProtoEnums.MODALITY_FACE, |
| BiometricsProtoEnums.ISSUE_HAL_DEATH, |
| -1 /* sensorId */); |
| } |
| |
| mScheduler.recordCrashState(); |
| mScheduler.reset(); |
| mCurrentSession = null; |
| } |
| } |