Merge "Biometric Scheduler Watchdog"
This commit is contained in:
@@ -768,6 +768,20 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a watchdog.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
|
||||
public void scheduleWatchdog() {
|
||||
try {
|
||||
mService.scheduleWatchdog();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelEnrollment(long requestId) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
|
||||
@@ -172,4 +172,9 @@ interface IFaceService {
|
||||
|
||||
// Registers BiometricStateListener.
|
||||
void registerBiometricStateListener(IBiometricStateListener listener);
|
||||
|
||||
// Internal operation used to clear face biometric scheduler.
|
||||
// Ensures that the scheduler is not stuck.
|
||||
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
|
||||
void scheduleWatchdog();
|
||||
}
|
||||
|
||||
@@ -1124,6 +1124,20 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
|
||||
return BIOMETRIC_LOCKOUT_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a watchdog.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
|
||||
public void scheduleWatchdog() {
|
||||
try {
|
||||
mService.scheduleWatchdog();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@@ -208,4 +208,9 @@ interface IFingerprintService {
|
||||
// Sends a power button pressed event to all listeners.
|
||||
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
|
||||
oneway void onPowerPressed();
|
||||
|
||||
// Internal operation used to clear fingerprint biometric scheduler.
|
||||
// Ensures that the scheduler is not stuck.
|
||||
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
|
||||
void scheduleWatchdog();
|
||||
}
|
||||
|
||||
@@ -106,7 +106,6 @@ import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
@@ -3796,4 +3795,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|
||||
}
|
||||
mListenModels.print(pw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a watchdog for the face and fingerprint BiometricScheduler.
|
||||
* Cancels all operations in the scheduler if it is hung for 10 seconds.
|
||||
*/
|
||||
public void startBiometricWatchdog() {
|
||||
if (mFaceManager != null) {
|
||||
mFaceManager.scheduleWatchdog();
|
||||
}
|
||||
if (mFpm != null) {
|
||||
mFpm.scheduleWatchdog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -751,6 +751,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
if (DEBUG) Log.d(TAG, "keyguardGone");
|
||||
mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(false);
|
||||
mKeyguardDisplayManager.hide();
|
||||
mUpdateMonitor.startBiometricWatchdog();
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
@@ -293,4 +294,30 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
|
||||
+ ", requestId=" + getRequestId()
|
||||
+ ", userId=" + getTargetUserId() + "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels this ClientMonitor
|
||||
*/
|
||||
public void cancel() {
|
||||
cancelWithoutStarting(mCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels this ClientMonitor without starting
|
||||
* @param callback
|
||||
*/
|
||||
public void cancelWithoutStarting(@NonNull ClientMonitorCallback callback) {
|
||||
Slog.d(TAG, "cancelWithoutStarting: " + this);
|
||||
|
||||
final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED;
|
||||
try {
|
||||
ClientMonitorCallbackConverter listener = getListener();
|
||||
if (listener != null) {
|
||||
listener.onError(getSensorId(), getCookie(), errorCode, 0 /* vendorCode */);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Failed to invoke sendError", e);
|
||||
}
|
||||
callback.onClientFinished(this, true /* success */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,4 +543,37 @@ public class BiometricScheduler {
|
||||
mPendingOperations.clear();
|
||||
mCurrentOperation = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks all pending operations as canceling and cancels the current
|
||||
* operation.
|
||||
*/
|
||||
private void clearScheduler() {
|
||||
if (mCurrentOperation == null) {
|
||||
return;
|
||||
}
|
||||
for (BiometricSchedulerOperation pendingOperation : mPendingOperations) {
|
||||
Slog.d(getTag(), "[Watchdog cancelling pending] "
|
||||
+ pendingOperation.getClientMonitor());
|
||||
pendingOperation.markCanceling();
|
||||
}
|
||||
Slog.d(getTag(), "[Watchdog cancelling current] "
|
||||
+ mCurrentOperation.getClientMonitor());
|
||||
mCurrentOperation.cancel(mHandler, getInternalCallback());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the timeout for the watchdog.
|
||||
*/
|
||||
public void startWatchdog() {
|
||||
if (mCurrentOperation == null) {
|
||||
return;
|
||||
}
|
||||
final BiometricSchedulerOperation mOperation = mCurrentOperation;
|
||||
mHandler.postDelayed(() -> {
|
||||
if (mOperation == mCurrentOperation) {
|
||||
clearScheduler();
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ public class BiometricSchedulerOperation {
|
||||
|
||||
/** Flags this operation as canceled, if possible, but does not cancel it until started. */
|
||||
public boolean markCanceling() {
|
||||
if (mState == STATE_WAITING_IN_QUEUE && isInterruptable()) {
|
||||
if (mState == STATE_WAITING_IN_QUEUE) {
|
||||
mState = STATE_WAITING_IN_QUEUE_CANCELING;
|
||||
return true;
|
||||
}
|
||||
@@ -287,10 +287,6 @@ public class BiometricSchedulerOperation {
|
||||
}
|
||||
|
||||
final int currentState = mState;
|
||||
if (!isInterruptable()) {
|
||||
Slog.w(TAG, "Cannot cancel - operation not interruptable: " + this);
|
||||
return;
|
||||
}
|
||||
if (currentState == STATE_STARTED_CANCELING) {
|
||||
Slog.w(TAG, "Cannot cancel - already invoked for operation: " + this);
|
||||
return;
|
||||
@@ -301,10 +297,10 @@ public class BiometricSchedulerOperation {
|
||||
|| currentState == STATE_WAITING_IN_QUEUE_CANCELING
|
||||
|| currentState == STATE_WAITING_FOR_COOKIE) {
|
||||
Slog.d(TAG, "[Cancelling] Current client (without start): " + mClientMonitor);
|
||||
((Interruptable) mClientMonitor).cancelWithoutStarting(getWrappedCallback(callback));
|
||||
mClientMonitor.cancelWithoutStarting(getWrappedCallback(callback));
|
||||
} else {
|
||||
Slog.d(TAG, "[Cancelling] Current client: " + mClientMonitor);
|
||||
((Interruptable) mClientMonitor).cancel();
|
||||
mClientMonitor.cancel();
|
||||
}
|
||||
|
||||
// forcibly finish this client if the HAL does not acknowledge within the timeout
|
||||
|
||||
@@ -183,6 +183,18 @@ public class FaceService extends SystemService {
|
||||
receiver, opPackageName, disabledFeatures, previewSurface, debugConsent);
|
||||
}
|
||||
|
||||
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
|
||||
@Override
|
||||
public void scheduleWatchdog() {
|
||||
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
|
||||
if (provider == null) {
|
||||
Slog.w(TAG, "Null provider for scheduling watchdog");
|
||||
return;
|
||||
}
|
||||
|
||||
provider.second.scheduleWatchdog(provider.first);
|
||||
}
|
||||
|
||||
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC)
|
||||
@Override // Binder call
|
||||
public long enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
|
||||
|
||||
@@ -128,4 +128,10 @@ public interface ServiceProvider extends BiometricServiceProvider<FaceSensorProp
|
||||
@NonNull String opPackageName);
|
||||
|
||||
void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args);
|
||||
|
||||
/**
|
||||
* Schedules watchdog for canceling hung operations
|
||||
* @param sensorId sensor ID of the associated operation
|
||||
*/
|
||||
default void scheduleWatchdog(int sensorId) {}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.android.server.biometrics.log.BiometricContext;
|
||||
import com.android.server.biometrics.log.BiometricLogger;
|
||||
import com.android.server.biometrics.sensors.AuthenticationClient;
|
||||
import com.android.server.biometrics.sensors.BaseClientMonitor;
|
||||
import com.android.server.biometrics.sensors.BiometricScheduler;
|
||||
import com.android.server.biometrics.sensors.ClientMonitorCallback;
|
||||
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
|
||||
import com.android.server.biometrics.sensors.InvalidationRequesterClient;
|
||||
@@ -661,4 +662,14 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
|
||||
void setTestHalEnabled(boolean enabled) {
|
||||
mTestHalEnabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleWatchdog(int sensorId) {
|
||||
Slog.d(getTag(), "Starting watchdog for face");
|
||||
final BiometricScheduler biometricScheduler = mSensors.get(sensorId).getScheduler();
|
||||
if (biometricScheduler == null) {
|
||||
return;
|
||||
}
|
||||
biometricScheduler.startWatchdog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -879,6 +879,18 @@ public class FingerprintService extends SystemService {
|
||||
provider.onPowerPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
|
||||
@Override
|
||||
public void scheduleWatchdog() {
|
||||
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
|
||||
if (provider == null) {
|
||||
Slog.w(TAG, "Null provider for scheduling watchdog");
|
||||
return;
|
||||
}
|
||||
|
||||
provider.second.scheduleWatchdog(provider.first);
|
||||
}
|
||||
};
|
||||
|
||||
public FingerprintService(Context context) {
|
||||
|
||||
@@ -140,4 +140,10 @@ public interface ServiceProvider extends
|
||||
@NonNull
|
||||
ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
|
||||
@NonNull String opPackageName);
|
||||
|
||||
/**
|
||||
* Schedules watchdog for canceling hung operations
|
||||
* @param sensorId sensor ID of the associated operation
|
||||
*/
|
||||
default void scheduleWatchdog(int sensorId) {}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ import com.android.server.biometrics.log.BiometricContext;
|
||||
import com.android.server.biometrics.log.BiometricLogger;
|
||||
import com.android.server.biometrics.sensors.AuthenticationClient;
|
||||
import com.android.server.biometrics.sensors.BaseClientMonitor;
|
||||
import com.android.server.biometrics.sensors.BiometricScheduler;
|
||||
import com.android.server.biometrics.sensors.BiometricStateCallback;
|
||||
import com.android.server.biometrics.sensors.ClientMonitorCallback;
|
||||
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
|
||||
@@ -779,4 +780,14 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleWatchdog(int sensorId) {
|
||||
Slog.d(getTag(), "Starting watchdog for fingerprint");
|
||||
final BiometricScheduler biometricScheduler = mSensors.get(sensorId).getScheduler();
|
||||
if (biometricScheduler == null) {
|
||||
return;
|
||||
}
|
||||
biometricScheduler.startWatchdog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.android.server.biometrics.sensors;
|
||||
|
||||
import static android.testing.TestableLooper.RunWithLooper;
|
||||
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static junit.framework.Assert.fail;
|
||||
@@ -24,8 +24,10 @@ import static junit.framework.Assert.fail;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -35,6 +37,7 @@ import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.withSettings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.BiometricAuthenticator;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.IBiometricService;
|
||||
import android.os.Binder;
|
||||
@@ -63,27 +66,25 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Presubmit
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
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;
|
||||
|
||||
@Mock
|
||||
private IBiometricService mBiometricService;
|
||||
|
||||
@Rule
|
||||
public final TestableContext mContext =
|
||||
new TestableContext(InstrumentationRegistry.getContext(), null);
|
||||
private BiometricScheduler mScheduler;
|
||||
private IBinder mToken;
|
||||
@Mock
|
||||
private IBiometricService mBiometricService;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -323,7 +324,7 @@ public class BiometricSchedulerTest {
|
||||
client1.getCallback().onClientFinished(client1, true /* success */);
|
||||
waitForIdle();
|
||||
verify(callback).onError(anyInt(), anyInt(),
|
||||
eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
|
||||
eq(BIOMETRIC_ERROR_CANCELED),
|
||||
eq(0) /* vendorCode */);
|
||||
assertNull(mScheduler.getCurrentClient());
|
||||
assertTrue(client1.isAlreadyDone());
|
||||
@@ -484,7 +485,7 @@ public class BiometricSchedulerTest {
|
||||
mScheduler.scheduleClientMonitor(interrupter);
|
||||
waitForIdle();
|
||||
|
||||
verify((Interruptable) interruptableMonitor).cancel();
|
||||
verify(interruptableMonitor).cancel();
|
||||
mScheduler.getInternalCallback().onClientFinished(interruptableMonitor, true /* success */);
|
||||
}
|
||||
|
||||
@@ -500,7 +501,7 @@ public class BiometricSchedulerTest {
|
||||
mScheduler.scheduleClientMonitor(interrupter);
|
||||
waitForIdle();
|
||||
|
||||
verify((Interruptable) interruptableMonitor, never()).cancel();
|
||||
verify(interruptableMonitor, never()).cancel();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -514,21 +515,180 @@ public class BiometricSchedulerTest {
|
||||
assertTrue(client.mDestroyed);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearBiometricQueue_clearsHungAuthOperation() {
|
||||
// Creating a hung client
|
||||
final TestableLooper looper = TestableLooper.get(this);
|
||||
final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
|
||||
final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
|
||||
lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */);
|
||||
final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
|
||||
|
||||
mScheduler.scheduleClientMonitor(client1, callback1);
|
||||
waitForIdle();
|
||||
|
||||
mScheduler.startWatchdog();
|
||||
waitForIdle();
|
||||
|
||||
//Checking client is hung
|
||||
verify(callback1).onClientStarted(client1);
|
||||
verify(callback1, never()).onClientFinished(any(), anyBoolean());
|
||||
assertNotNull(mScheduler.mCurrentOperation);
|
||||
assertEquals(0, mScheduler.getCurrentPendingCount());
|
||||
|
||||
looper.moveTimeForward(10000);
|
||||
waitForIdle();
|
||||
looper.moveTimeForward(3000);
|
||||
waitForIdle();
|
||||
|
||||
// The hung client did not honor this operation, verify onError and authenticated
|
||||
// were never called.
|
||||
assertFalse(client1.mOnErrorCalled);
|
||||
assertFalse(client1.mAuthenticateCalled);
|
||||
verify(callback1).onClientFinished(client1, false /* success */);
|
||||
assertNull(mScheduler.mCurrentOperation);
|
||||
assertEquals(0, mScheduler.getCurrentPendingCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthWorks_afterClearBiometricQueue() {
|
||||
// Creating a hung client
|
||||
final TestableLooper looper = TestableLooper.get(this);
|
||||
final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
|
||||
final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
|
||||
lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */);
|
||||
final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
|
||||
|
||||
mScheduler.scheduleClientMonitor(client1, callback1);
|
||||
|
||||
assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor());
|
||||
assertEquals(0, mScheduler.getCurrentPendingCount());
|
||||
|
||||
//Checking client is hung
|
||||
waitForIdle();
|
||||
verify(callback1, never()).onClientFinished(any(), anyBoolean());
|
||||
|
||||
//Start watchdog
|
||||
mScheduler.startWatchdog();
|
||||
waitForIdle();
|
||||
|
||||
// The watchdog should kick off the cancellation
|
||||
looper.moveTimeForward(10000);
|
||||
waitForIdle();
|
||||
// After 10 seconds the HAL has 3 seconds to respond to a cancel
|
||||
looper.moveTimeForward(3000);
|
||||
waitForIdle();
|
||||
|
||||
// The hung client did not honor this operation, verify onError and authenticated
|
||||
// were never called.
|
||||
assertFalse(client1.mOnErrorCalled);
|
||||
assertFalse(client1.mAuthenticateCalled);
|
||||
verify(callback1).onClientFinished(client1, false /* success */);
|
||||
assertEquals(0, mScheduler.getCurrentPendingCount());
|
||||
assertNull(mScheduler.mCurrentOperation);
|
||||
|
||||
|
||||
//Run additional auth client
|
||||
final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext,
|
||||
lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */);
|
||||
final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
|
||||
|
||||
mScheduler.scheduleClientMonitor(client2, callback2);
|
||||
|
||||
assertEquals(client2, mScheduler.mCurrentOperation.getClientMonitor());
|
||||
assertEquals(0, mScheduler.getCurrentPendingCount());
|
||||
|
||||
//Start watchdog
|
||||
mScheduler.startWatchdog();
|
||||
waitForIdle();
|
||||
mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class),
|
||||
mock(ClientMonitorCallback.class));
|
||||
waitForIdle();
|
||||
|
||||
//Ensure auth client passes
|
||||
verify(callback2).onClientStarted(client2);
|
||||
client2.getCallback().onClientFinished(client2, true);
|
||||
waitForIdle();
|
||||
|
||||
looper.moveTimeForward(10000);
|
||||
waitForIdle();
|
||||
// After 10 seconds the HAL has 3 seconds to respond to a cancel
|
||||
looper.moveTimeForward(3000);
|
||||
waitForIdle();
|
||||
|
||||
//Asserting auth client passes
|
||||
assertTrue(client2.isAlreadyDone());
|
||||
assertNotNull(mScheduler.mCurrentOperation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearBiometricQueue_doesNotClearOperationsWhenQueueNotStuck() {
|
||||
//Creating clients
|
||||
final TestableLooper looper = TestableLooper.get(this);
|
||||
final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
|
||||
final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
|
||||
lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class), 0 /* cookie */);
|
||||
final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
|
||||
|
||||
mScheduler.scheduleClientMonitor(client1, callback1);
|
||||
//Start watchdog
|
||||
mScheduler.startWatchdog();
|
||||
waitForIdle();
|
||||
mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class),
|
||||
mock(ClientMonitorCallback.class));
|
||||
mScheduler.scheduleClientMonitor(mock(BaseClientMonitor.class),
|
||||
mock(ClientMonitorCallback.class));
|
||||
waitForIdle();
|
||||
|
||||
assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor());
|
||||
assertEquals(2, mScheduler.getCurrentPendingCount());
|
||||
verify(callback1, never()).onClientFinished(any(), anyBoolean());
|
||||
verify(callback1).onClientStarted(client1);
|
||||
|
||||
//Client finishes successfully
|
||||
client1.getCallback().onClientFinished(client1, true);
|
||||
waitForIdle();
|
||||
|
||||
// The watchdog should kick off the cancellation
|
||||
looper.moveTimeForward(10000);
|
||||
waitForIdle();
|
||||
// After 10 seconds the HAL has 3 seconds to respond to a cancel
|
||||
looper.moveTimeForward(3000);
|
||||
waitForIdle();
|
||||
|
||||
//Watchdog does not clear pending operations
|
||||
assertEquals(1, mScheduler.getCurrentPendingCount());
|
||||
assertNotNull(mScheduler.mCurrentOperation);
|
||||
|
||||
}
|
||||
|
||||
private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
|
||||
return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
|
||||
}
|
||||
|
||||
private void waitForIdle() {
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
}
|
||||
|
||||
private static class TestAuthenticationClient extends AuthenticationClient<Object> {
|
||||
boolean mStartedHal = false;
|
||||
boolean mStoppedHal = false;
|
||||
boolean mDestroyed = false;
|
||||
int mNumCancels = 0;
|
||||
boolean mAuthenticateCalled = false;
|
||||
boolean mOnErrorCalled = false;
|
||||
|
||||
public TestAuthenticationClient(@NonNull Context context,
|
||||
TestAuthenticationClient(@NonNull Context context,
|
||||
@NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
|
||||
@NonNull ClientMonitorCallbackConverter listener) {
|
||||
this(context, lazyDaemon, token, listener, 1 /* cookie */);
|
||||
}
|
||||
|
||||
TestAuthenticationClient(@NonNull Context context,
|
||||
@NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
|
||||
@NonNull ClientMonitorCallbackConverter listener, int cookie) {
|
||||
super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
|
||||
false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */,
|
||||
false /* restricted */, TAG, cookie, false /* requireConfirmation */,
|
||||
TEST_SENSOR_ID, mock(BiometricLogger.class), mock(BiometricContext.class),
|
||||
true /* isStrongBiometric */, null /* taskStackListener */,
|
||||
mock(LockoutTracker.class), false /* isKeyguard */,
|
||||
@@ -546,7 +706,19 @@ public class BiometricSchedulerTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleLifecycleAfterAuth(boolean authenticated) {}
|
||||
protected void handleLifecycleAfterAuth(boolean authenticated) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
|
||||
boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
|
||||
mAuthenticateCalled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onErrorInternal(int errorCode, int vendorCode, boolean finish) {
|
||||
mOnErrorCalled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasUserDetected() {
|
||||
@@ -651,8 +823,4 @@ public class BiometricSchedulerTest {
|
||||
mDestroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void waitForIdle() {
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user