Merge "5/n: Add support for initial coex cases" into sc-dev am: 30a9a9b5ec

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15329699

Change-Id: I9750720a2b54bb8351f14c2fe01c6eaef50425b5
This commit is contained in:
Kevin Chyn
2021-07-20 22:06:49 +00:00
committed by Automerger Merge Worker
12 changed files with 226 additions and 6 deletions

View File

@@ -73,6 +73,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.biometrics.sensors.CoexCoordinator;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -1100,6 +1101,12 @@ public class BiometricService extends SystemService {
}
return new ArrayList<>();
}
public boolean isAdvancedCoexLogicEnabled(Context context) {
return (Build.IS_USERDEBUG || Build.IS_ENG)
&& Settings.Secure.getInt(context.getContentResolver(),
CoexCoordinator.SETTING_ENABLE_NAME, 0) != 0;
}
}
/**
@@ -1126,6 +1133,12 @@ public class BiometricService extends SystemService {
mSettingObserver = mInjector.getSettingObserver(context, mHandler,
mEnabledOnKeyguardCallbacks);
// TODO(b/193089985) This logic lives here (outside of CoexCoordinator) so that it doesn't
// need to depend on context. We can remove this code once the advanced logic is enabled
// by default.
CoexCoordinator coexCoordinator = CoexCoordinator.getInstance();
coexCoordinator.setAdvancedLogicEnabled(injector.isAdvancedCoexLogicEnabled(context));
try {
injector.getActivityManagerService().registerUserSwitchObserver(
new UserSwitchObserver() {
@@ -1437,5 +1450,7 @@ public class BiometricService extends SystemService {
pw.println();
pw.println("CurrentSession: " + mCurrentAuthSession);
pw.println();
pw.println("CoexCoordinator: " + CoexCoordinator.getInstance().toString());
pw.println();
}
}

View File

@@ -359,7 +359,8 @@ public class BiometricScheduler {
* @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures
* (such as fingerprint swipe).
*/
public BiometricScheduler(@NonNull String tag, @SensorType int sensorType,
public BiometricScheduler(@NonNull String tag,
@SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS,

View File

@@ -16,11 +16,17 @@
package com.android.server.biometrics.sensors;
import static com.android.server.biometrics.sensors.BiometricScheduler.SENSOR_TYPE_FACE;
import static com.android.server.biometrics.sensors.BiometricScheduler.SENSOR_TYPE_UDFPS;
import static com.android.server.biometrics.sensors.BiometricScheduler.sensorTypeToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import java.util.HashMap;
import java.util.Map;
@@ -33,6 +39,8 @@ import java.util.Map;
public class CoexCoordinator {
private static final String TAG = "BiometricCoexCoordinator";
public static final String SETTING_ENABLE_NAME =
"com.android.server.biometrics.sensors.CoexCoordinator.enable";
private static final boolean DEBUG = true;
/**
@@ -54,16 +62,30 @@ public class CoexCoordinator {
private static CoexCoordinator sInstance;
/**
* @return a singleton instance.
*/
@NonNull
static CoexCoordinator getInstance() {
public static CoexCoordinator getInstance() {
if (sInstance == null) {
sInstance = new CoexCoordinator();
}
return sInstance;
}
@VisibleForTesting
public void setAdvancedLogicEnabled(boolean enabled) {
mAdvancedLogicEnabled = enabled;
}
@VisibleForTesting
void reset() {
mClientMap.clear();
}
// SensorType to AuthenticationClient map
private final Map<Integer, AuthenticationClient<?>> mClientMap;
private boolean mAdvancedLogicEnabled;
private CoexCoordinator() {
// Singleton
@@ -105,8 +127,33 @@ public class CoexCoordinator {
callback.sendHapticFeedback();
// For BP, BiometricService will add the authToken to Keystore.
callback.sendAuthenticationResult(false /* addAuthTokenIfStrong */);
} else if (isUnknownClient(client)) {
// Client doesn't exist in our map for some reason. Give the user feedback so the
// device doesn't feel like it's stuck. All other cases below can assume that the
// client exists in our map.
callback.sendHapticFeedback();
callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
} else if (mAdvancedLogicEnabled && client.isKeyguard()) {
if (isSingleAuthOnly(client)) {
// Single sensor authentication
callback.sendHapticFeedback();
callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
} else {
// Multi sensor authentication
AuthenticationClient<?> udfps = mClientMap.getOrDefault(SENSOR_TYPE_UDFPS, null);
if (isCurrentFaceAuth(client)) {
if (isPointerDown(udfps)) {
// Face auth success while UDFPS pointer down. No callback, no haptic.
// Feedback will be provided after UDFPS result.
} else {
callback.sendHapticFeedback();
callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
}
}
}
} else {
// Keyguard, FingerprintManager, FaceManager, etc
// Non-keyguard authentication. For example, Fingerprint Settings use of
// FingerprintManager for highlighting fingers
callback.sendHapticFeedback();
callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
}
@@ -123,4 +170,41 @@ public class CoexCoordinator {
callback.sendAuthenticationResult(false /* addAuthTokenIfStrong */);
}
}
private boolean isCurrentFaceAuth(@NonNull AuthenticationClient<?> client) {
return client == mClientMap.getOrDefault(SENSOR_TYPE_FACE, null);
}
private boolean isPointerDown(@Nullable AuthenticationClient<?> client) {
if (client instanceof Udfps) {
return ((Udfps) client).isPointerDown();
}
return false;
}
private boolean isUnknownClient(@NonNull AuthenticationClient<?> client) {
for (AuthenticationClient<?> c : mClientMap.values()) {
if (c == client) {
return false;
}
}
return true;
}
private boolean isSingleAuthOnly(@NonNull AuthenticationClient<?> client) {
if (mClientMap.values().size() != 1) {
return false;
}
for (AuthenticationClient<?> c : mClientMap.values()) {
if (c != client) {
return false;
}
}
return true;
}
public String toString() {
return "Enabled: " + mAdvancedLogicEnabled;
}
}

View File

@@ -494,8 +494,8 @@ public class Sensor {
mToken = new Binder();
mHandler = handler;
mSensorProperties = sensorProperties;
mScheduler = new UserAwareBiometricScheduler(tag, BiometricScheduler.SENSOR_TYPE_FACE,
null /* gestureAvailabilityDispatcher */,
mScheduler = new UserAwareBiometricScheduler(tag,
BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */,
() -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
new UserAwareBiometricScheduler.UserSwitchCallback() {
@NonNull

View File

@@ -27,4 +27,5 @@ public interface Udfps {
void onPointerDown(int x, int y, float minor, float major);
void onPointerUp();
void onUiReady();
boolean isPointerDown();
}

View File

@@ -55,6 +55,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
@NonNull private final LockoutCache mLockoutCache;
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ICancellationSignal mCancellationSignal;
private boolean mIsPointerDown;
FingerprintAuthenticationClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
@@ -143,6 +144,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
@Override
public void onPointerDown(int x, int y, float minor, float major) {
try {
mIsPointerDown = true;
getFreshDaemon().onPointerDown(0 /* pointerId */, x, y, minor, major);
if (getListener() != null) {
getListener().onUdfpsPointerDown(getSensorId());
@@ -155,6 +157,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
@Override
public void onPointerUp() {
try {
mIsPointerDown = false;
getFreshDaemon().onPointerUp(0 /* pointerId */);
if (getListener() != null) {
getListener().onUdfpsPointerUp(getSensorId());
@@ -164,6 +167,11 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
}
}
@Override
public boolean isPointerDown() {
return mIsPointerDown;
}
@Override
public void onUiReady() {
try {

View File

@@ -55,6 +55,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
private final @FingerprintManager.EnrollReason int mEnrollReason;
@Nullable private ICancellationSignal mCancellationSignal;
private final int mMaxTemplatesPerUser;
private boolean mIsPointerDown;
FingerprintEnrollClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
@@ -167,6 +168,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
@Override
public void onPointerDown(int x, int y, float minor, float major) {
try {
mIsPointerDown = true;
getFreshDaemon().onPointerDown(0 /* pointerId */, x, y, minor, major);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send pointer down", e);
@@ -176,12 +178,18 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
@Override
public void onPointerUp() {
try {
mIsPointerDown = false;
getFreshDaemon().onPointerUp(0 /* pointerId */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send pointer up", e);
}
}
@Override
public boolean isPointerDown() {
return mIsPointerDown;
}
@Override
public void onUiReady() {
try {

View File

@@ -138,7 +138,8 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
TestableBiometricScheduler(@NonNull String tag,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher);
super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER,
gestureAvailabilityDispatcher);
mInternalCallback = new TestableInternalCallback();
}

View File

@@ -52,6 +52,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
private final LockoutFrameworkImpl mLockoutFrameworkImpl;
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
private boolean mIsPointerDown;
FingerprintAuthenticationClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@@ -160,6 +161,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
@Override
public void onPointerDown(int x, int y, float minor, float major) {
mIsPointerDown = true;
UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
if (getListener() != null) {
try {
@@ -172,6 +174,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
@Override
public void onPointerUp() {
mIsPointerDown = false;
UdfpsHelper.onFingerUp(getFreshDaemon());
if (getListener() != null) {
try {
@@ -182,6 +185,11 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
}
}
@Override
public boolean isPointerDown() {
return mIsPointerDown;
}
@Override
public void onUiReady() {
// Unsupported in HIDL.

View File

@@ -49,6 +49,7 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
private final boolean mIsStrongBiometric;
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
private boolean mIsPointerDown;
public FingerprintDetectClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@@ -99,14 +100,21 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
@Override
public void onPointerDown(int x, int y, float minor, float major) {
mIsPointerDown = true;
UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
}
@Override
public void onPointerUp() {
mIsPointerDown = false;
UdfpsHelper.onFingerUp(getFreshDaemon());
}
@Override
public boolean isPointerDown() {
return mIsPointerDown;
}
@Override
public void onUiReady() {
// Unsupported in HIDL.

View File

@@ -52,6 +52,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private final ISidefpsController mSidefpsController;
private final @FingerprintManager.EnrollReason int mEnrollReason;
private boolean mIsPointerDown;
FingerprintEnrollClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@@ -157,14 +158,21 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
@Override
public void onPointerDown(int x, int y, float minor, float major) {
mIsPointerDown = true;
UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
}
@Override
public void onPointerUp() {
mIsPointerDown = false;
UdfpsHelper.onFingerUp(getFreshDaemon());
}
@Override
public boolean isPointerDown() {
return mIsPointerDown;
}
@Override
public void onUiReady() {
// Unsupported in HIDL.

View File

@@ -16,17 +16,24 @@
package com.android.server.biometrics.sensors;
import static com.android.server.biometrics.sensors.BiometricScheduler.SENSOR_TYPE_FACE;
import static com.android.server.biometrics.sensors.BiometricScheduler.SENSOR_TYPE_UDFPS;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import android.content.Context;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -36,8 +43,12 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class CoexCoordinatorTest {
private static final String TAG = "CoexCoordinatorTest";
private CoexCoordinator mCoexCoordinator;
@Mock
private Context mContext;
@Mock
private CoexCoordinator.Callback mCallback;
@@ -45,13 +56,18 @@ public class CoexCoordinatorTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mCoexCoordinator = CoexCoordinator.getInstance();
mCoexCoordinator.setAdvancedLogicEnabled(true);
}
@Test
public void testBiometricPrompt_authSuccess() {
mCoexCoordinator.reset();
AuthenticationClient<?> client = mock(AuthenticationClient.class);
when(client.isBiometricPrompt()).thenReturn(true);
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
mCoexCoordinator.onAuthenticationSucceeded(client, mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
@@ -59,9 +75,13 @@ public class CoexCoordinatorTest {
@Test
public void testBiometricPrompt_authReject_whenNotLockedOut() {
mCoexCoordinator.reset();
AuthenticationClient<?> client = mock(AuthenticationClient.class);
when(client.isBiometricPrompt()).thenReturn(true);
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
mCoexCoordinator.onAuthenticationRejected(client, LockoutTracker.LOCKOUT_NONE, mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
@@ -69,11 +89,69 @@ public class CoexCoordinatorTest {
@Test
public void testBiometricPrompt_authReject_whenLockedOut() {
mCoexCoordinator.reset();
AuthenticationClient<?> client = mock(AuthenticationClient.class);
when(client.isBiometricPrompt()).thenReturn(true);
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
mCoexCoordinator.onAuthenticationRejected(client, LockoutTracker.LOCKOUT_TIMED, mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
}
@Test
public void testKeyguard_faceAuthOnly_success() {
mCoexCoordinator.reset();
AuthenticationClient<?> client = mock(AuthenticationClient.class);
when(client.isKeyguard()).thenReturn(true);
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
mCoexCoordinator.onAuthenticationSucceeded(client, mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
}
@Test
public void testKeyguard_faceAuth_udfpsNotTouching_faceSuccess() {
mCoexCoordinator.reset();
AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
when(faceClient.isKeyguard()).thenReturn(true);
AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
withSettings().extraInterfaces(Udfps.class));
when(udfpsClient.isKeyguard()).thenReturn(true);
when(((Udfps) udfpsClient).isPointerDown()).thenReturn(false);
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
mCoexCoordinator.onAuthenticationSucceeded(faceClient, mCallback);
verify(mCallback).sendHapticFeedback();
verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
}
@Test
public void testKeyguard_faceAuth_udfpsTouching_faceSuccess() {
mCoexCoordinator.reset();
AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
when(faceClient.isKeyguard()).thenReturn(true);
AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
withSettings().extraInterfaces(Udfps.class));
when(udfpsClient.isKeyguard()).thenReturn(true);
when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true);
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
mCoexCoordinator.onAuthenticationSucceeded(faceClient, mCallback);
verify(mCallback, never()).sendHapticFeedback();
verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
}
}