9/n: Add BiometricScheduler proto dump

1) Adds biometrics.proto definition for BaseClientMonitor subtypes
2) Adds BiometricScheduler proto dump

When dumping the scheduler, the caller may request for the recent
operation queue to be cleared after dumping or not.

Note that we don't need BiometricScheduler.Operation#STATE_*
in the dump, since (for now) we just want to know what operations
have been run.

We can always add additional state, success/failure, etc in the
future if needed.

Bug: 159667191
Test: atest com.android.server.biometrics
Test: atest CtsBiometricsTestCases
Change-Id: I7d2350f00aaeef03ab7d74013b476af053240320
This commit is contained in:
Kevin Chyn
2021-01-19 18:51:02 -08:00
parent 6d6a948994
commit 7a707810e7
42 changed files with 363 additions and 37 deletions

View File

@@ -38,7 +38,7 @@ interface IBiometricAuthenticator {
SensorPropertiesInternal getSensorProperties(String opPackageName);
// Requests a proto dump of the sensor. See biometrics.proto
byte[] dumpSensorServiceStateProto();
byte[] dumpSensorServiceStateProto(boolean clearSchedulerBuffer);
// This method prepares the service to start authenticating, but doesn't start authentication.
// This is protected by the MANAGE_BIOMETRIC signature permission. This method should only be

View File

@@ -35,7 +35,7 @@ interface IFaceService {
ITestSession createTestSession(int sensorId, String opPackageName);
// Requests a proto dump of the specified sensor
byte[] dumpSensorServiceStateProto(int sensorId);
byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer);
// Retrieve static sensor properties for all face sensors
List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);

View File

@@ -36,7 +36,7 @@ interface IFingerprintService {
ITestSession createTestSession(int sensorId, String opPackageName);
// Requests a proto dump of the specified sensor
byte[] dumpSensorServiceStateProto(int sensorId);
byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer);
// Retrieve static sensor properties for all fingerprint sensors
List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);

View File

@@ -120,8 +120,8 @@ message SensorStateProto {
optional Modality modality = 2;
// State of the sensor's scheduler. True if currently handling an operation, false if idle.
optional bool is_busy = 3;
// State of the sensor's scheduler.
optional BiometricSchedulerProto scheduler = 3;
// User states for this sensor.
repeated UserStateProto user_states = 4;
@@ -136,4 +136,39 @@ message UserStateProto {
// Number of fingerprints enrolled
optional int32 num_enrolled = 2;
}
// BiometricScheduler dump
message BiometricSchedulerProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
// Operation currently being handled by the BiometricScheduler
optional ClientMonitorEnum current_operation = 1;
// Total number of operations that have been handled, not including the current one if one
// exists. Kept in FIFO order (most recent at the end of the array)
optional int32 total_operations = 2;
// A list of recent past operations in the order which they were handled
repeated ClientMonitorEnum recent_operations = 3;
}
// BaseClientMonitor subtypes
enum ClientMonitorEnum {
CM_NONE = 0;
CM_UPDATE_ACTIVE_USER = 1;
CM_ENROLL = 2;
CM_AUTHENTICATE = 3;
CM_REMOVE = 4;
CM_GET_AUTHENTICATOR_ID = 5;
CM_ENUMERATE = 6;
CM_INTERNAL_CLEANUP = 7;
CM_SET_FEATURE = 8;
CM_GET_FEATURE = 9;
CM_GENERATE_CHALLENGE = 10;
CM_REVOKE_CHALLENGE = 11;
CM_RESET_LOCKOUT = 12;
CM_DETECT_INTERACTION = 13;
CM_INVALIDATION_REQUESTER = 14;
CM_INVALIDATE = 15;
}

View File

@@ -37,7 +37,6 @@ import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.IBinder;
import android.os.RemoteException;
@@ -62,8 +61,6 @@ public final class AuthSession implements IBinder.DeathRecipient {
private static final String TAG = "BiometricService/AuthSession";
private static final boolean DEBUG = false;
/*
* Defined in biometrics.proto
*/

View File

@@ -815,12 +815,16 @@ public class BiometricService extends SystemService {
final long ident = Binder.clearCallingIdentity();
try {
if (args.length > 0 && "--proto".equals(args[0])) {
final boolean clearSchedulerBuffer = args.length > 1
&& "--clear-scheduler-buffer".equals(args[1]);
Slog.d(TAG, "ClearSchedulerBuffer: " + clearSchedulerBuffer);
final ProtoOutputStream proto = new ProtoOutputStream(fd);
proto.write(BiometricServiceStateProto.AUTH_SESSION_STATE,
mCurrentAuthSession != null ? mCurrentAuthSession.getState()
: STATE_AUTH_IDLE);
for (BiometricSensor sensor : mSensors) {
byte[] serviceState = sensor.impl.dumpSensorServiceStateProto();
byte[] serviceState = sensor.impl
.dumpSensorServiceStateProto(clearSchedulerBuffer);
proto.write(BiometricServiceStateProto.SENSOR_SERVICE_STATES, serviceState);
}
proto.flush();

View File

@@ -33,6 +33,7 @@ import android.security.KeyStore;
import android.util.EventLog;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.Utils;
import java.util.ArrayList;
@@ -298,4 +299,9 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
}
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_AUTHENTICATE;
}
}

View File

@@ -24,6 +24,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import java.util.NoSuchElementException;
/**
@@ -79,6 +81,12 @@ public abstract class BaseClientMonitor extends LoggableMonitor
@NonNull protected Callback mCallback;
/**
* Returns a ClientMonitorEnum constant defined in biometrics.proto
* @return
*/
public abstract int getProtoEnum();
/**
* @param context system_server context
* @param token a unique token for the client
@@ -195,10 +203,16 @@ public abstract class BaseClientMonitor extends LoggableMonitor
return mSensorId;
}
@VisibleForTesting
public Callback getCallback() {
return mCallback;
}
@Override
public String toString() {
return "{[" + mSequentialId + "] "
+ this.getClass().getSimpleName()
+ ", " + getProtoEnum()
+ ", " + getOwnerString()
+ ", " + getCookie() + "}";
}

View File

@@ -28,8 +28,11 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.BiometricSchedulerProto;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import java.io.PrintWriter;
@@ -51,6 +54,8 @@ import java.util.Locale;
public class BiometricScheduler {
private static final String BASE_TAG = "BiometricScheduler";
// Number of recent operations to keep in our logs for dumpsys
private static final int LOG_NUM_RECENT_OPERATIONS = 50;
/**
* Contains all the necessary information for a HAL operation.
@@ -200,6 +205,10 @@ public class BiometricScheduler {
@VisibleForTesting @Nullable Operation mCurrentOperation;
@NonNull private final ArrayDeque<CrashState> mCrashStates;
private int mTotalOperationsHandled;
private final int mRecentOperationsLimit;
@NonNull private final List<Integer> mRecentOperations;
// Internal callback, notified when an operation is complete. Notifies the requester
// that the operation is complete, before performing internal scheduler work (such as
// starting the next client).
@@ -240,7 +249,12 @@ public class BiometricScheduler {
mCurrentOperation.mClientMonitor.getSensorId(), false /* active */);
}
if (mRecentOperations.size() >= mRecentOperationsLimit) {
mRecentOperations.remove(0);
}
mRecentOperations.add(mCurrentOperation.mClientMonitor.getProtoEnum());
mCurrentOperation = null;
mTotalOperationsHandled++;
startNextOperationIfIdle();
});
}
@@ -249,13 +263,15 @@ public class BiometricScheduler {
@VisibleForTesting
BiometricScheduler(@NonNull String tag,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull IBiometricService biometricService) {
@NonNull IBiometricService biometricService, int recentOperationsLimit) {
mBiometricTag = tag;
mInternalCallback = new InternalCallback();
mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
mPendingOperations = new ArrayDeque<>();
mBiometricService = biometricService;
mCrashStates = new ArrayDeque<>();
mRecentOperationsLimit = recentOperationsLimit;
mRecentOperations = new ArrayList<>();
}
/**
@@ -267,7 +283,7 @@ public class BiometricScheduler {
public BiometricScheduler(@NonNull String tag,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
this(tag, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)));
ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS);
}
/**
@@ -602,6 +618,24 @@ public class BiometricScheduler {
}
}
public byte[] dumpProtoState(boolean clearSchedulerBuffer) {
final ProtoOutputStream proto = new ProtoOutputStream();
proto.write(BiometricSchedulerProto.CURRENT_OPERATION, mCurrentOperation != null
? mCurrentOperation.mClientMonitor.getProtoEnum() : BiometricsProto.CM_NONE);
proto.write(BiometricSchedulerProto.TOTAL_OPERATIONS, mTotalOperationsHandled);
Slog.d(getTag(), "Total operations: " + mTotalOperationsHandled);
for (int i = 0; i < mRecentOperations.size(); i++) {
Slog.d(getTag(), "Operation: " + mRecentOperations.get(i));
proto.write(BiometricSchedulerProto.RECENT_OPERATIONS, mRecentOperations.get(i));
}
proto.flush();
if (clearSchedulerBuffer) {
mRecentOperations.clear();
}
return proto.getBytes();
}
/**
* Clears the scheduler of anything work-related. This should be used for example when the
* HAL dies.

View File

@@ -24,6 +24,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import java.util.Arrays;
/**
@@ -106,4 +108,9 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> {
false /* enrollSuccessful */);
super.onError(error, vendorCode);
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_ENROLL;
}
}

View File

@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
public abstract class GenerateChallengeClient<T> extends HalClientMonitor<T> {
private static final String TAG = "GenerateChallengeClient";
@@ -50,4 +52,9 @@ public abstract class GenerateChallengeClient<T> extends HalClientMonitor<T> {
startHalOperation();
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_GENERATE_CHALLENGE;
}
}

View File

@@ -24,6 +24,7 @@ import android.os.IBinder;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.BiometricsProto;
import java.util.ArrayList;
import java.util.List;
@@ -166,4 +167,9 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
}
((EnumerateConsumer) mCurrentTask).onEnumerationResult(identifier, remaining);
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_INTERNAL_CLEANUP;
}
}

View File

@@ -24,6 +24,7 @@ import android.os.IBinder;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.BiometricsProto;
import java.util.ArrayList;
import java.util.List;
@@ -123,4 +124,9 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
return mUnknownHALTemplates;
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_ENUMERATE;
}
}

View File

@@ -24,6 +24,8 @@ import android.hardware.biometrics.IInvalidationCallback;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import java.util.Map;
/**
@@ -70,4 +72,9 @@ public abstract class InvalidationClient<S extends BiometricAuthenticator.Identi
public void unableToStart() {
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_INVALIDATE;
}
}

View File

@@ -23,6 +23,8 @@ import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IInvalidationCallback;
import com.android.server.biometrics.BiometricsProto;
/**
* ClientMonitor subclass responsible for coordination of authenticatorId invalidation of other
* sensors. See {@link InvalidationClient} for the ClientMonitor subclass responsible for initiating
@@ -89,4 +91,9 @@ public class InvalidationRequesterClient<S extends BiometricAuthenticator.Identi
mBiometricManager.invalidateAuthenticatorIds(getTargetUserId(), getSensorId(),
mInvalidationCallback);
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_INVALIDATION_REQUESTER;
}
}

View File

@@ -25,6 +25,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import java.util.Map;
/**
@@ -90,4 +92,9 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier,
mCallback.onClientFinished(this, true /* success */);
}
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_REMOVE;
}
}

View File

@@ -21,6 +21,8 @@ import android.content.Context;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import com.android.server.biometrics.BiometricsProto;
public abstract class RevokeChallengeClient<T> extends HalClientMonitor<T> {
public RevokeChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@@ -42,4 +44,9 @@ public abstract class RevokeChallengeClient<T> extends HalClientMonitor<T> {
startHalOperation();
mCallback.onClientFinished(this, true /* success */);
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_REVOKE_CHALLENGE;
}
}

View File

@@ -52,8 +52,8 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
}
@Override
public byte[] dumpSensorServiceStateProto() throws RemoteException {
return mFaceService.dumpSensorServiceStateProto(mSensorId);
public byte[] dumpSensorServiceStateProto(boolean clearSchedulerBuffer) throws RemoteException {
return mFaceService.dumpSensorServiceStateProto(mSensorId, clearSchedulerBuffer);
}
@Override

View File

@@ -147,13 +147,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba
}
@Override
public byte[] dumpSensorServiceStateProto(int sensorId) {
public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ProtoOutputStream proto = new ProtoOutputStream();
final ServiceProvider provider = getProviderForSensor(sensorId);
if (provider != null) {
provider.dumpProtoState(sensorId, proto);
provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer);
}
proto.flush();
return proto.getBytes();
@@ -405,7 +405,7 @@ public class FaceService extends SystemService implements BiometricServiceCallba
final ProtoOutputStream proto = new ProtoOutputStream(fd);
for (ServiceProvider provider : mServiceProviders) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
provider.dumpProtoState(props.sensorId, proto);
provider.dumpProtoState(props.sensorId, proto, false);
}
}
proto.flush();

View File

@@ -121,7 +121,8 @@ public interface ServiceProvider {
void scheduleInternalCleanup(int sensorId, int userId);
void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto);
void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
boolean clearSchedulerBuffer);
void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);

View File

@@ -23,6 +23,7 @@ import android.hardware.biometrics.face.ISession;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.HalClientMonitor;
import java.util.Map;
@@ -65,4 +66,9 @@ class FaceGetAuthenticatorIdClient extends HalClientMonitor<ISession> {
mAuthenticatorIds.put(getTargetUserId(), authenticatorId);
mCallback.onClientFinished(this, true /* success */);
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_GET_AUTHENTICATOR_ID;
}
}

View File

@@ -571,9 +571,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
@Override
public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
boolean clearSchedulerBuffer) {
if (mSensors.contains(sensorId)) {
mSensors.get(sensorId).dumpProtoState(sensorId, proto);
mSensors.get(sensorId).dumpProtoState(sensorId, proto, clearSchedulerBuffer);
}
}

View File

@@ -25,6 +25,7 @@ import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutCache;
@@ -81,4 +82,9 @@ public class FaceResetLockoutClient extends HalClientMonitor<ISession> {
mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
mCallback.onClientFinished(this, true /* success */);
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_RESET_LOCKOUT;
}
}

View File

@@ -486,12 +486,13 @@ public class Sensor implements IBinder.DeathRecipient {
mTestHalEnabled = enabled;
}
void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
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.IS_BUSY, mScheduler.getCurrentClient() != null);
proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
for (UserInfo user : UserManager.get(mContext).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();

View File

@@ -767,12 +767,13 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
@Override
public void dumpProtoState(int sensorId, ProtoOutputStream proto) {
public void dumpProtoState(int sensorId, 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.IS_BUSY, mScheduler.getCurrentClient() != null);
proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
for (UserInfo user : UserManager.get(mContext).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();

View File

@@ -27,6 +27,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -88,4 +89,9 @@ public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> {
boolean getValue() {
return mValue;
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_GET_FEATURE;
}
}

View File

@@ -23,6 +23,7 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.HalClientMonitor;
import java.util.ArrayList;
@@ -71,4 +72,9 @@ public class FaceResetLockoutClient extends HalClientMonitor<IBiometricsFace> {
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_RESET_LOCKOUT;
}
}

View File

@@ -25,6 +25,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -88,4 +89,9 @@ public class FaceSetFeatureClient extends HalClientMonitor<IBiometricsFace> {
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_SET_FEATURE;
}
}

View File

@@ -24,6 +24,7 @@ import android.os.Environment;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.HalClientMonitor;
import java.io.File;
@@ -91,4 +92,9 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_UPDATE_ACTIVE_USER;
}
}

View File

@@ -53,8 +53,8 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
}
@Override
public byte[] dumpSensorServiceStateProto() throws RemoteException {
return mFingerprintService.dumpSensorServiceStateProto(mSensorId);
public byte[] dumpSensorServiceStateProto(boolean clearSchedulerBuffer) throws RemoteException {
return mFingerprintService.dumpSensorServiceStateProto(mSensorId, clearSchedulerBuffer);
}
@Override

View File

@@ -116,13 +116,13 @@ public class FingerprintService extends SystemService implements BiometricServic
}
@Override
public byte[] dumpSensorServiceStateProto(int sensorId) {
public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ProtoOutputStream proto = new ProtoOutputStream();
final ServiceProvider provider = getProviderForSensor(sensorId);
if (provider != null) {
provider.dumpProtoState(sensorId, proto);
provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer);
}
proto.flush();
return proto.getBytes();
@@ -419,7 +419,7 @@ public class FingerprintService extends SystemService implements BiometricServic
for (ServiceProvider provider : mServiceProviders) {
for (FingerprintSensorPropertiesInternal props
: provider.getSensorProperties()) {
provider.dumpProtoState(props.sensorId, proto);
provider.dumpProtoState(props.sensorId, proto, false);
}
}
proto.flush();

View File

@@ -128,7 +128,8 @@ public interface ServiceProvider {
void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller);
void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto);
void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
boolean clearSchedulerBuffer);
void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);

View File

@@ -27,6 +27,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
@@ -91,4 +92,9 @@ class FingerprintDetectClient extends AcquisitionClient<ISession> {
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_DETECT_INTERACTION;
}
}

View File

@@ -23,6 +23,7 @@ import android.hardware.biometrics.fingerprint.ISession;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.HalClientMonitor;
import java.util.Map;
@@ -65,4 +66,9 @@ class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<ISession> {
mAuthenticatorIds.put(getTargetUserId(), authenticatorId);
mCallback.onClientFinished(this, true /* success */);
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_GET_AUTHENTICATOR_ID;
}
}

View File

@@ -627,9 +627,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
@Override
public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
boolean clearSchedulerBuffer) {
if (mSensors.contains(sensorId)) {
mSensors.get(sensorId).dumpProtoState(sensorId, proto);
mSensors.get(sensorId).dumpProtoState(sensorId, proto, clearSchedulerBuffer);
}
}

View File

@@ -25,6 +25,7 @@ import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutCache;
@@ -76,4 +77,9 @@ class FingerprintResetLockoutClient extends HalClientMonitor<ISession> {
mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
mCallback.onClientFinished(this, true /* success */);
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_RESET_LOCKOUT;
}
}

View File

@@ -481,12 +481,13 @@ class Sensor implements IBinder.DeathRecipient {
mTestHalEnabled = enabled;
}
void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
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.FINGERPRINT);
proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
for (UserInfo user : UserManager.get(mContext).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();

View File

@@ -714,12 +714,13 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
@Override
public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
public 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.FINGERPRINT);
proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
for (UserInfo user : UserManager.get(mContext).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();

View File

@@ -28,6 +28,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -123,4 +124,9 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
Slog.e(TAG, "Remote exception when sending onDetected", e);
}
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_DETECT_INTERACTION;
}
}

View File

@@ -26,6 +26,7 @@ import android.os.RemoteException;
import android.os.SELinux;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.HalClientMonitor;
import java.io.File;
@@ -121,4 +122,9 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_UPDATE_ACTIVE_USER;
}
}

View File

@@ -50,7 +50,7 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub {
}
@Override
public byte[] dumpSensorServiceStateProto() throws RemoteException {
public byte[] dumpSensorServiceStateProto(boolean clearSchedulerBuffer) throws RemoteException {
return null;
}

View File

@@ -34,11 +34,14 @@ import android.hardware.biometrics.IBiometricService;
import android.os.Binder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.util.proto.ProtoOutputStream;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.server.biometrics.nano.BiometricSchedulerProto;
import com.android.server.biometrics.nano.BiometricsProto;
import com.android.server.biometrics.sensors.BiometricScheduler.Operation;
import org.junit.Before;
@@ -52,6 +55,7 @@ public class BiometricSchedulerTest {
private static final String TAG = "BiometricSchedulerTest";
private static final int TEST_SENSOR_ID = 1;
private static final int LOG_NUM_RECENT_OPERATIONS = 2;
private BiometricScheduler mScheduler;
private IBinder mToken;
@@ -66,7 +70,7 @@ public class BiometricSchedulerTest {
MockitoAnnotations.initMocks(this);
mToken = new Binder();
mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */,
mBiometricService);
mBiometricService, LOG_NUM_RECENT_OPERATIONS);
}
@Test
@@ -186,6 +190,88 @@ public class BiometricSchedulerTest {
assertNull(mScheduler.mCurrentOperation);
}
@Test
public void testProtoDump_singleCurrentOperation() throws Exception {
// Nothing so far
BiometricSchedulerProto bsp = getDump(true /* clearSchedulerBuffer */);
assertEquals(BiometricsProto.CM_NONE, bsp.currentOperation);
assertEquals(0, bsp.totalOperations);
assertEquals(0, bsp.recentOperations.length);
// Pretend the scheduler is busy enrolling, and check the proto dump again.
final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
() -> mock(Object.class), BiometricsProto.CM_ENROLL);
mScheduler.scheduleClientMonitor(client);
waitForIdle();
bsp = getDump(true /* clearSchedulerBuffer */);
assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation);
// No operations have completed yet
assertEquals(0, bsp.totalOperations);
assertEquals(0, bsp.recentOperations.length);
// Finish this operation, so the next scheduled one can start
client.getCallback().onClientFinished(client, true);
}
@Test
public void testProtoDump_fifo() throws Exception {
// Add the first operation
final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
() -> mock(Object.class), BiometricsProto.CM_ENROLL);
mScheduler.scheduleClientMonitor(client);
waitForIdle();
BiometricSchedulerProto bsp = getDump(false /* clearSchedulerBuffer */);
assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation);
// No operations have completed yet
assertEquals(0, bsp.totalOperations);
assertEquals(0, bsp.recentOperations.length);
// Finish this operation, so the next scheduled one can start
client.getCallback().onClientFinished(client, true);
// Add another operation
final TestClientMonitor2 client2 = new TestClientMonitor2(mContext, mToken,
() -> mock(Object.class), BiometricsProto.CM_REMOVE);
mScheduler.scheduleClientMonitor(client2);
waitForIdle();
bsp = getDump(false /* clearSchedulerBuffer */);
assertEquals(BiometricsProto.CM_REMOVE, bsp.currentOperation);
assertEquals(1, bsp.totalOperations); // Enroll finished
assertEquals(1, bsp.recentOperations.length);
assertEquals(BiometricsProto.CM_ENROLL, bsp.recentOperations[0]);
client2.getCallback().onClientFinished(client2, true);
// And another operation
final TestClientMonitor2 client3 = new TestClientMonitor2(mContext, mToken,
() -> mock(Object.class), BiometricsProto.CM_AUTHENTICATE);
mScheduler.scheduleClientMonitor(client3);
waitForIdle();
bsp = getDump(false /* clearSchedulerBuffer */);
assertEquals(BiometricsProto.CM_AUTHENTICATE, bsp.currentOperation);
assertEquals(2, bsp.totalOperations);
assertEquals(2, bsp.recentOperations.length);
assertEquals(BiometricsProto.CM_ENROLL, bsp.recentOperations[0]);
assertEquals(BiometricsProto.CM_REMOVE, bsp.recentOperations[1]);
// Finish the last operation, and check that the first operation is removed from the FIFO.
// The test initializes the scheduler with "LOG_NUM_RECENT_OPERATIONS = 2" :)
client3.getCallback().onClientFinished(client3, true);
waitForIdle();
bsp = getDump(true /* clearSchedulerBuffer */);
assertEquals(3, bsp.totalOperations);
assertEquals(2, bsp.recentOperations.length);
assertEquals(BiometricsProto.CM_REMOVE, bsp.recentOperations[0]);
assertEquals(BiometricsProto.CM_AUTHENTICATE, bsp.recentOperations[1]);
// Nothing is currently running anymore
assertEquals(BiometricsProto.CM_NONE, bsp.currentOperation);
// RecentOperations queue is cleared (by the previous dump)
bsp = getDump(true /* clearSchedulerBuffer */);
assertEquals(0, bsp.recentOperations.length);
}
private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
}
private static class BiometricPromptClientMonitor extends AuthenticationClient<Object> {
public BiometricPromptClientMonitor(@NonNull Context context, @NonNull IBinder token,
@@ -207,6 +293,21 @@ public class BiometricSchedulerTest {
}
}
private static class TestClientMonitor2 extends TestClientMonitor {
private final int mProtoEnum;
public TestClientMonitor2(@NonNull Context context, @NonNull IBinder token,
@NonNull LazyDaemon<Object> lazyDaemon, int protoEnum) {
super(context, token, lazyDaemon);
mProtoEnum = protoEnum;
}
@Override
public int getProtoEnum() {
return mProtoEnum;
}
}
private static class TestClientMonitor extends HalClientMonitor<Object> {
private boolean mUnableToStart;
private boolean mStarted;
@@ -229,6 +330,13 @@ public class BiometricSchedulerTest {
mUnableToStart = true;
}
@Override
public int getProtoEnum() {
// Anything other than CM_NONE, which is used to represent "idle". Tests that need
// real proto enums should use TestClientMonitor2
return BiometricsProto.CM_UPDATE_ACTIVE_USER;
}
@Override
public void start(@NonNull Callback callback) {
super.start(callback);