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

Commit 04ab3466 authored by Bryce Lee's avatar Bryce Lee
Browse files

Allow at most one pinned stack task.

This changelist enforces only one task may be present in the pinned
stack at a time. If a task is already persent, the existing task is
moved to the fullscreen stack.

Fixes: 36844394
Test: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerPinnedStackTests#testPipMovedToFullscreenStack
Test: bit FrameworksServicesTests:com.android.server.am.ActivityStackSupervisorTests#testDisallowMultipleTasksInPinnedStack

Change-Id: Iaf0fbda6df835d93738fdf6f7f3a8c5956c2b262
parent ff4e132c
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -2878,6 +2878,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D

        mWindowManager.deferSurfaceLayout();

        // This will clear the pinned stack by moving an existing task to the full screen stack,
        // ensuring only one task is present.
        moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);

        // Need to make sure the pinned stack exist so we can resize it below...
        final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);

+16 −2
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import android.service.voice.IVoiceInteractionSession;
import android.util.DisplayMetrics;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.util.XmlUtils;

@@ -445,10 +446,23 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta

        final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
        final Configuration overrideConfig = getOverrideConfiguration();
        mWindowContainerController = new TaskWindowContainerController(taskId, this,
        setWindowContainerController(new TaskWindowContainerController(taskId, this,
                getStack().getWindowContainerController(), userId, bounds, overrideConfig,
                mResizeMode, mSupportsPictureInPicture, isHomeTask(), onTop, showForAllUsers,
                lastTaskDescription);
                lastTaskDescription));
    }

    /**
     * Should only be invoked from {@link #createWindowContainer(boolean, boolean)}.
     */
    @VisibleForTesting
    protected void setWindowContainerController(TaskWindowContainerController controller) {
        if (mWindowContainerController != null) {
            throw new IllegalArgumentException("Window container=" + mWindowContainerController
                    + " already created for task=" + this);
        }

        mWindowContainerController = controller;
    }

    void removeWindowContainer() {
+27 −15
Original line number Diff line number Diff line
@@ -16,7 +16,8 @@

package com.android.server.am;

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import android.content.ComponentName;
import android.platform.test.annotations.Presubmit;
@@ -36,50 +37,61 @@ import org.junit.Test;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityRecordTests extends ActivityTestsBase {
    private static final int TEST_STACK_ID = 100;

    private final ComponentName testActivityComponent =
            ComponentName.unflattenFromString("com.foo/.BarActivity");
    @Test
    public void testStackCleanupOnClearingTask() throws Exception {
        final ActivityManagerService service = createActivityManagerService();
        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
        final TaskRecord task = createTask(service, testActivityComponent, testStack);
        final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
        final ActivityRecord record = createActivity(service, testActivityComponent, task);

        record.setTask(null);
        assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1);
        assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1);
    }

    @Test
    public void testStackCleanupOnActivityRemoval() throws Exception {
        final ActivityManagerService service = createActivityManagerService();
        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
        final TaskRecord task = createTask(service, testActivityComponent, testStack);
        final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
        final ActivityRecord record = createActivity(service, testActivityComponent, task);

        task.removeActivity(record);
        assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1);
        assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID),  1);
    }

    @Test
    public void testStackCleanupOnTaskRemoval() throws Exception {
        final ActivityManagerService service = createActivityManagerService();
        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
        final TaskRecord task = createTask(service, testActivityComponent, testStack);
        final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
        final ActivityRecord record = createActivity(service, testActivityComponent, task);

        testStack.removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING);
        assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1);
        service.mStackSupervisor.getStack(TEST_STACK_ID)
                .removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING);

        // Stack should be gone on task removal.
        assertNull(service.mStackSupervisor.getStack(TEST_STACK_ID));
    }

    @Test
    public void testNoCleanupMovingActivityInSameStack() throws Exception {
        final ActivityManagerService service = createActivityManagerService();
        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
        final TaskRecord oldTask = createTask(service, testActivityComponent, testStack);
        final TaskRecord oldTask = createTask(service, testActivityComponent, TEST_STACK_ID);
        final ActivityRecord record = createActivity(service, testActivityComponent, oldTask);
        final TaskRecord newTask = createTask(service, testActivityComponent, testStack);
        final TaskRecord newTask = createTask(service, testActivityComponent, TEST_STACK_ID);

        record.reparent(newTask, 0, null /*reason*/);
        assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 0);
        assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 0);
    }

    private static int getActivityRemovedFromStackCount(ActivityManagerService service,
            int stackId) {
        final ActivityStack stack = service.mStackSupervisor.getStack(stackId);
        if (stack instanceof ActivityStackReporter) {
            return ((ActivityStackReporter) stack).onActivityRemovedFromStackInvocationCount();
        }

        return -1;
    }
}
+68 −0
Original line number Diff line number Diff line
@@ -16,8 +16,14 @@

package com.android.server.am;

import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import android.content.ComponentName;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
@@ -25,6 +31,10 @@ import android.support.test.runner.AndroidJUnit4;
import org.junit.runner.RunWith;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;

/**
@@ -37,6 +47,9 @@ import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackSupervisorTests extends ActivityTestsBase {
    private final ComponentName testActivityComponent =
            ComponentName.unflattenFromString("com.foo/.BarActivity");

    /**
     * This test ensures that we do not try to restore a task based off an invalid task id. The
     * stack supervisor is a test version so there will be no tasks present. We should expect
@@ -49,4 +62,59 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
                MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, 0 /*stackId*/);
        assertNull(task);
    }

    /**
     * This test ensures that an existing task in the pinned stack is moved to the fullscreen
     * activity stack when a new task is added.
     */
    @Test
    public void testReplacingTaskInPinnedStack() throws Exception {
        final ActivityManagerService service = createActivityManagerService();
        final TaskRecord firstTask = createTask(service, testActivityComponent,
                FULLSCREEN_WORKSPACE_STACK_ID);
        final ActivityRecord firstActivity = createActivity(service, testActivityComponent,
                firstTask);
        // Create a new task on the full screen stack
        final TaskRecord secondTask = createTask(service, testActivityComponent,
                FULLSCREEN_WORKSPACE_STACK_ID);
        final ActivityRecord secondActivity = createActivity(service, testActivityComponent,
                secondTask);
        service.mStackSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack",
                service.mStackSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID));

        // Ensure full screen stack has both tasks.
        ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask,
                secondTask);

        // Move first activity to pinned stack.
        service.mStackSupervisor.moveActivityToPinnedStackLocked(firstActivity,
                new Rect() /*sourceBounds*/, 0f /*aspectRatio*/, false, "initialMove");

        // Ensure a task has moved over.
        ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, firstTask);
        ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, secondTask);

        // Move second activity to pinned stack.
        service.mStackSupervisor.moveActivityToPinnedStackLocked(secondActivity,
                new Rect() /*sourceBounds*/, 0f /*aspectRatio*/ /*destBounds*/, false, "secondMove");

        // Ensure stacks have swapped tasks.
        ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, secondTask);
        ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask);
    }

    private static void ensureStackPlacement(ActivityStackSupervisor supervisor, int stackId,
            TaskRecord... tasks) {
        final ActivityStack stack = supervisor.getStack(stackId);
        final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
        assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);

        if (tasks == null) {
            return;
        }

        for (TaskRecord task : tasks) {
            assertTrue(stackTasks.contains(task));
        }
    }
}
+8 −9
Original line number Diff line number Diff line
@@ -37,28 +37,27 @@ import org.junit.Test;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackTests extends ActivityTestsBase {
    private final ComponentName testActivityComponent =
    private static final int TEST_STACK_ID = 100;
    private static final ComponentName testActivityComponent =
            ComponentName.unflattenFromString("com.foo/.BarActivity");

    @Test
    public void testEmptyTaskCleanupOnRemove() throws Exception {
        final ActivityManagerService service = createActivityManagerService();
        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
        final TaskRecord task = createTask(service, testActivityComponent, testStack);
        final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
        assertNotNull(task.getWindowContainerController());
        testStack.removeTask(task, "testEmptyTaskCleanupOnRemove",
                ActivityStack.REMOVE_TASK_MODE_DESTROYING);
        service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task,
                "testEmptyTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
        assertNull(task.getWindowContainerController());
    }
    @Test
    public void testOccupiedTaskCleanupOnRemove() throws Exception {
        final ActivityManagerService service = createActivityManagerService();
        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
        final TaskRecord task = createTask(service, testActivityComponent, testStack);
        final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
        final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
        assertNotNull(task.getWindowContainerController());
        testStack.removeTask(task, "testOccupiedTaskCleanupOnRemove",
                ActivityStack.REMOVE_TASK_MODE_DESTROYING);
        service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task,
                "testOccupiedTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
        assertNotNull(task.getWindowContainerController());
    }
}
Loading