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

Commit 44cad397 authored by Diya Bera's avatar Diya Bera Committed by Android (Google) Code Review
Browse files

Merge "Accept authentication for app when it is top activity" into main

parents 6cd7b56f 2333701c
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