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

Commit 2333701c authored by Diya Bera's avatar Diya Bera
Browse files

Accept authentication for app when it is top activity

- Allow successful auth for top activity even if it is not visible
- Cancel all sensors before hiding biometric prompt

Flag: EXEMPT bug fix
Fixes: 401031815
Test: atest FingerprintAuthenticationClientTest
FaceAuthenticationClientTest AuthControllerTest

Change-Id: I91b13f5c985e1d2c1647580a8819562bb359f941
parent 52a686e3
Loading
Loading
Loading
Loading
+40 −3
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
@@ -995,8 +996,8 @@ public class AuthControllerTest extends SysuiTestCase {
    }

    @Test
    public void testShowDialog_whenOwnerNotInForeground() {
        PromptInfo promptInfo = createTestPromptInfo();
    public void testShowDialog_whenOwnerNotInForegroundAndNotVisible() {
        final PromptInfo promptInfo = createTestPromptInfo();
        promptInfo.setAllowBackgroundAuthentication(false);
        switchTask("other_package");
        mAuthController.showAuthenticationDialog(promptInfo,
@@ -1013,6 +1014,27 @@ public class AuthControllerTest extends SysuiTestCase {
        verify(mDialog1, never()).show(any());
    }

    @Test
    public void testShowDialog_whenOwnerNotInForegroundAndVisible() {
        final PromptInfo promptInfo = createTestPromptInfo();
        final AuthController.Callback callback = mock(AuthController.Callback.class);
        promptInfo.setAllowBackgroundAuthentication(false);
        switchTaskWithVisibility("other_package", true /* isVisible */);
        mAuthController.addCallback(callback);
        mAuthController.showAuthenticationDialog(promptInfo,
                mReceiver /* receiver */,
                new int[]{1} /* sensorIds */,
                false /* credentialAllowed */,
                true /* requireConfirmation */,
                0 /* userId */,
                0 /* operationId */,
                "testPackage",
                REQUEST_ID);

        assertNotNull(mAuthController.mCurrentDialog);
        verify(mDialog1).show(mWindowManager);
    }

    @Test
    public void testShowDialog_visibleBackgroundUser() {
        int backgroundUserId = 1001;
@@ -1109,16 +1131,31 @@ public class AuthControllerTest extends SysuiTestCase {
                REQUEST_ID);
    }

    private void switchTask(String packageName) {
    private void switchTaskWithVisibility(String packageName, boolean isVisible) {
        final List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>();
        final ActivityManager.RunningTaskInfo taskInfo =
                mock(ActivityManager.RunningTaskInfo.class);
        taskInfo.topActivity = mock(ComponentName.class);
        when(taskInfo.topActivity.getPackageName()).thenReturn(packageName);
        when(taskInfo.topActivity.getClassName()).thenReturn(AuthControllerTest.class.getName());
        tasks.add(taskInfo);

        final ActivityManager.RunningTaskInfo callingTaskInfo =
                mock(ActivityManager.RunningTaskInfo.class);
        callingTaskInfo.topActivity = mock(ComponentName.class);
        when(callingTaskInfo.topActivity.getPackageName()).thenReturn("Dialog1");
        when(callingTaskInfo.topActivity.getClassName()).thenReturn(
                AuthControllerTest.class.getName());
        callingTaskInfo.isVisible = isVisible;
        tasks.add(callingTaskInfo);

        when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
    }

    private void switchTask(String packageName) {
        switchTaskWithVisibility(packageName, false /* isVisible */);
    }

    private PromptInfo createTestPromptInfo() {
        PromptInfo promptInfo = new PromptInfo();

+71 −6
Original line number Diff line number Diff line
@@ -162,8 +162,10 @@ object Utils {
    fun ActivityTaskManager.isSystemAppOrInBackground(
        context: Context,
        clientPackage: String,
        clientClassNameIfItIsConfirmDeviceCredentialActivity: String?
        clientClassNameIfItIsConfirmDeviceCredentialActivity: String?,
    ): Boolean {
        // TODO (b/409812027): Consolidate and scope out auth requirements for biometric prompt

        Log.v(TAG, "Checking if the authenticating is in background, clientPackage:$clientPackage")
        val tasks = getTasks(Int.MAX_VALUE)
        if (tasks == null || tasks.isEmpty()) {
@@ -176,11 +178,74 @@ object Utils {
        val topPackageEqualsToClient = topActivity!!.packageName == clientPackage
        val isClientConfirmDeviceCredentialActivity =
            clientClassNameIfItIsConfirmDeviceCredentialActivity != null

        if (
            !isClientInBackgroundOrNotVisible(
                isSystemApp,
                isVisible = true,
                topPackageEqualsToClient,
                isClientConfirmDeviceCredentialActivity,
                clientClassNameIfItIsConfirmDeviceCredentialActivity,
                topActivity!!.className,
                isTopPackage = true,
            )
        ) {
            return false
        }

        for (task in tasks) {
            val packageName = task.topActivity!!.packageName
            val className = task.topActivity!!.className
            val isVisible = task.isVisible

            Log.v(TAG, "Running task, top: $packageName, isVisible: $isVisible")

            if (
                !isClientInBackgroundOrNotVisible(
                    isSystemApp = false,
                    isVisible,
                    taskPackageEqualsClientPackage = packageName == clientPackage,
                    isClientConfirmDeviceCredentialActivity,
                    clientClassNameIfItIsConfirmDeviceCredentialActivity,
                    className,
                )
            ) {
                return false
            }
        }

        return true
    }

    fun isClientInBackgroundOrNotVisible(
        isSystemApp: Boolean,
        isVisible: Boolean,
        taskPackageEqualsClientPackage: Boolean,
        isClientConfirmDeviceCredentialActivity: Boolean,
        clientClassNameIfItIsConfirmDeviceCredentialActivity: String?,
        topActivityClassName: String,
        isTopPackage: Boolean = false,
    ): Boolean {

        if (isVisible) {
            if (isSystemApp || taskPackageEqualsClientPackage) {
                // b/339532378: If it's ConfirmDeviceCredentialActivity, we need to check further on
                // class name.
        return !(isSystemApp || topPackageEqualsToClient) ||
            (isClientConfirmDeviceCredentialActivity &&
                topActivity.className != clientClassNameIfItIsConfirmDeviceCredentialActivity)
                if (isClientConfirmDeviceCredentialActivity) {
                    return !isTopPackage ||
                        clientClassNameIfItIsConfirmDeviceCredentialActivity != topActivityClassName
                }
                return false
            } else if (
                isTopPackage &&
                    isClientConfirmDeviceCredentialActivity &&
                    clientClassNameIfItIsConfirmDeviceCredentialActivity == topActivityClassName
            ) {
                return false
            }
        }

        return true
    }
    // LINT.ThenChange(frameworks/base/services/core/java/com/android/server/biometrics/Utils.java)
}
+3 −1
Original line number Diff line number Diff line
@@ -438,7 +438,8 @@ public final class AuthSession implements IBinder.DeathRecipient {
     */
    boolean onErrorReceived(int sensorId, int cookie, @BiometricConstants.Errors int error,
            int vendorCode) throws RemoteException {
        Slog.d(TAG, "onErrorReceived sensor: " + sensorId + " error: " + error);
        Slog.d(TAG, "onErrorReceived sensor: " + sensorId + " error: " + error
                + " state: " + mState);

        if (!containsCookie(cookie)) {
            Slog.e(TAG, "Unknown/expired cookie: " + cookie);
@@ -510,6 +511,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
                    mState = STATE_SHOWING_DEVICE_CREDENTIAL;
                    mStatusBarService.onBiometricError(modality, error, vendorCode);
                } else if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
                    cancelAllSensors();
                    mStatusBarService.hideAuthenticationDialog(mRequestId);
                    // TODO: If multiple authenticators are simultaneously running, this will
                    // need to be modified. Send the error to the client here, instead of doing
+13 −2
Original line number Diff line number Diff line
@@ -617,20 +617,31 @@ public class Utils {
    /**
     * Checks if a client package is running in the background.
     *
     * @param activityTaskManager Task manager which provides the list of clients running.
     * @param clientPackage The name of the package to be checked.
     * @return Whether the client package is running in background
     */
    public static boolean isBackground(String clientPackage) {
    public static boolean isBackground(ActivityTaskManager activityTaskManager,
            String clientPackage) {
        Slog.v(TAG, "Checking if the authenticating is in background,"
                + " clientPackage:" + clientPackage);
        final List<ActivityManager.RunningTaskInfo> tasks =
                ActivityTaskManager.getInstance().getTasks(Integer.MAX_VALUE);
                activityTaskManager.getTasks(Integer.MAX_VALUE);

        if (tasks == null || tasks.isEmpty()) {
            Slog.d(TAG, "No running tasks reported");
            return true;
        }

        //Allow auth for top activity even if it is not visible
        final ActivityManager.RunningTaskInfo topTaskInfo = tasks.getFirst();
        if (topTaskInfo != null && topTaskInfo.topActivity != null) {
            final String topPackage = topTaskInfo.topActivity.getPackageName();
            if (topPackage.contentEquals(clientPackage)) {
                return false;
            }
        }

        for (ActivityManager.RunningTaskInfo taskInfo : tasks) {
            final ComponentName topActivity = taskInfo.topActivity;
            if (topActivity != null) {
+1 −1
Original line number Diff line number Diff line
@@ -208,7 +208,7 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions>
        if (!mAllowBackgroundAuthentication && authenticated
                && !Utils.isKeyguard(getContext(), getOwnerString())
                && !Utils.isSystem(getContext(), getOwnerString())) {
            isBackgroundAuth = Utils.isBackground(getOwnerString());
            isBackgroundAuth = Utils.isBackground(mActivityTaskManager, getOwnerString());
        }

        // Fail authentication if we can't confirm the client activity is on top.
Loading