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

Commit 1639cef6 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Fix mismatched state between SystemUI and FingerprintService" into...

Merge "Fix mismatched state between SystemUI and FingerprintService" into sc-v2-dev am: 255aae73 am: d2a036ab

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

Change-Id: Ib45d6318ab0bf7150ceb6f4c2b8d256c2c0c1c7a
parents 3ef3f295 d2a036ab
Loading
Loading
Loading
Loading
+79 −56
Original line number Diff line number Diff line
@@ -49,7 +49,6 @@ import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseBooleanArray;
@@ -65,6 +64,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.concurrency.Execution;

import java.util.ArrayList;
import java.util.Arrays;
@@ -92,15 +92,20 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
    private static final boolean DEBUG = true;
    private static final int SENSOR_PRIVACY_DELAY = 500;

    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private final Handler mHandler;
    private final Execution mExecution;
    private final CommandQueue mCommandQueue;
    private final ActivityTaskManager mActivityTaskManager;
    @Nullable private final FingerprintManager mFingerprintManager;
    @Nullable private final FaceManager mFaceManager;
    @Nullable
    private final FingerprintManager mFingerprintManager;
    @Nullable
    private final FaceManager mFaceManager;
    private final Provider<UdfpsController> mUdfpsControllerFactory;
    private final Provider<SidefpsController> mSidefpsControllerFactory;
    @Nullable private final PointF mFaceAuthSensorLocation;
    @Nullable private PointF mFingerprintLocation;
    @Nullable
    private final PointF mFaceAuthSensorLocation;
    @Nullable
    private PointF mFingerprintLocation;
    private final Set<Callback> mCallbacks = new HashSet<>();

    // TODO: These should just be saved from onSaveState
@@ -133,62 +138,27 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
        }
    }

    private final FingerprintStateListener mFingerprintStateListener =
            new FingerprintStateListener() {
        @Override
        public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
            Log.d(TAG, "onEnrollmentsChanged, userId: " + userId
                    + ", sensorId: " + sensorId
                    + ", hasEnrollments: " + hasEnrollments);
            for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) {
                if (prop.sensorId == sensorId) {
                    mUdfpsEnrolledForUser.put(userId, hasEnrollments);
                }
            }

            for (Callback cb : mCallbacks) {
                cb.onEnrollmentsChanged();
            }
        }
    };

    @NonNull
    private final IFingerprintAuthenticatorsRegisteredCallback
            mFingerprintAuthenticatorsRegisteredCallback =
            new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
                @Override public void onAllAuthenticatorsRegistered(
                @Override
                public void onAllAuthenticatorsRegistered(
                        List<FingerprintSensorPropertiesInternal> sensors) {
                    if (DEBUG) {
                        Log.d(TAG, "onFingerprintProvidersAvailable | sensors: " + Arrays.toString(
                                sensors.toArray()));
                    }
                    mFpProps = sensors;
                    List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
                    List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
                    for (FingerprintSensorPropertiesInternal props : mFpProps) {
                        if (props.isAnyUdfpsType()) {
                            udfpsProps.add(props);
                        }
                        if (props.isAnySidefpsType()) {
                            sidefpsProps.add(props);
                        }
                    }
                    mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
                    if (mUdfpsProps != null) {
                        mUdfpsController = mUdfpsControllerFactory.get();
                    }
                    mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
                    if (mSidefpsProps != null) {
                        mSidefpsController = mSidefpsControllerFactory.get();
                    mHandler.post(() -> handleAllAuthenticatorsRegistered(sensors));
                }
            };

                    for (Callback cb : mCallbacks) {
                        cb.onAllAuthenticatorsRegistered();
                    }
    private final FingerprintStateListener mFingerprintStateListener =
            new FingerprintStateListener() {
                @Override
                public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
                    mHandler.post(
                            () -> handleEnrollmentsChanged(userId, sensorId, hasEnrollments));
                }
            };

    @VisibleForTesting final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    @VisibleForTesting
    final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (mCurrentDialog != null
@@ -212,6 +182,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
    };

    private void handleTaskStackChanged() {
        mExecution.assertIsMainThread();
        if (mCurrentDialog != null) {
            try {
                final String clientPackage = mCurrentDialog.getOpPackageName();
@@ -241,6 +212,56 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
        }
    }

    private void handleAllAuthenticatorsRegistered(
            List<FingerprintSensorPropertiesInternal> sensors) {
        mExecution.assertIsMainThread();
        if (DEBUG) {
            Log.d(TAG, "handleAllAuthenticatorsRegistered | sensors: " + Arrays.toString(
                    sensors.toArray()));
        }
        mFpProps = sensors;
        List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
        List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
        for (FingerprintSensorPropertiesInternal props : mFpProps) {
            if (props.isAnyUdfpsType()) {
                udfpsProps.add(props);
            }
            if (props.isAnySidefpsType()) {
                sidefpsProps.add(props);
            }
        }
        mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
        if (mUdfpsProps != null) {
            mUdfpsController = mUdfpsControllerFactory.get();
        }
        mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
        if (mSidefpsProps != null) {
            mSidefpsController = mSidefpsControllerFactory.get();
        }
        for (Callback cb : mCallbacks) {
            cb.onAllAuthenticatorsRegistered();
        }
        mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener);
    }

    private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
        mExecution.assertIsMainThread();
        Log.d(TAG, "handleEnrollmentsChanged, userId: " + userId + ", sensorId: " + sensorId
                + ", hasEnrollments: " + hasEnrollments);
        if (mUdfpsProps == null) {
            Log.d(TAG, "handleEnrollmentsChanged, mUdfpsProps is null");
        } else {
            for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) {
                if (prop.sensorId == sensorId) {
                    mUdfpsEnrolledForUser.put(userId, hasEnrollments);
                }
            }
        }
        for (Callback cb : mCallbacks) {
            cb.onEnrollmentsChanged();
        }
    }

    /**
     * Adds a callback. See {@link Callback}.
     */
@@ -449,6 +470,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba

    @Inject
    public AuthController(Context context,
            Execution execution,
            CommandQueue commandQueue,
            ActivityTaskManager activityTaskManager,
            @NonNull WindowManager windowManager,
@@ -459,6 +481,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
            @NonNull DisplayManager displayManager,
            @Main Handler handler) {
        super(context);
        mExecution = execution;
        mHandler = handler;
        mCommandQueue = commandQueue;
        mActivityTaskManager = activityTaskManager;
        mFingerprintManager = fingerprintManager;
@@ -470,7 +494,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
        mOrientationListener = new BiometricDisplayListener(
                context,
                displayManager,
                handler,
                mHandler,
                BiometricDisplayListener.SensorType.Generic.INSTANCE,
                () -> {
                    onOrientationChanged();
@@ -521,7 +545,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
        if (mFingerprintManager != null) {
            mFingerprintManager.addAuthenticatorsRegisteredCallback(
                    mFingerprintAuthenticatorsRegisteredCallback);
            mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener);
        }

        mTaskStackListener = new BiometricTaskStackListener();
+79 −9
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -53,12 +54,14 @@ import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintStateListener;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.WindowManager;

@@ -67,6 +70,8 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;

import org.junit.Before;
import org.junit.Test;
@@ -112,20 +117,27 @@ public class AuthControllerTest extends SysuiTestCase {
    private SidefpsController mSidefpsController;
    @Mock
    private DisplayManager mDisplayManager;
    @Mock
    private Handler mHandler;
    @Captor
    ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor;
    @Captor
    ArgumentCaptor<FingerprintStateListener> mFingerprintStateCaptor;

    private TestableContext mContextSpy;
    private Execution mExecution;
    private TestableLooper mTestableLooper;
    private Handler mHandler;
    private TestableAuthController mAuthController;

    @Before
    public void setup() throws RemoteException {
        MockitoAnnotations.initMocks(this);

        TestableContext context = spy(mContext);
        mContextSpy = spy(mContext);
        mExecution = new FakeExecution();
        mTestableLooper = TestableLooper.get(this);
        mHandler = new Handler(mTestableLooper.getLooper());

        when(context.getPackageManager()).thenReturn(mPackageManager);
        when(mContextSpy.getPackageManager()).thenReturn(mPackageManager);
        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
                .thenReturn(true);
        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
@@ -158,18 +170,75 @@ public class AuthControllerTest extends SysuiTestCase {
        props.add(prop);
        when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);

        mAuthController = new TestableAuthController(context, mCommandQueue,
        mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
                mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
                () -> mUdfpsController, () -> mSidefpsController);

        mAuthController.start();
        verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
                mAuthenticatorsRegisteredCaptor.capture());

        mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props);

        // Ensures that the operations posted on the handler get executed.
        mTestableLooper.processAllMessages();
    }

    // Callback tests

    @Test
    public void testRegistersFingerprintStateListener_afterAllAuthenticatorsAreRegistered()
            throws RemoteException {
        // This test is sensitive to prior FingerprintManager interactions.
        reset(mFingerprintManager);

        // This test requires an uninitialized AuthController.
        AuthController authController = new TestableAuthController(mContextSpy, mExecution,
                mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
                mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
        authController.start();

        verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
                mAuthenticatorsRegisteredCaptor.capture());
        mTestableLooper.processAllMessages();

        verify(mFingerprintManager, never()).registerFingerprintStateListener(any());

        mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
        mTestableLooper.processAllMessages();

        verify(mFingerprintManager).registerFingerprintStateListener(any());
    }

    @Test
    public void testDoesNotCrash_afterEnrollmentsChangedForUnknownSensor() throws RemoteException {
        // This test is sensitive to prior FingerprintManager interactions.
        reset(mFingerprintManager);

        // This test requires an uninitialized AuthController.
        AuthController authController = new TestableAuthController(mContextSpy, mExecution,
                mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
                mFaceManager, () -> mUdfpsController, () -> mSidefpsController);
        authController.start();

        verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
                mAuthenticatorsRegisteredCaptor.capture());

        // Emulates a device with no authenticators (empty list).
        mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
        mTestableLooper.processAllMessages();

        verify(mFingerprintManager).registerFingerprintStateListener(
                mFingerprintStateCaptor.capture());

        // Enrollments changed for an unknown sensor.
        mFingerprintStateCaptor.getValue().onEnrollmentsChanged(0 /* userId */,
                0xbeef /* sensorId */, true /* hasEnrollments */);
        mTestableLooper.processAllMessages();

        // Nothing should crash.
    }

    @Test
    public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
        showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
@@ -497,7 +566,7 @@ public class AuthControllerTest extends SysuiTestCase {
        when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);

        mAuthController.mTaskStackListener.onTaskStackChanged();
        waitForIdleSync();
        mTestableLooper.processAllMessages();

        assertNull(mAuthController.mCurrentDialog);
        assertNull(mAuthController.mReceiver);
@@ -528,7 +597,7 @@ public class AuthControllerTest extends SysuiTestCase {
        showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
        Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        mAuthController.mBroadcastReceiver.onReceive(mContext, intent);
        waitForIdleSync();
        mTestableLooper.processAllMessages();

        assertNull(mAuthController.mCurrentDialog);
        assertNull(mAuthController.mReceiver);
@@ -598,6 +667,7 @@ public class AuthControllerTest extends SysuiTestCase {
        private PromptInfo mLastBiometricPromptInfo;

        TestableAuthController(Context context,
                Execution execution,
                CommandQueue commandQueue,
                ActivityTaskManager activityTaskManager,
                WindowManager windowManager,
@@ -605,7 +675,7 @@ public class AuthControllerTest extends SysuiTestCase {
                FaceManager faceManager,
                Provider<UdfpsController> udfpsControllerFactory,
                Provider<SidefpsController> sidefpsControllerFactory) {
            super(context, commandQueue, activityTaskManager, windowManager,
            super(context, execution, commandQueue, activityTaskManager, windowManager,
                    fingerprintManager, faceManager, udfpsControllerFactory,
                    sidefpsControllerFactory, mDisplayManager, mHandler);
        }