5/n: Add support for initial coex cases
1) Adds a way to enable/disable this. By default, this is disabled on top of tree (so this change should be a no-op. By default, tests enable the advanced behavior. 2) Adds a dump to "adb shell dumpsys biometric" Bug: 193089985 Test: atest CoexCoordinatorTest Test: atest com.android.server.biometrics Test: adb shell dumpsys biometric (with and without secure setting) Change-Id: I9fe553e9d4b78e34a20b9130f854143a355275bc
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,4 +27,5 @@ public interface Udfps {
|
||||
void onPointerDown(int x, int y, float minor, float major);
|
||||
void onPointerUp();
|
||||
void onUiReady();
|
||||
boolean isPointerDown();
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user