Merge "Finish TestSession implementation for FaceProvider"

This commit is contained in:
TreeHugger Robot
2020-11-18 19:15:40 +00:00
committed by Android (Google) Code Review
11 changed files with 585 additions and 109 deletions

View File

@@ -564,6 +564,11 @@ public class BiometricScheduler {
public void dump(PrintWriter pw) {
pw.println("Dump of BiometricScheduler " + getTag());
pw.println("Current operation: " + mCurrentOperation);
pw.println("Pending operations: " + mPendingOperations.size());
for (Operation operation : mPendingOperations) {
pw.println("Pending operation: " + operation);
}
for (CrashState crashState : mCrashStates) {
pw.println("Crash State " + crashState);
}

View File

@@ -0,0 +1,205 @@
/*
* 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 static android.Manifest.permission.TEST_BIOMETRIC;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.face.Face;
import android.hardware.face.IFaceServiceReceiver;
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.face.FaceUtils;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
/**
* A test session implementation for {@link FaceProvider}. See
* {@link android.hardware.biometrics.BiometricTestSession}.
*/
public class BiometricTestSessionImpl extends ITestSession.Stub {
private static final String TAG = "BiometricTestSessionImpl";
@NonNull private final Context mContext;
private final int mSensorId;
@NonNull private final FaceProvider 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 BiometricPrompt APIs and does not use this receiver.
*/
private final IFaceServiceReceiver mReceiver = new IFaceServiceReceiver.Stub() {
@Override
public void onEnrollResult(Face face, int remaining) {
}
@Override
public void onAcquired(int acquireInfo, int vendorCode) {
}
@Override
public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) {
}
@Override
public void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
}
@Override
public void onAuthenticationFailed() {
}
@Override
public void onError(int error, int vendorCode) {
}
@Override
public void onRemoved(Face face, int remaining) {
}
@Override
public void onFeatureSet(boolean success, int feature) {
}
@Override
public void onFeatureGet(boolean success, int feature, boolean value) {
}
@Override
public void onChallengeGenerated(int sensorId, long challenge) {
}
@Override
public void onChallengeInterrupted(int sensorId) {
}
@Override
public void onChallengeInterruptFinished(int sensorId) {
}
};
BiometricTestSessionImpl(@NonNull Context context, int sensorId,
@NonNull FaceProvider 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(), new int[0] /* disabledFeatures */, 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 faces
List<Face> faces = FaceUtils.getInstance(mSensorId)
.getBiometricsForUser(mContext, userId);
if (faces.isEmpty()) {
Slog.w(TAG, "No faces, returning");
return;
}
final int fid = faces.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);
}
}

View File

@@ -47,6 +47,11 @@ class FaceGetAuthenticatorIdClient extends ClientMonitor<ISession> {
// Nothing to do here
}
public void start(@NonNull Callback callback) {
super.start(callback);
startHalOperation();
}
@Override
protected void startHalOperation() {
try {

View File

@@ -67,6 +67,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
private static final String TAG = "FaceProvider";
private static final int ENROLL_TIMEOUT_SEC = 75;
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
@@ -150,6 +152,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Nullable
private synchronized IFace getHalInstance() {
if (mTestHalEnabled) {
return new TestHal();
}
if (mDaemon != null) {
return mDaemon;
}
@@ -525,7 +531,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Override
public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
if (mSensors.contains(sensorId)) {
mSensors.get(sensorId).dumpProtoState(sensorId, proto);
}
}
@Override
@@ -576,7 +584,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull
@Override
public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
return null; // TODO
return mSensors.get(sensorId).createTestSession();
}
@Override
@@ -593,4 +601,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
});
}
void setTestHalEnabled(boolean enabled) {
mTestHalEnabled = enabled;
}
}

View File

@@ -61,6 +61,11 @@ public class FaceResetLockoutClient extends ClientMonitor<ISession> {
// Nothing to do here
}
public void start(@NonNull Callback callback) {
super.start(callback);
startHalOperation();
}
@Override
protected void startHalOperation() {
try {

View File

@@ -19,7 +19,9 @@ 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.face.Error;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
@@ -31,10 +33,15 @@ 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.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.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
@@ -56,6 +63,8 @@ import java.util.Map;
*/
public class Sensor implements IBinder.DeathRecipient {
private boolean mTestHalEnabled;
@NonNull private final String mTag;
@NonNull private final FaceProvider mProvider;
@NonNull private final Context mContext;
@@ -67,27 +76,6 @@ public class Sensor implements IBinder.DeathRecipient {
@NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession;
@Nullable private Session mCurrentSession;
@Override
public void binderDied() {
Slog.e(mTag, "Binder died");
mHandler.post(() -> {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (client instanceof Interruptable) {
Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
final Interruptable interruptable = (Interruptable) client;
interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
mScheduler.recordCrashState();
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
BiometricsProtoEnums.MODALITY_FACE,
BiometricsProtoEnums.ISSUE_HAL_DEATH);
mCurrentSession = null;
}
});
}
static class Session {
@NonNull final HalSessionCallback mHalSessionCallback;
@NonNull private final String mTag;
@@ -104,67 +92,6 @@ public class Sensor implements IBinder.DeathRecipient {
}
}
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties) {
mTag = tag;
mProvider = provider;
mContext = context;
mHandler = handler;
mSensorProperties = sensorProperties;
mScheduler = new BiometricScheduler(tag, null /* gestureAvailabilityDispatcher */);
mLockoutCache = new LockoutCache();
mAuthenticatorIds = new HashMap<>();
mLazySession = () -> (mCurrentSession != null) ? mCurrentSession.mSession : null;
}
@NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
return mLazySession;
}
@NonNull FaceSensorPropertiesInternal 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;
}
}
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);
newSession.asBinder().linkToDeath(this, 0 /* flags */);
mCurrentSession = new Session(mTag, newSession, userId, resultController);
}
@NonNull BiometricScheduler getScheduler() {
return mScheduler;
}
@NonNull LockoutCache getLockoutCache() {
return mLockoutCache;
}
@NonNull Map<Integer, Long> getAuthenticatorIds() {
return mAuthenticatorIds;
}
static class HalSessionCallback extends ISessionCallback.Stub {
/**
* Interface to sends results to the HalSessionCallback's owner.
@@ -453,4 +380,120 @@ public class Sensor implements IBinder.DeathRecipient {
}
}
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties) {
mTag = tag;
mProvider = provider;
mContext = context;
mHandler = handler;
mSensorProperties = sensorProperties;
mScheduler = new BiometricScheduler(tag, null /* 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 FaceSensorPropertiesInternal 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 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);
newSession.asBinder().linkToDeath(this, 0 /* flags */);
mCurrentSession = new Session(mTag, newSession, userId, resultController);
}
@NonNull BiometricScheduler getScheduler() {
return mScheduler;
}
@NonNull LockoutCache getLockoutCache() {
return mLockoutCache;
}
@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(SensorServiceStateProto.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,
FaceUtils.getInstance(mSensorProperties.sensorId)
.getBiometricsForUser(mContext, userId).size());
proto.end(userToken);
}
proto.end(sensorToken);
}
@Override
public void binderDied() {
Slog.e(mTag, "Binder died");
mHandler.post(() -> {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (client instanceof Interruptable) {
Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
final Interruptable interruptable = (Interruptable) client;
interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
mScheduler.recordCrashState();
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
BiometricsProtoEnums.MODALITY_FACE,
BiometricsProtoEnums.ISSUE_HAL_DEATH);
mCurrentSession = null;
}
});
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.ISessionCallback;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.common.NativeHandle;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.Binder;
import android.os.IBinder;
/**
* Test HAL that provides only no-ops.
*/
public class TestHal extends IFace.Stub {
@Override
public SensorProps[] getSensorProps() {
return new SensorProps[0];
}
@Override
public ISession createSession(int sensorId, int userId, ISessionCallback cb) {
return new ISession() {
@Override
public void generateChallenge(int cookie, int timeoutSec) {
}
@Override
public void revokeChallenge(int cookie, long challenge) {
}
@Override
public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
NativeHandle previewSurface) {
return null;
}
@Override
public ICancellationSignal authenticate(int cookie, long operationId) {
return null;
}
@Override
public ICancellationSignal detectInteraction(int cookie) {
return null;
}
@Override
public void enumerateEnrollments(int cookie) {
}
@Override
public void removeEnrollments(int cookie, int[] enrollmentIds) {
}
@Override
public void getAuthenticatorId(int cookie) {
}
@Override
public void invalidateAuthenticatorId(int cookie) {
}
@Override
public void resetLockout(int cookie, HardwareAuthToken hat) {
}
@Override
public IBinder asBinder() {
return new Binder();
}
};
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.Error;
import android.hardware.biometrics.face.ISession;
import android.hardware.common.NativeHandle;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
/**
* Test session that provides mostly no-ops.
*/
public class TestSession extends ISession.Stub {
private static final String TAG = "TestSession";
@NonNull
private final Sensor.HalSessionCallback mHalSessionCallback;
TestSession(@NonNull Sensor.HalSessionCallback halSessionCallback) {
mHalSessionCallback = halSessionCallback;
}
@Override
public void generateChallenge(int cookie, int timeoutSec) {
mHalSessionCallback.onChallengeGenerated(0 /* challenge */);
}
@Override
public void revokeChallenge(int cookie, long challenge) {
mHalSessionCallback.onChallengeRevoked(challenge);
}
@Override
public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
NativeHandle previewSurface) {
return null;
}
@Override
public ICancellationSignal authenticate(int cookie, long operationId) {
return new ICancellationSignal() {
@Override
public void cancel() throws RemoteException {
mHalSessionCallback.onError(Error.CANCELED, 0 /* vendorCode */);
}
@Override
public IBinder asBinder() {
return new Binder();
}
};
}
@Override
public ICancellationSignal detectInteraction(int cookie) {
return null;
}
@Override
public void enumerateEnrollments(int cookie) {
}
@Override
public void removeEnrollments(int cookie, int[] enrollmentIds) {
}
@Override
public void getAuthenticatorId(int cookie) {
mHalSessionCallback.onAuthenticatorIdRetrieved(0);
}
@Override
public void invalidateAuthenticatorId(int cookie) {
}
@Override
public void resetLockout(int cookie, HardwareAuthToken hat) {
mHalSessionCallback.onLockoutCleared();
}
}

View File

@@ -143,7 +143,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
// Fake authentication with any of the existing fingers
List<Fingerprint> fingerprints = FingerprintUtils.getInstance()
List<Fingerprint> fingerprints = FingerprintUtils.getInstance(mSensorId)
.getBiometricsForUser(mContext, userId);
if (fingerprints.isEmpty()) {
Slog.w(TAG, "No fingerprints, returning");

View File

@@ -80,27 +80,6 @@ class Sensor implements IBinder.DeathRecipient {
@Nullable private Session mCurrentSession;
@NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession;
@Override
public void binderDied() {
Slog.e(mTag, "Binder died");
mHandler.post(() -> {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (client instanceof Interruptable) {
Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
final Interruptable interruptable = (Interruptable) client;
interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
mScheduler.recordCrashState();
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
BiometricsProtoEnums.MODALITY_FINGERPRINT,
BiometricsProtoEnums.ISSUE_HAL_DEATH);
mCurrentSession = null;
}
});
}
static class Session {
@NonNull private final String mTag;
@NonNull private final ISession mSession;
@@ -501,11 +480,33 @@ class Sensor implements IBinder.DeathRecipient {
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.write(UserStateProto.NUM_ENROLLED,
FingerprintUtils.getInstance(mSensorProperties.sensorId)
.getBiometricsForUser(mContext, userId).size());
proto.end(userToken);
}
proto.end(sensorToken);
}
@Override
public void binderDied() {
Slog.e(mTag, "Binder died");
mHandler.post(() -> {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (client instanceof Interruptable) {
Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
final Interruptable interruptable = (Interruptable) client;
interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
mScheduler.recordCrashState();
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
BiometricsProtoEnums.MODALITY_FINGERPRINT,
BiometricsProtoEnums.ISSUE_HAL_DEATH);
mCurrentSession = null;
}
});
}
}

View File

@@ -23,7 +23,7 @@ import android.hardware.keymaster.HardwareAuthToken;
import android.util.Slog;
/**
* Test HAL that provides only provides mostly no-ops.
* Test session that provides mostly no-ops.
*/
class TestSession extends ISession.Stub {