Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c44066c authored by Diya Bera's avatar Diya Bera
Browse files

Add metric for incorrect biometric scheduler operation state

Flag: EXEMPT bugfix
Test: atest BiometricSchedulerOperationTest
Bug: 269047889
Change-Id: I4a1d66679c4a41cf9dac61c83ba495643900cca7
parent c7aa0be4
Loading
Loading
Loading
Loading
+11 −34
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.biometrics.BiometricConstants;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -28,11 +27,12 @@ import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.modules.expresslog.Counter;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.function.BooleanSupplier;


/**
 * Contains all the necessary information for a HAL operation.
@@ -89,8 +89,6 @@ public class BiometricSchedulerOperation {
    private final BaseClientMonitor mClientMonitor;
    @Nullable
    private final ClientMonitorCallback mClientCallback;
    @NonNull
    private final BooleanSupplier mIsDebuggable;
    @Nullable
    private ClientMonitorCallback mOnStartCallback;
    @OperationState
@@ -99,6 +97,7 @@ public class BiometricSchedulerOperation {
    @NonNull
    final Runnable mCancelWatchdog;

    @VisibleForTesting
    BiometricSchedulerOperation(
            @NonNull BaseClientMonitor clientMonitor,
            @Nullable ClientMonitorCallback callback
@@ -106,33 +105,14 @@ public class BiometricSchedulerOperation {
        this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
    }

    @VisibleForTesting
    BiometricSchedulerOperation(
            @NonNull BaseClientMonitor clientMonitor,
            @Nullable ClientMonitorCallback callback,
            @NonNull BooleanSupplier isDebuggable
    ) {
        this(clientMonitor, callback, STATE_WAITING_IN_QUEUE, isDebuggable);
    }

    protected BiometricSchedulerOperation(
            @NonNull BaseClientMonitor clientMonitor,
            @Nullable ClientMonitorCallback callback,
            @OperationState int state
    ) {
        this(clientMonitor, callback, state, Build::isDebuggable);
    }

    private BiometricSchedulerOperation(
            @NonNull BaseClientMonitor clientMonitor,
            @Nullable ClientMonitorCallback callback,
            @OperationState int state,
            @NonNull BooleanSupplier isDebuggable
    ) {
        mClientMonitor = clientMonitor;
        mClientCallback = callback;
        mState = state;
        mIsDebuggable = isDebuggable;
        mCancelWatchdog = () -> {
            if (!isFinished()) {
                Slog.e(TAG, "[Watchdog Triggered]: " + this);
@@ -187,9 +167,7 @@ public class BiometricSchedulerOperation {

        if (mClientMonitor.getCookie() != 0) {
            String err = "operation requires cookie";
            if (mIsDebuggable.getAsBoolean()) {
                throw new IllegalStateException(err);
            }
            Counter.logIncrement("biometric.value_biometric_scheduler_operation_state_error_count");
            Slog.e(TAG, err);
        }

@@ -456,10 +434,9 @@ public class BiometricSchedulerOperation {
    private boolean errorWhenOneOf(String op, @OperationState int... states) {
        final boolean isError = ArrayUtils.contains(states, mState);
        if (isError) {
            String err = op + ": mState must not be " + mState;
            if (mIsDebuggable.getAsBoolean()) {
                throw new IllegalStateException(err);
            }
            Counter.logIncrement(
                    "biometric.value_biometric_scheduler_operation_state_error_count");
            final String err = op + ": mState must not be " + mState;
            Slog.e(TAG, err);
        }
        return isError;
@@ -468,10 +445,10 @@ public class BiometricSchedulerOperation {
    private boolean errorWhenNoneOf(String op, @OperationState int... states) {
        final boolean isError = !ArrayUtils.contains(states, mState);
        if (isError) {
            String err = op + ": mState=" + mState + " must be one of " + Arrays.toString(states);
            if (mIsDebuggable.getAsBoolean()) {
                throw new IllegalStateException(err);
            }
            Counter.logIncrement(
                    "biometric.value_biometric_scheduler_operation_state_error_count");
            final String err = op + ": mState=" + mState + " must be one of "
                    + Arrays.toString(states);
            Slog.e(TAG, err);
        }
        return isError;
+18 −95
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ 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.testng.Assert.assertThrows;

import android.hardware.biometrics.BiometricConstants;
import android.os.Handler;
@@ -88,16 +87,14 @@ public class BiometricSchedulerOperationTest {
    private Handler mHandler;
    private BiometricSchedulerOperation mInterruptableOperation;
    private BiometricSchedulerOperation mNonInterruptableOperation;
    private boolean mIsDebuggable;

    @Before
    public void setUp() {
        mHandler = new Handler(TestableLooper.get(this).getLooper());
        mIsDebuggable = false;
        mInterruptableOperation = new BiometricSchedulerOperation(mInterruptableClientMonitor,
                mClientCallback, () -> mIsDebuggable);
                mClientCallback);
        mNonInterruptableOperation = new BiometricSchedulerOperation(mNonInterruptableClientMonitor,
                mClientCallback, () -> mIsDebuggable);
                mClientCallback);

        when(mInterruptableClientMonitor.isInterruptable()).thenReturn(true);
        when(mNonInterruptableClientMonitor.isInterruptable()).thenReturn(false);
@@ -143,32 +140,13 @@ public class BiometricSchedulerOperationTest {
    }

    @Test
    public void testSecondStartWithCookieCrashesWhenDebuggable() {
    public void testSecondStartWithCookieFails() {
        final int cookie = 5;
        mIsDebuggable = true;
        when(mInterruptableClientMonitor.getCookie()).thenReturn(cookie);
        when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);

        final boolean started = mInterruptableOperation.startWithCookie(mOnStartCallback, cookie);
        assertThat(started).isTrue();

        assertThrows(IllegalStateException.class,
                () -> mInterruptableOperation.startWithCookie(mOnStartCallback, cookie));
    }

    @Test
    public void testSecondStartWithCookieFailsNicelyWhenNotDebuggable() {
        final int cookie = 5;
        mIsDebuggable = false;
        when(mInterruptableClientMonitor.getCookie()).thenReturn(cookie);
        when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);

        final boolean started = mInterruptableOperation.startWithCookie(mOnStartCallback, cookie);
        assertThat(started).isTrue();

        final boolean startedAgain = mInterruptableOperation.startWithCookie(mOnStartCallback,
                cookie);
        assertThat(startedAgain).isFalse();
        assertThat(mInterruptableOperation.startWithCookie(mOnStartCallback, cookie)).isTrue();
        assertThat(mInterruptableOperation.startWithCookie(mOnStartCallback, cookie)).isFalse();
    }

    @Test
@@ -217,56 +195,23 @@ public class BiometricSchedulerOperationTest {
    }

    @Test
    public void secondStartCrashesWhenDebuggable() {
        mIsDebuggable = true;
    public void secondStartFails() {
        when(mInterruptableClientMonitor.getCookie()).thenReturn(0);
        when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);

        final boolean started = mInterruptableOperation.start(mOnStartCallback);
        assertThat(started).isTrue();

        assertThrows(IllegalStateException.class, () -> mInterruptableOperation.start(
                mOnStartCallback));
    }

    @Test
    public void secondStartFailsNicelyWhenNotDebuggable() {
        mIsDebuggable = false;
        when(mInterruptableClientMonitor.getCookie()).thenReturn(0);
        when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);

        final boolean started = mInterruptableOperation.start(mOnStartCallback);
        assertThat(started).isTrue();

        final boolean startedAgain = mInterruptableOperation.start(mOnStartCallback);
        assertThat(startedAgain).isFalse();
        assertThat(mInterruptableOperation.start(mOnStartCallback)).isTrue();
        assertThat(mInterruptableOperation.start(mOnStartCallback)).isFalse();
    }

    @Test
    public void doesNotStartWithCookie() {
        // This class only throws exceptions when debuggable.
        mIsDebuggable = true;
        when(mInterruptableClientMonitor.getCookie()).thenReturn(9);
        assertThrows(IllegalStateException.class,
                () -> mInterruptableOperation.start(mock(ClientMonitorCallback.class)));
    }

    @Test
    public void cannotRestart() {
        // This class only throws exceptions when debuggable.
        mIsDebuggable = true;
        when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);

        mInterruptableOperation.start(mOnStartCallback);

        assertThrows(IllegalStateException.class,
                () -> mInterruptableOperation.start(mock(ClientMonitorCallback.class)));
        assertThat(mInterruptableOperation.start(mock(ClientMonitorCallback.class))).isFalse();
    }

    @Test
    public void abortsNotRunning() {
        // This class only throws exceptions when debuggable.
        mIsDebuggable = true;
    public void abortSuccessfulIfOperationNotRunning() {
        when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);

        mInterruptableOperation.abort();
@@ -274,28 +219,17 @@ public class BiometricSchedulerOperationTest {
        assertThat(mInterruptableOperation.isFinished()).isTrue();
        verify(mInterruptableClientMonitor).unableToStart();
        verify(mInterruptableClientMonitor).destroy();
        assertThrows(IllegalStateException.class,
                () -> mInterruptableOperation.start(mock(ClientMonitorCallback.class)));
        assertThat(mInterruptableOperation.start(mock(ClientMonitorCallback.class))).isFalse();
    }

    @Test
    public void abortCrashesWhenDebuggableIfOperationIsRunning() {
        mIsDebuggable = true;
    public void abortFailsIfOperationIsRunning() {
        when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);

        mInterruptableOperation.start(mOnStartCallback);

        assertThrows(IllegalStateException.class, () -> mInterruptableOperation.abort());
    }

    @Test
    public void abortFailsNicelyWhenNotDebuggableIfOperationIsRunning() {
        mIsDebuggable = false;
        when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);

        mInterruptableOperation.start(mOnStartCallback);

        mInterruptableOperation.abort();

        assertThat(mInterruptableOperation.isFinished()).isFalse();
    }

    @Test
@@ -344,21 +278,7 @@ public class BiometricSchedulerOperationTest {
    }

    @Test
    public void cancelCrashesWhenDebuggableIfOperationIsFinished() {
        mIsDebuggable = true;
        when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);

        mInterruptableOperation.abort();
        assertThat(mInterruptableOperation.isFinished()).isTrue();

        final ClientMonitorCallback cancelCb = mock(ClientMonitorCallback.class);
        assertThrows(IllegalStateException.class, () -> mInterruptableOperation.cancel(mHandler,
                cancelCb));
    }

    @Test
    public void cancelFailsNicelyWhenNotDebuggableIfOperationIsFinished() {
        mIsDebuggable = false;
    public void cancelFailsIfOperationIsFinished() {
        when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);

        mInterruptableOperation.abort();
@@ -366,6 +286,9 @@ public class BiometricSchedulerOperationTest {

        final ClientMonitorCallback cancelCb = mock(ClientMonitorCallback.class);
        mInterruptableOperation.cancel(mHandler, cancelCb);

        verify(mInterruptableClientMonitor, never()).cancel();
        verify(mInterruptableClientMonitor, never()).cancelWithoutStarting(any());
    }

    @Test