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

Commit 99b50138 authored by Diya Bera's avatar Diya Bera
Browse files

Fix biometric scheduler watchdog

Prevent user switching for clients that are marked as cancelling

Flag: com.android.server.biometrics.biometric_scheduler_fix
Fixes: 420939050
Test: atest BiometricSchedulerTest
Change-Id: I6bfc06ca510f750ec26083a4140c48ec1e8c162f
parent 8ca800ef
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -41,3 +41,13 @@ flag {
  description: "This flag controls FRR dialog improvement"
  bug: "380800403"
}

flag {
  name: "biometric_scheduler_fix"
  namespace: "biometrics_framework"
  description: "This flag fixes a bug where watchdog is ineffective when user switching is required."
  bug: "420939050"
  metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.expresslog.Counter;
import com.android.server.biometrics.BiometricSchedulerProto;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.Flags;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;

import java.io.PrintWriter;
@@ -314,8 +315,11 @@ public class BiometricScheduler<T, U> {

        final int currentUserId = mCurrentUserRetriever.get();
        final int nextUserId = mPendingOperations.getFirst().getTargetUserId();
        final boolean shouldStartNextOperationIfMarkedCancelling =
                Flags.biometricSchedulerFix() && mPendingOperations.getFirst().isMarkedCanceling();

        if (nextUserId == currentUserId || mPendingOperations.getFirst().isStartUserOperation()) {
        if (nextUserId == currentUserId || mPendingOperations.getFirst().isStartUserOperation()
                || shouldStartNextOperationIfMarkedCancelling) {
            startNextOperationIfIdle();
        } else if (currentUserId == UserHandle.USER_NULL && mUserSwitchProvider != null) {
            final BaseClientMonitor startClient =
+48 −1
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
@@ -66,6 +67,7 @@ import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;

import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.nano.BiometricSchedulerProto;
@@ -801,6 +803,46 @@ public class BiometricSchedulerTest {

    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_BIOMETRIC_SCHEDULER_FIX)
    public void testBiometricQueueHung_watchdogTriggered_noUserSwitch() {
        final int currentUserId = mCurrentUserId;
        final int newUserId = 10;

        final Supplier<Object> lazyDaemon1 = () -> mock(Object.class);
        final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon1,
                0 /* cookie */, BiometricsProto.CM_UPDATE_ACTIVE_USER, currentUserId);
        final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);

        final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken, lazyDaemon1,
                0 /* cookie */, BiometricsProto.CM_UPDATE_ACTIVE_USER, newUserId);
        final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);

        mScheduler.scheduleClientMonitor(client1, callback1);
        mScheduler.scheduleClientMonitor(client2, callback2);

        waitForIdle();

        assertThat(mScheduler.mPendingOperations.size()).isEqualTo(1);
        assertThat(mScheduler.mCurrentOperation.getClientMonitor()).isEqualTo(client1);
        verify(callback1).onClientStarted(client1);

        mScheduler.startWatchdog();
        waitForIdle();
        mLooper.moveTimeForward(10000);
        waitForIdle();
        // After 10 seconds the HAL has 3 seconds to respond to a cancel
        mLooper.moveTimeForward(3000);
        waitForIdle();


        assertThat(mScheduler.mPendingOperations.size()).isEqualTo(0);
        assertThat(mScheduler.mCurrentOperation).isEqualTo(null);
        assertThat(mCurrentUserId).isEqualTo(currentUserId);
        verify(callback1).onClientFinished(client1, true);
        verify(callback2).onClientFinished(client2, true);
    }

    @Test
    public void testTwoInternalCleanupOps_withFirstFavorHalEnrollment() throws Exception {
        final String owner = "test.owner";
@@ -1182,7 +1224,12 @@ public class BiometricSchedulerTest {

        TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
                @NonNull Supplier<Object> lazyDaemon, int cookie, int protoEnum) {
            super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */, TAG,
            this(context, token, lazyDaemon, cookie, protoEnum, 0 /* userId */);
        }

        TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
                @NonNull Supplier<Object> lazyDaemon, int cookie, int protoEnum, int userId) {
            super(context, lazyDaemon, token /* token */, null /* listener */, userId, TAG,
                    cookie, TEST_SENSOR_ID, mock(BiometricLogger.class),
                    mock(BiometricContext.class), false /* isMandatoryBiometrics */);
            mProtoEnum = protoEnum;