Merge "4/n: Make CoexCoordinator multi-sensor-aware" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
c4815d0f2d
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.IBiometricService;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
@@ -48,8 +49,11 @@ import java.util.Locale;
|
||||
|
||||
/**
|
||||
* A scheduler for biometric HAL operations. Maintains a queue of {@link BaseClientMonitor}
|
||||
* operations, without caring about its implementation details. Operations may perform one or more
|
||||
* operations, without caring about its implementation details. Operations may perform zero or more
|
||||
* interactions with the HAL before finishing.
|
||||
*
|
||||
* We currently assume (and require) that each biometric sensor have its own instance of a
|
||||
* {@link BiometricScheduler}. See {@link CoexCoordinator}.
|
||||
*/
|
||||
public class BiometricScheduler {
|
||||
|
||||
@@ -57,6 +61,55 @@ public class BiometricScheduler {
|
||||
// Number of recent operations to keep in our logs for dumpsys
|
||||
protected static final int LOG_NUM_RECENT_OPERATIONS = 50;
|
||||
|
||||
/**
|
||||
* Unknown sensor type. This should never be used, and is a sign that something is wrong during
|
||||
* initialization.
|
||||
*/
|
||||
public static final int SENSOR_TYPE_UNKNOWN = 0;
|
||||
|
||||
/**
|
||||
* Face authentication.
|
||||
*/
|
||||
public static final int SENSOR_TYPE_FACE = 1;
|
||||
|
||||
/**
|
||||
* Any UDFPS type. See {@link FingerprintSensorPropertiesInternal#isAnyUdfpsType()}.
|
||||
*/
|
||||
public static final int SENSOR_TYPE_UDFPS = 2;
|
||||
|
||||
/**
|
||||
* Any other fingerprint sensor. We can add additional definitions in the future when necessary.
|
||||
*/
|
||||
public static final int SENSOR_TYPE_FP_OTHER = 3;
|
||||
|
||||
@IntDef({SENSOR_TYPE_UNKNOWN, SENSOR_TYPE_FACE, SENSOR_TYPE_UDFPS, SENSOR_TYPE_FP_OTHER})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface SensorType {}
|
||||
|
||||
public static @SensorType int sensorTypeFromFingerprintProperties(
|
||||
@NonNull FingerprintSensorPropertiesInternal props) {
|
||||
if (props.isAnyUdfpsType()) {
|
||||
return SENSOR_TYPE_UDFPS;
|
||||
}
|
||||
|
||||
return SENSOR_TYPE_FP_OTHER;
|
||||
}
|
||||
|
||||
public static String sensorTypeToString(@SensorType int sensorType) {
|
||||
switch (sensorType) {
|
||||
case SENSOR_TYPE_UNKNOWN:
|
||||
return "Unknown";
|
||||
case SENSOR_TYPE_FACE:
|
||||
return "Face";
|
||||
case SENSOR_TYPE_UDFPS:
|
||||
return "Udfps";
|
||||
case SENSOR_TYPE_FP_OTHER:
|
||||
return "OtherFp";
|
||||
default:
|
||||
return "UnknownUnknown";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains all the necessary information for a HAL operation.
|
||||
*/
|
||||
@@ -207,6 +260,7 @@ public class BiometricScheduler {
|
||||
}
|
||||
|
||||
@NonNull protected final String mBiometricTag;
|
||||
private final @SensorType int mSensorType;
|
||||
@Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
|
||||
@NonNull private final IBiometricService mBiometricService;
|
||||
@NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
@@ -218,6 +272,7 @@ public class BiometricScheduler {
|
||||
private int mTotalOperationsHandled;
|
||||
private final int mRecentOperationsLimit;
|
||||
@NonNull private final List<Integer> mRecentOperations;
|
||||
@NonNull private final CoexCoordinator mCoexCoordinator;
|
||||
|
||||
// Internal callback, notified when an operation is complete. Notifies the requester
|
||||
// that the operation is complete, before performing internal scheduler work (such as
|
||||
@@ -226,6 +281,12 @@ public class BiometricScheduler {
|
||||
@Override
|
||||
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
|
||||
Slog.d(getTag(), "[Started] " + clientMonitor);
|
||||
|
||||
if (clientMonitor instanceof AuthenticationClient) {
|
||||
mCoexCoordinator.addAuthenticationClient(mSensorType,
|
||||
(AuthenticationClient<?>) clientMonitor);
|
||||
}
|
||||
|
||||
if (mCurrentOperation.mClientCallback != null) {
|
||||
mCurrentOperation.mClientCallback.onClientStarted(clientMonitor);
|
||||
}
|
||||
@@ -248,6 +309,11 @@ public class BiometricScheduler {
|
||||
}
|
||||
|
||||
Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success);
|
||||
if (clientMonitor instanceof AuthenticationClient) {
|
||||
mCoexCoordinator.removeAuthenticationClient(mSensorType,
|
||||
(AuthenticationClient<?>) clientMonitor);
|
||||
}
|
||||
|
||||
mCurrentOperation.mState = Operation.STATE_FINISHED;
|
||||
|
||||
if (mCurrentOperation.mClientCallback != null) {
|
||||
@@ -271,10 +337,12 @@ public class BiometricScheduler {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
BiometricScheduler(@NonNull String tag,
|
||||
BiometricScheduler(@NonNull String tag, @SensorType int sensorType,
|
||||
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
|
||||
@NonNull IBiometricService biometricService, int recentOperationsLimit) {
|
||||
@NonNull IBiometricService biometricService, int recentOperationsLimit,
|
||||
@NonNull CoexCoordinator coexCoordinator) {
|
||||
mBiometricTag = tag;
|
||||
mSensorType = sensorType;
|
||||
mInternalCallback = new InternalCallback();
|
||||
mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
|
||||
mPendingOperations = new ArrayDeque<>();
|
||||
@@ -282,6 +350,7 @@ public class BiometricScheduler {
|
||||
mCrashStates = new ArrayDeque<>();
|
||||
mRecentOperationsLimit = recentOperationsLimit;
|
||||
mRecentOperations = new ArrayList<>();
|
||||
mCoexCoordinator = coexCoordinator;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,10 +359,11 @@ public class BiometricScheduler {
|
||||
* @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures
|
||||
* (such as fingerprint swipe).
|
||||
*/
|
||||
public BiometricScheduler(@NonNull String tag,
|
||||
public BiometricScheduler(@NonNull String tag, @SensorType int sensorType,
|
||||
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
|
||||
this(tag, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS);
|
||||
this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS,
|
||||
CoexCoordinator.getInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -645,6 +715,7 @@ public class BiometricScheduler {
|
||||
|
||||
public void dump(PrintWriter pw) {
|
||||
pw.println("Dump of BiometricScheduler " + getTag());
|
||||
pw.println("Type: " + mSensorType);
|
||||
pw.println("Current operation: " + mCurrentOperation);
|
||||
pw.println("Pending operations: " + mPendingOperations.size());
|
||||
for (Operation operation : mPendingOperations) {
|
||||
|
||||
@@ -16,7 +16,13 @@
|
||||
|
||||
package com.android.server.biometrics.sensors;
|
||||
|
||||
import static com.android.server.biometrics.sensors.BiometricScheduler.sensorTypeToString;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Singleton that contains the core logic for determining if haptics and authentication callbacks
|
||||
@@ -27,6 +33,7 @@ import android.annotation.NonNull;
|
||||
public class CoexCoordinator {
|
||||
|
||||
private static final String TAG = "BiometricCoexCoordinator";
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
/**
|
||||
* Callback interface notifying the owner of "results" from the CoexCoordinator's business
|
||||
@@ -47,10 +54,6 @@ public class CoexCoordinator {
|
||||
|
||||
private static CoexCoordinator sInstance;
|
||||
|
||||
private CoexCoordinator() {
|
||||
// Singleton
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static CoexCoordinator getInstance() {
|
||||
if (sInstance == null) {
|
||||
@@ -59,6 +62,43 @@ public class CoexCoordinator {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
// SensorType to AuthenticationClient map
|
||||
private final Map<Integer, AuthenticationClient<?>> mClientMap;
|
||||
|
||||
private CoexCoordinator() {
|
||||
// Singleton
|
||||
mClientMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public void addAuthenticationClient(@BiometricScheduler.SensorType int sensorType,
|
||||
@NonNull AuthenticationClient<?> client) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "addAuthenticationClient(" + sensorTypeToString(sensorType) + ")"
|
||||
+ ", client: " + client);
|
||||
}
|
||||
|
||||
if (mClientMap.containsKey(sensorType)) {
|
||||
Slog.w(TAG, "Overwriting existing client: " + mClientMap.get(sensorType)
|
||||
+ " with new client: " + client);
|
||||
}
|
||||
|
||||
mClientMap.put(sensorType, client);
|
||||
}
|
||||
|
||||
public void removeAuthenticationClient(@BiometricScheduler.SensorType int sensorType,
|
||||
@NonNull AuthenticationClient<?> client) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "removeAuthenticationClient(" + sensorTypeToString(sensorType) + ")"
|
||||
+ ", client: " + client);
|
||||
}
|
||||
|
||||
if (!mClientMap.containsKey(sensorType)) {
|
||||
Slog.e(TAG, "sensorType: " + sensorType + " does not exist in map. Client: " + client);
|
||||
return;
|
||||
}
|
||||
mClientMap.remove(sensorType);
|
||||
}
|
||||
|
||||
public void onAuthenticationSucceeded(@NonNull AuthenticationClient<?> client,
|
||||
@NonNull Callback callback) {
|
||||
if (client.isBiometricPrompt()) {
|
||||
|
||||
@@ -83,24 +83,26 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
UserAwareBiometricScheduler(@NonNull String tag,
|
||||
UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
|
||||
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
|
||||
@NonNull IBiometricService biometricService,
|
||||
@NonNull CurrentUserRetriever currentUserRetriever,
|
||||
@NonNull UserSwitchCallback userSwitchCallback) {
|
||||
super(tag, gestureAvailabilityDispatcher, biometricService, LOG_NUM_RECENT_OPERATIONS);
|
||||
@NonNull UserSwitchCallback userSwitchCallback,
|
||||
@NonNull CoexCoordinator coexCoordinator) {
|
||||
super(tag, sensorType, gestureAvailabilityDispatcher, biometricService,
|
||||
LOG_NUM_RECENT_OPERATIONS, coexCoordinator);
|
||||
|
||||
mCurrentUserRetriever = currentUserRetriever;
|
||||
mUserSwitchCallback = userSwitchCallback;
|
||||
}
|
||||
|
||||
public UserAwareBiometricScheduler(@NonNull String tag,
|
||||
public UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
|
||||
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
|
||||
@NonNull CurrentUserRetriever currentUserRetriever,
|
||||
@NonNull UserSwitchCallback userSwitchCallback) {
|
||||
this(tag, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
|
||||
this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.BIOMETRIC_SERVICE)), currentUserRetriever,
|
||||
userSwitchCallback);
|
||||
userSwitchCallback, CoexCoordinator.getInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -494,7 +494,8 @@ public class Sensor {
|
||||
mToken = new Binder();
|
||||
mHandler = handler;
|
||||
mSensorProperties = sensorProperties;
|
||||
mScheduler = new UserAwareBiometricScheduler(tag, null /* gestureAvailabilityDispatcher */,
|
||||
mScheduler = new UserAwareBiometricScheduler(tag, BiometricScheduler.SENSOR_TYPE_FACE,
|
||||
null /* gestureAvailabilityDispatcher */,
|
||||
() -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
|
||||
new UserAwareBiometricScheduler.UserSwitchCallback() {
|
||||
@NonNull
|
||||
|
||||
@@ -355,7 +355,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
|
||||
public Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps,
|
||||
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
|
||||
this(context, sensorProps, lockoutResetDispatcher,
|
||||
new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */));
|
||||
new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
|
||||
null /* gestureAvailabilityTracker */));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -449,7 +449,9 @@ class Sensor {
|
||||
mHandler = handler;
|
||||
mSensorProperties = sensorProperties;
|
||||
mLockoutCache = new LockoutCache();
|
||||
mScheduler = new UserAwareBiometricScheduler(tag, gestureAvailabilityDispatcher,
|
||||
mScheduler = new UserAwareBiometricScheduler(tag,
|
||||
BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties),
|
||||
gestureAvailabilityDispatcher,
|
||||
() -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
|
||||
new UserAwareBiometricScheduler.UserSwitchCallback() {
|
||||
@NonNull
|
||||
|
||||
@@ -352,7 +352,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
|
||||
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
|
||||
final Handler handler = new Handler(Looper.getMainLooper());
|
||||
final BiometricScheduler scheduler =
|
||||
new BiometricScheduler(TAG, gestureAvailabilityDispatcher);
|
||||
new BiometricScheduler(TAG,
|
||||
BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps),
|
||||
gestureAvailabilityDispatcher);
|
||||
final HalResultController controller = new HalResultController(sensorProps.sensorId,
|
||||
context, handler,
|
||||
scheduler);
|
||||
|
||||
@@ -138,7 +138,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
|
||||
|
||||
TestableBiometricScheduler(@NonNull String tag,
|
||||
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
|
||||
super(tag, gestureAvailabilityDispatcher);
|
||||
super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher);
|
||||
mInternalCallback = new TestableInternalCallback();
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,9 @@ public class BiometricSchedulerTest {
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mToken = new Binder();
|
||||
mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */,
|
||||
mBiometricService, LOG_NUM_RECENT_OPERATIONS);
|
||||
mScheduler = new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_UNKNOWN,
|
||||
null /* gestureAvailabilityTracker */, mBiometricService, LOG_NUM_RECENT_OPERATIONS,
|
||||
CoexCoordinator.getInstance());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -74,6 +74,7 @@ public class UserAwareBiometricSchedulerTest {
|
||||
mUserStoppedCallback = new TestUserStoppedCallback();
|
||||
|
||||
mScheduler = new UserAwareBiometricScheduler(TAG,
|
||||
BiometricScheduler.SENSOR_TYPE_UNKNOWN,
|
||||
null /* gestureAvailabilityDispatcher */,
|
||||
mBiometricService,
|
||||
() -> mCurrentUserId,
|
||||
@@ -92,7 +93,8 @@ public class UserAwareBiometricSchedulerTest {
|
||||
return new TestStartUserClient(mContext, Object::new, mToken, newUserId,
|
||||
TEST_SENSOR_ID, mUserStartedCallback, mStartOperationsFinish);
|
||||
}
|
||||
});
|
||||
},
|
||||
CoexCoordinator.getInstance());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.server.biometrics.sensors.BiometricScheduler;
|
||||
import com.android.server.biometrics.sensors.LockoutCache;
|
||||
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
|
||||
import com.android.server.biometrics.sensors.LockoutTracker;
|
||||
@@ -78,6 +79,7 @@ public class SensorTest {
|
||||
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
|
||||
|
||||
mScheduler = new UserAwareBiometricScheduler(TAG,
|
||||
BiometricScheduler.SENSOR_TYPE_FACE,
|
||||
null /* gestureAvailabilityDispatcher */,
|
||||
() -> USER_ID,
|
||||
mUserSwitchCallback);
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.server.biometrics.sensors.BiometricScheduler;
|
||||
import com.android.server.biometrics.sensors.LockoutCache;
|
||||
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
|
||||
import com.android.server.biometrics.sensors.LockoutTracker;
|
||||
@@ -78,6 +79,7 @@ public class SensorTest {
|
||||
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
|
||||
|
||||
mScheduler = new UserAwareBiometricScheduler(TAG,
|
||||
BiometricScheduler.SENSOR_TYPE_FP_OTHER,
|
||||
null /* gestureAvailabilityDispatcher */,
|
||||
() -> USER_ID,
|
||||
mUserSwitchCallback);
|
||||
|
||||
Reference in New Issue
Block a user