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

Commit c6b91c65 authored by David Stevens's avatar David Stevens
Browse files

Prefer to launch activities on the source display

If the Activity was sent by another Activity, the source display is
whatever display the original activity was running on. Otherwise,
the source display is the primary display.

- When looking for a task to launch the activity into, ignore affinity
  matches on other displays so we ignore tasks on other displays (when
  possible). This is effectively the inverse of LAUNCH_ADJACENT, which
  ignores all affinity matches to try to launch into the adjacent stack.
- Bypass LAUNCH_ADJACENT's logic for determining the launch stack if
  we're not on the primary display.
- If the focused stack is dynamic, only consider it if the source was on
  the same display as that dynamic stack.
- If no particular stack matches, use the source display instead of
  defaulting to the primary display.

This behavior also means we don't need to check display access
permissions unless the caller requests a specific display, since the
source is already running on the display and thus has permissions.

Bug: 34230873
Test: android.server.cts.ActivityManagerDisplayTests
Test: #testTaskMatchAcrossDisplays
Test: #testNewTaskSameDisplay
Change-Id: Ic3b4754ffb290f84c3964352105594961604891a
parent a98767ab
Loading
Loading
Loading
Loading
+15 −9
Original line number Diff line number Diff line
@@ -2884,9 +2884,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
        return true;
    }

    ActivityRecord findTaskLocked(ActivityRecord r) {
    ActivityRecord findTaskLocked(ActivityRecord r, int displayId) {
        mTmpFindTaskResult.r = null;
        mTmpFindTaskResult.matchedByRootAffinity = false;
        ActivityRecord affinityMatch = null;
        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -2902,17 +2903,22 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
                    continue;
                }
                stack.findTaskLocked(r, mTmpFindTaskResult);
                // It is possible to have task in multiple stacks with the same root affinity.
                // If the match we found was based on root affinity we keep on looking to see if
                // there is a better match in another stack. We eventually return the match based
                // on root affinity if we don't find a better match.
                if (mTmpFindTaskResult.r != null && !mTmpFindTaskResult.matchedByRootAffinity) {
                // It is possible to have tasks in multiple stacks with the same root affinity, so
                // we should keep looking after finding an affinity match to see if there is a
                // better match in another stack. Also, task affinity isn't a good enough reason
                // to target a display which isn't the source of the intent, so skip any affinity
                // matches not on the specified display.
                if (mTmpFindTaskResult.r != null) {
                    if (!mTmpFindTaskResult.matchedByRootAffinity) {
                        return mTmpFindTaskResult.r;
                    } else if (mTmpFindTaskResult.r.getDisplayId() == displayId) {
                        affinityMatch = mTmpFindTaskResult.r;
                    }
                }
            }
        if (DEBUG_TASKS && mTmpFindTaskResult.r == null) Slog.d(TAG_TASKS, "No task found");
        return mTmpFindTaskResult.r;
        }
        if (DEBUG_TASKS && affinityMatch == null) Slog.d(TAG_TASKS, "No task found");
        return affinityMatch;
    }

    ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
+39 −32
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
@@ -169,6 +170,7 @@ class ActivityStarter {
    private boolean mDoResume;
    private int mStartFlags;
    private ActivityRecord mSourceRecord;
    private int mSourceDisplayId;

    private TaskRecord mInTask;
    private boolean mAddingToTask;
@@ -208,6 +210,7 @@ class ActivityStarter {
        mDoResume = false;
        mStartFlags = 0;
        mSourceRecord = null;
        mSourceDisplayId = INVALID_DISPLAY;

        mInTask = null;
        mAddingToTask = false;
@@ -451,8 +454,8 @@ class ActivityStarter {
                    Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
                            true, false) + "} from uid " + callingUid + " on display "
                            + (container == null ? (mSupervisor.mFocusedStack == null ?
                            Display.DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId) :
                            (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY :
                            DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId) :
                            (container.mActivityDisplay == null ? DEFAULT_DISPLAY :
                                    container.mActivityDisplay.mDisplayId)));
                }
            }
@@ -1193,6 +1196,11 @@ class ActivityStarter {
        mVoiceSession = voiceSession;
        mVoiceInteractor = voiceInteractor;

        mSourceDisplayId = sourceRecord != null ? sourceRecord.getDisplayId() : INVALID_DISPLAY;
        if (mSourceDisplayId == INVALID_DISPLAY) {
            mSourceDisplayId = DEFAULT_DISPLAY;
        }

        mLaunchBounds = getOverrideBounds(r, options, inTask);

        mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;
@@ -1439,7 +1447,7 @@ class ActivityStarter {
                        !mLaunchSingleTask);
            } else {
                // Otherwise find the best task to put the activity in.
                intentActivity = mSupervisor.findTaskLocked(mStartActivity);
                intentActivity = mSupervisor.findTaskLocked(mStartActivity, mSourceDisplayId);
            }
        }
        return intentActivity;
@@ -1925,17 +1933,13 @@ class ActivityStarter {
            return container.mStack;
        }

        // The fullscreen stack can contain any task regardless of if the task is resizeable
        // or not. So, we let the task go in the fullscreen task if it is the focus stack.
        // Same also applies to dynamic stacks, as they behave similar to fullscreen stack.
        // If the freeform or docked stack has focus, and the activity to be launched is resizeable,
        // we can also put it in the focused stack.
        if (canLaunchIntoFocusedStack(r, newTask)) {
            if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
                    "computeStackFocus: Have a focused stack=" + mSupervisor.mFocusedStack);
            return mSupervisor.mFocusedStack;
        }

        if (mSourceDisplayId == DEFAULT_DISPLAY) {
            // We first try to put the task in the first dynamic stack on home display.
            final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks;
            for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
@@ -1946,12 +1950,14 @@ class ActivityStarter {
                    return stack;
                }
            }

            // If there is no suitable dynamic stack then we figure out which static stack to use.
            final int stackId = task != null ? task.getLaunchStackId() :
                    bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
                            FULLSCREEN_WORKSPACE_STACK_ID;
            stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
        } else {
            stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
        }
        if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
                + r + " stackId=" + stack.mStackId);
        return stack;
@@ -1959,35 +1965,35 @@ class ActivityStarter {

    /** Check if provided activity record can launch in currently focused stack. */
    private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
        // The fullscreen stack can contain any task regardless of if the task is resizeable
        // or not. So, we let the task go in the fullscreen task if it is the focus stack.
        // Same also applies to dynamic stacks, as they behave similar to fullscreen stack.
        // If the freeform or docked stack has focus, and the activity to be launched is resizeable,
        // we can also put it in the focused stack.
        final ActivityStack focusedStack = mSupervisor.mFocusedStack;
        final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
        final boolean canUseFocusedStack;
        switch (focusedStackId) {
            case FULLSCREEN_WORKSPACE_STACK_ID:
                // The fullscreen stack can contain any task regardless of if the task is resizeable
                // or not. So, we let the task go in the fullscreen task if it is the focus stack.
                canUseFocusedStack = true;
                break;
            case ASSISTANT_STACK_ID:
                canUseFocusedStack = r.isAssistantActivity();
                break;
            case DOCKED_STACK_ID:
                // Any activty which supports split screen can go in the docked stack.
                canUseFocusedStack = r.supportsSplitScreen();
                break;
            case FREEFORM_WORKSPACE_STACK_ID:
                // Any activty which supports freeform can go in the freeform stack.
                canUseFocusedStack = r.supportsFreeform();
                break;
            default:
                canUseFocusedStack = isDynamicStack(focusedStackId)
                        && mSupervisor.isCallerAllowedToLaunchOnDisplay(r.launchedFromPid,
                        r.launchedFromUid, focusedStack.mDisplayId);
                // Dynamic stacks behave similarly to the fullscreen stack and can contain any task.
                canUseFocusedStack = isDynamicStack(focusedStackId);
        }

        return canUseFocusedStack
                && (!newTask || focusedStack.mActivityContainer.isEligibleForNewTasks());
                && (!newTask || focusedStack.mActivityContainer.isEligibleForNewTasks())
                // We strongly prefer to launch activities on the same display as their source.
                && (mSourceDisplayId == focusedStack.mDisplayId);
    }

    private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task,
@@ -2034,7 +2040,8 @@ class ActivityStarter {
            return mSupervisor.getValidLaunchStackOnDisplay(launchDisplayId, r);
        }

        if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0) {
        if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0
                || mSourceDisplayId != DEFAULT_DISPLAY) {
            return null;
        }
        // Otherwise handle adjacent launch.