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

Commit 53c6bc5e authored by Liran Binyamin's avatar Liran Binyamin Committed by Android (Google) Code Review
Browse files

Merge "Update logic around reusing bubbled activities" into main

parents 3f62917a a8a63db6
Loading
Loading
Loading
Loading
+15 −3
Original line number Diff line number Diff line
@@ -139,6 +139,7 @@ import com.android.server.wm.BackgroundActivityStartController.BalCode;
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import com.android.server.wm.LaunchParamsController.LaunchParams;
import com.android.server.wm.TaskFragment.EmbeddingCheckResult;
import com.android.wm.shell.Flags;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -1723,7 +1724,14 @@ class ActivityStarter {
        // Get top task at beginning because the order may be changed when reusing existing task.
        final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
        final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;
        final Task reusedTask = resolveReusableTask();
        final boolean sourceActivityLaunchedFromBubble =
                sourceRecord != null && sourceRecord.getLaunchedFromBubble();
        // if the flag is enabled, allow reusing bubbled tasks only if the source activity is
        // bubbled.
        final boolean includeLaunchedFromBubble =
                Flags.onlyReuseBubbledTaskWhenLaunchedFromBubble()
                        ? sourceActivityLaunchedFromBubble : true;
        final Task reusedTask = resolveReusableTask(includeLaunchedFromBubble);

        // If requested, freeze the task list
        if (mOptions != null && mOptions.freezeRecentTasksReordering()
@@ -2722,8 +2730,11 @@ class ActivityStarter {
    /**
     * Decide whether the new activity should be inserted into an existing task. Returns null
     * if not or an ActivityRecord with the task into which the new activity should be added.
     *
     * @param includeLaunchedFromBubble whether a task whose top activity was launched from a bubble
     *                                  should be allowed to be reused for the new activity.
     */
    private Task resolveReusableTask() {
    private Task resolveReusableTask(boolean includeLaunchedFromBubble) {
        // If a target task is specified, try to reuse that one
        if (mOptions != null && mOptions.getLaunchTaskId() != INVALID_TASK_ID) {
            Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId());
@@ -2767,7 +2778,8 @@ class ActivityStarter {
            } else {
                // Otherwise find the best task to put the activity in.
                intentActivity =
                        mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);
                        mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea,
                                includeLaunchedFromBubble);
            }
        }

+17 −8
Original line number Diff line number Diff line
@@ -314,13 +314,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
        private boolean isDocument;
        private Uri documentData;

        void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info) {
        // determines whether to include bubbled tasks. defaults to true to preserve previous
        // behavior.
        private boolean mIncludeLaunchedFromBubble = true;

        void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
                boolean includeLaunchedFromBubble) {
            mActivityType = activityType;
            mTaskAffinity = taskAffinity;
            mIntent = intent;
            mInfo = info;
            mIdealRecord = null;
            mCandidateRecord = null;
            mIncludeLaunchedFromBubble = includeLaunchedFromBubble;
        }

        /**
@@ -362,7 +368,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
            }

            // Overlays should not be considered as the task's logical top activity.
            final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
            final ActivityRecord r = task.getTopNonFinishingActivity(
                    false /* includeOverlays */, mIncludeLaunchedFromBubble);

            if (r == null || r.finishing || r.mUserId != userId
                    || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
@@ -2372,18 +2379,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
    }

    @Nullable
    ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) {
    ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea,
            boolean includeLaunchedFromBubble) {
        return findTask(r.getActivityType(), r.taskAffinity, r.intent, r.info,
                preferredTaskDisplayArea);
                preferredTaskDisplayArea, includeLaunchedFromBubble);
    }

    @Nullable
    ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
            TaskDisplayArea preferredTaskDisplayArea) {
            TaskDisplayArea preferredTaskDisplayArea, boolean includeLaunchedFromBubble) {
        ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s"
                        + ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info,
                preferredTaskDisplayArea);
        mTmpFindTaskResult.init(activityType, taskAffinity, intent, info);
                        + ", info=%s, preferredTDA=%s, includeLaunchedFromBubble=%b", activityType,
                taskAffinity, intent, info, preferredTaskDisplayArea, includeLaunchedFromBubble);
        mTmpFindTaskResult.init(activityType, taskAffinity, intent, info,
                includeLaunchedFromBubble);

        // Looking up task on preferred display area first
        ActivityRecord candidateActivity = null;
+18 −5
Original line number Diff line number Diff line
@@ -1104,21 +1104,34 @@ class TaskFragment extends WindowContainer<WindowContainer> {
    }

    ActivityRecord getTopNonFinishingActivity() {
        return getTopNonFinishingActivity(true /* includeOverlays */);
        return getTopNonFinishingActivity(
                true /* includeOverlays */, true /* includeLaunchedFromBubble */);
    }

    /**
     * Returns the top-most non-finishing activity, even if the activity is NOT ok to show to
     * the current user.
     * @param includeOverlays whether the task overlay activity should be included.
     * @param includeLaunchedFromBubble whether activities that were launched from a bubble should
     *                                  be included.
     * @see #topRunningActivity(boolean)
     */
    ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
        // Split into 2 to avoid object creation due to variable capture.
    ActivityRecord getTopNonFinishingActivity(boolean includeOverlays,
            boolean includeLaunchedFromBubble) {
        // Split to avoid object creation due to variable capture.
        if (includeOverlays) {
            return getActivity((r) -> !r.finishing);
            if (includeLaunchedFromBubble) {
                return getActivity(r -> !r.finishing);
            } else {
                return getActivity(r -> !r.finishing && !r.getLaunchedFromBubble());
            }
        }
        if (includeLaunchedFromBubble) {
            return getActivity(r -> !r.finishing && !r.isTaskOverlay());
        } else {
            return getActivity(
                    r -> !r.finishing && !r.isTaskOverlay() && !r.getLaunchedFromBubble());
        }
        return getActivity((r) -> !r.finishing && !r.isTaskOverlay());
    }

    ActivityRecord topRunningActivity() {
+125 −5
Original line number Diff line number Diff line
@@ -95,6 +95,8 @@ import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.service.voice.IVoiceInteractionSession;
@@ -112,6 +114,7 @@ import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
import com.android.server.wm.utils.MockTracker;
import com.android.wm.shell.Flags;

import org.junit.After;
import org.junit.Before;
@@ -492,7 +495,8 @@ public class ActivityStarterTests extends WindowTestsBase {

        // Start activity and delivered new intent.
        starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
        doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any());
        doReturn(splitSecondReusableActivity)
                .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
        final int result = starter.setReason("testSplitScreenDeliverToTop").execute();

        // Ensure result is delivering intent to top.
@@ -519,7 +523,8 @@ public class ActivityStarterTests extends WindowTestsBase {

        // Start activity and delivered new intent.
        starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
        doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any());
        doReturn(splitSecondReusableActivity)
                .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
        final int result = starter.setReason("testSplitScreenMoveToFront").execute();

        // Ensure result is moving task to front.
@@ -566,7 +571,7 @@ public class ActivityStarterTests extends WindowTestsBase {

        // Start activity and delivered new intent.
        starter.getIntent().setComponent(activities.get(3).mActivityComponent);
        doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any());
        doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
        final int result = starter.setReason("testDesktopModeDeliverToTop").execute();

        // Ensure result is delivering intent to top.
@@ -593,7 +598,8 @@ public class ActivityStarterTests extends WindowTestsBase {

        // Start activity and delivered new intent.
        starter.getIntent().setComponent(desktopModeReusableActivity.mActivityComponent);
        doReturn(desktopModeReusableActivity).when(mRootWindowContainer).findTask(any(), any());
        doReturn(desktopModeReusableActivity)
                .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
        final int result = starter.setReason("testDesktopModeMoveToFront").execute();

        // Ensure result is moving task to front.
@@ -755,7 +761,7 @@ public class ActivityStarterTests extends WindowTestsBase {

        final ActivityRecord baseActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
        baseActivity.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED);
        doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any());
        doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any(), anyBoolean());

        ActivityOptions rawOptions = ActivityOptions.makeBasic()
                .setPendingIntentCreatorBackgroundActivityStartMode(
@@ -1648,6 +1654,120 @@ public class ActivityStarterTests extends WindowTestsBase {
        assertNotEquals(inTask, target.getTask());
    }

    @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
    @Test
    public void launchActivity_reusesBubbledTask() {
        final ActivityStarter starter = prepareStarter(0, false);
        final ActivityRecord bubbledActivity = createBubbledActivity();

        // create the target activity to be launched with the same component as the bubbled activity
        final ActivityRecord targetRecord = new ActivityBuilder(mAtm)
                .setLaunchMode(LAUNCH_SINGLE_TASK)
                .setComponent(ActivityBuilder.getDefaultComponent()).build();
        starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
        startActivityInner(starter, targetRecord, bubbledActivity, null /* options */,
                null /* inTask */, null /* inTaskFragment */);

        assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
    }

    @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
    @Test
    public void launchActivity_nullSourceRecord_doesNotReuseBubbledTask() {
        final ActivityStarter starter = prepareStarter(0, false);
        final ActivityRecord bubbledActivity = createBubbledActivity();

        // create the target activity to be launched
        final ActivityRecord targetRecord =
                new ActivityBuilder(mAtm)
                        .setLaunchMode(LAUNCH_SINGLE_TASK)
                        .setComponent(ActivityBuilder.getDefaultComponent()).build();
        starter.getIntent().setComponent(bubbledActivity.mActivityComponent);

        // pass null as the source record
        startActivityInner(starter, targetRecord, null, null /* options */,
                null /* inTask */, null /* inTaskFragment */);

        assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask());
    }

    @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
    @Test
    public void launchActivity_nonBubbledSourceRecord_doesNotReuseBubbledTask() {
        final ActivityStarter starter = prepareStarter(0, false);
        final ActivityRecord bubbledActivity = createBubbledActivity();

        // create a non bubbled activity
        final ActivityRecord nonBubbleSourceRecord =
                new ActivityBuilder(mAtm).setCreateTask(true)
                        .setLaunchMode(LAUNCH_SINGLE_TASK)
                        .setComponent(ActivityBuilder.getDefaultComponent())
                        .build();

        // create the target activity to be launched
        final ActivityRecord targetRecord =
                new ActivityBuilder(mAtm)
                        .setLaunchMode(LAUNCH_SINGLE_TASK)
                        .setComponent(ActivityBuilder.getDefaultComponent()).build();
        starter.getIntent().setComponent(bubbledActivity.mActivityComponent);

        // use the non bubbled activity as the source
        startActivityInner(starter, targetRecord, nonBubbleSourceRecord, null /* options */,
                null /* inTask */, null /* inTaskFragment*/);

        assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask());
    }

    @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
    @Test
    public void launchActivity_nullSourceRecord_flagDisabled_reusesBubbledTask() {
        final ActivityStarter starter = prepareStarter(0, false);
        final ActivityRecord bubbledActivity = createBubbledActivity();

        // create the target activity to be launched
        final ActivityRecord targetRecord =
                new ActivityBuilder(mAtm)
                        .setLaunchMode(LAUNCH_SINGLE_TASK)
                        .setComponent(ActivityBuilder.getDefaultComponent()).build();
        starter.getIntent().setComponent(bubbledActivity.mActivityComponent);

        // pass null as the source record
        startActivityInner(starter, targetRecord, null, null /* options */,
                null /* inTask */, null /* inTaskFragment */);

        assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
    }

    @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
    @Test
    public void launchActivity_fromBubble_flagDisabled_reusesBubbledTask() {
        final ActivityStarter starter = prepareStarter(0, false);
        final ActivityRecord bubbledActivity = createBubbledActivity();

        // create the target activity to be launched with the same component as the bubbled activity
        final ActivityRecord targetRecord =
                new ActivityBuilder(mAtm)
                        .setLaunchMode(LAUNCH_SINGLE_TASK)
                        .setComponent(ActivityBuilder.getDefaultComponent()).build();
        starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
        startActivityInner(starter, targetRecord, bubbledActivity, null /* options */,
                null /* inTask */, null /* inTaskFragment */);

        assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
    }

    private ActivityRecord createBubbledActivity() {
        final ActivityOptions opts = ActivityOptions.makeBasic();
        opts.setTaskAlwaysOnTop(true);
        opts.setLaunchedFromBubble(true);
        opts.setLaunchBounds(new Rect(10, 10, 100, 100));
        return new ActivityBuilder(mAtm)
                .setCreateTask(true)
                .setComponent(ActivityBuilder.getDefaultComponent())
                .setActivityOptions(opts)
                .build();
    }

    private static void startActivityInner(ActivityStarter starter, ActivityRecord target,
            ActivityRecord source, ActivityOptions options, Task inTask,
            TaskFragment inTaskFragment) {
+6 −5
Original line number Diff line number Diff line
@@ -405,11 +405,12 @@ public class RootTaskTests extends WindowTestsBase {

        final RootWindowContainer.FindTaskResult result =
                new RootWindowContainer.FindTaskResult();
        result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info);
        result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info, true);
        result.process(task);

        assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */));
        assertEquals(taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */));
        assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */, true));
        assertEquals(
                taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */, true));
        assertNotNull(result.mIdealRecord);
    }

@@ -432,7 +433,7 @@ public class RootTaskTests extends WindowTestsBase {
        final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent(
                target).setTargetActivity(targetActivity).build();
        RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult();
        result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info);
        result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info, true);
        result.process(parentTask);
        assertThat(result.mIdealRecord).isNotNull();

@@ -440,7 +441,7 @@ public class RootTaskTests extends WindowTestsBase {
        final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent(
                alias).setTargetActivity(targetActivity).build();
        result = new RootWindowContainer.FindTaskResult();
        result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info);
        result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info, true);
        result.process(parentTask);
        assertThat(result.mIdealRecord).isNotNull();
    }
Loading