Merge "Allow at most one pinned stack task." into oc-dev

This commit is contained in:
Bryce Lee
2017-04-18 21:30:58 +00:00
committed by Android (Google) Code Review
8 changed files with 215 additions and 42 deletions

View File

@@ -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);

View File

@@ -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() {

View File

@@ -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;
}
}

View File

@@ -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));
}
}
}

View File

@@ -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());
}
}

View File

@@ -17,6 +17,11 @@
package com.android.server.am;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import org.mockito.invocation.InvocationOnMock;
import android.app.ActivityManager;
import android.content.ComponentName;
@@ -33,6 +38,7 @@ import com.android.server.AttributeCache;
import com.android.server.wm.AppWindowContainerController;
import com.android.server.wm.StackWindowController;
import com.android.server.wm.TaskWindowContainerController;
import com.android.server.wm.WindowManagerService;
import com.android.server.wm.WindowTestUtils;
import org.junit.After;
@@ -64,16 +70,15 @@ public class ActivityTestsBase {
protected ActivityManagerService createActivityManagerService() {
final ActivityManagerService service = new TestActivityManagerService(mContext);
service.mWindowManager = WindowTestUtils.getWindowManagerService(mContext);
service.mWindowManager = WindowTestUtils.getMockWindowManagerService();
return service;
}
protected static TestActivityStack createActivityStack(ActivityManagerService service,
protected static ActivityStack createActivityStack(ActivityManagerService service,
int stackId, int displayId, boolean onTop) {
if (service.mStackSupervisor instanceof TestActivityStackSupervisor) {
final TestActivityStack stack = ((TestActivityStackSupervisor) service.mStackSupervisor)
return ((TestActivityStackSupervisor) service.mStackSupervisor)
.createTestStack(service, stackId, onTop);
return stack;
}
return null;
@@ -103,7 +108,7 @@ public class ActivityTestsBase {
}
protected static TaskRecord createTask(ActivityManagerService service,
ComponentName component, ActivityStack stack) {
ComponentName component, int stackId) {
final ActivityInfo aInfo = new ActivityInfo();
aInfo.applicationInfo = new ApplicationInfo();
aInfo.applicationInfo.packageName = component.getPackageName();
@@ -113,13 +118,16 @@ public class ActivityTestsBase {
final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/,
null /*_taskDescription*/, new ActivityManager.TaskThumbnailInfo());
final ActivityStack stack = service.mStackSupervisor.getStack(stackId,
true /*createStaticStackIfNeeded*/, true /*onTop*/);
stack.addTask(task, true, "creating test task");
task.setStack(stack);
task.createWindowContainer(true, true);
task.setWindowContainerController(mock(TaskWindowContainerController.class));
return task;
}
/**
* An {@link ActivityManagerService} subclass which provides a test
* {@link ActivityStackSupervisor}.
@@ -127,6 +135,9 @@ public class ActivityTestsBase {
protected static class TestActivityManagerService extends ActivityManagerService {
public TestActivityManagerService(Context context) {
super(context);
mSupportsMultiWindow = true;
mSupportsMultiDisplay = true;
mWindowManager = WindowTestUtils.getWindowManagerService(context);
}
@Override
@@ -142,6 +153,12 @@ public class ActivityTestsBase {
protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
super(service, looper);
mWindowManager = prepareMockWindowManager();
}
// No home stack is set.
@Override
void moveHomeStackToFront(String reason) {
}
// Invoked during {@link ActivityStack} creation.
@@ -149,18 +166,45 @@ public class ActivityTestsBase {
void updateUIDsPresentOnDisplay() {
}
public TestActivityStack createTestStack(ActivityManagerService service, int stackId,
boolean onTop) {
// Just return the current front task.
@Override
ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus) {
return mFocusedStack;
}
// Called when moving activity to pinned stack.
@Override
void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
boolean preserveWindows) {
}
public <T extends ActivityStack> T createTestStack(ActivityManagerService service,
int stackId, boolean onTop) {
final ActivityDisplay display = new ActivityDisplay();
final TestActivityContainer container =
new TestActivityContainer(service, stackId, display, onTop);
return container.getStack();
mActivityContainers.put(stackId, container);
return (T) container.getStack();
}
@Override
protected <T extends ActivityStack> T getStack(int stackId,
boolean createStaticStackIfNeeded, boolean createOnTop) {
final T stack = super.getStack(stackId, createStaticStackIfNeeded, createOnTop);
if (stack != null || !createStaticStackIfNeeded) {
return stack;
}
return createTestStack(mService, stackId, createOnTop);
}
private class TestActivityContainer extends ActivityContainer {
private ActivityManagerService mService;
private TestActivityStack mStack;
private final ActivityManagerService mService;
private boolean mOnTop;
private int mStackId;
private ActivityStack mStack;
TestActivityContainer(ActivityManagerService service, int stackId,
ActivityDisplay activityDisplay, boolean onTop) {
@@ -174,12 +218,16 @@ public class ActivityTestsBase {
// we cannot set {@link mService} by the time the super constructor calling this
// method is invoked.
mOnTop = onTop;
mStackId = stackId;
}
public TestActivityStack getStack() {
public ActivityStack getStack() {
if (mStack == null) {
mStack = new TestActivityStack(this,
new RecentTasks(mService, mService.mStackSupervisor), mOnTop);
final RecentTasks recents =
new RecentTasks(mService, mService.mStackSupervisor);
mStack = mStackId == ActivityManager.StackId.PINNED_STACK_ID
? new PinnedActivityStack(this, recents, mOnTop)
: new TestActivityStack(this, recents, mOnTop);
}
return mStack;
@@ -187,13 +235,31 @@ public class ActivityTestsBase {
}
}
private static WindowManagerService prepareMockWindowManager() {
final WindowManagerService service = mock(WindowManagerService.class);
doAnswer((InvocationOnMock invocationOnMock) -> {
final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
if (runnable != null) {
runnable.run();
}
return null;
}).when(service).inSurfaceTransaction(any());
return service;
}
protected interface ActivityStackReporter {
int onActivityRemovedFromStackInvocationCount();
}
/**
* Override of {@link ActivityStack} that tracks test metrics, such as the number of times a
* method is called. Note that its functionality depends on the implementations of the
* construction arguments.
*/
protected static class TestActivityStack<T extends StackWindowController>
extends ActivityStack<T> {
extends ActivityStack<T> implements ActivityStackReporter {
private int mOnActivityRemovedFromStackCount = 0;
private T mContainerController;
TestActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer,
@@ -208,6 +274,7 @@ public class ActivityTestsBase {
}
// Returns the number of times {@link #onActivityRemovedFromStack} has been called
@Override
public int onActivityRemovedFromStackInvocationCount() {
return mOnActivityRemovedFromStackCount;
}
@@ -225,6 +292,7 @@ public class ActivityTestsBase {
}
}
protected static class ActivityStackBuilder {
private boolean mOnTop = true;
private int mStackId = 0;
@@ -251,7 +319,7 @@ public class ActivityTestsBase {
return this;
}
public TestActivityStack build() {
public ActivityStack build() {
return createActivityStack(mService, mStackId, mDisplayId, mOnTop);
}
}

View File

@@ -90,6 +90,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
return null;
}).when(am).notifyKeyguardFlagsChanged(any());
}
sWm = WindowManagerService.main(context, mock(InputManagerService.class), true, false,
false, new TestWindowManagerPolicy());
}

View File

@@ -47,6 +47,13 @@ public class WindowTestUtils {
return TestWindowManagerPolicy.getWindowManagerService(context);
}
/**
* Retrieves an instance of a mock {@link WindowManagerService}.
*/
public static WindowManagerService getMockWindowManagerService() {
return mock(WindowManagerService.class);
}
/**
* Creates a mock instance of {@link StackWindowController}.
*/