Added support for static vs. dynamic stacks
Now that stacks represent workspaces we can define static
stacks which help shape the characteristics of the tasks
they contain. For example, fullscreen tasks/activities will
normally be launched in the stack with id
FULLSCREEN_WORKSPACE_STACK_ID, while freeform tasks/activities
will normally be launched in the stack with id
FREEFORM_WORKSPACE_STACK_ID.
Also, added ability to position a task at any index in a stack.
Bug: 22068114
Change-Id: Ib6c62a84b5f204fbf072755264c5c5eda6184f97
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index b1e4701..3ed72e5 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -139,6 +139,7 @@
" am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
" am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
" am stack split <STACK_ID> <v|h> [INTENT]\n" +
+ " am stack positiontask <TASK_ID> <STACK_ID> <POSITION>\n" +
" am stack list\n" +
" am stack info <STACK_ID>\n" +
" am task lock <TASK_ID>\n" +
@@ -280,6 +281,8 @@
" of the current task will be moved to the new stack. Command will also force\n" +
" all current tasks in both stacks to be resizeable.\n" +
"\n" +
+ "am stack positiontask: place <TASK_ID> in <STACK_ID> at <POSITION>" +
+ "\n" +
"am stack list: list all of the activity stacks and their sizes.\n" +
"\n" +
"am stack info: display the information about activity stack <STACK_ID>.\n" +
@@ -1921,6 +1924,8 @@
runStackMoveTask();
} else if (op.equals("resize")) {
runStackResize();
+ } else if (op.equals("positiontask")) {
+ runStackPositionTask();
} else if (op.equals("list")) {
runStackList();
} else if (op.equals("info")) {
@@ -1984,6 +1989,20 @@
}
}
+ private void runStackPositionTask() throws Exception {
+ String taskIdStr = nextArgRequired();
+ int taskId = Integer.valueOf(taskIdStr);
+ String stackIdStr = nextArgRequired();
+ int stackId = Integer.valueOf(stackIdStr);
+ String positionStr = nextArgRequired();
+ int position = Integer.valueOf(positionStr);
+
+ try {
+ mAm.positionTaskInStack(taskId, stackId, position);
+ } catch (RemoteException e) {
+ }
+ }
+
private void runStackList() throws Exception {
try {
List<StackInfo> stacks = mAm.getAllStackInfos();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5974fcf..b7a7d07 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -404,6 +404,42 @@
*/
public static final int COMPAT_MODE_TOGGLE = 2;
+ /**
+ * First static stack stack ID.
+ * @hide
+ */
+ public static final int FIRST_STATIC_STACK_ID = 0;
+
+ /**
+ * Home activity stack ID.
+ * @hide
+ */
+ public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
+
+ /**
+ * ID of stack where fullscreen activities are normally launched into.
+ * @hide
+ */
+ public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
+
+ /**
+ * ID of stack where freeform/resized activities are normally launched into.
+ * @hide
+ */
+ public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
+
+ /**
+ * Last static stack stack ID.
+ * @hide
+ */
+ public static final int LAST_STATIC_STACK_ID = FREEFORM_WORKSPACE_STACK_ID;
+
+ /**
+ * Start of ID range used by stacks that are created dynamically.
+ * @hide
+ */
+ public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
+
/** @hide */
public int getFrontActivityScreenCompatMode() {
try {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index facaee4..abfe215 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -752,6 +752,16 @@
return true;
}
+ case POSITION_TASK_IN_STACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int taskId = data.readInt();
+ int stackId = data.readInt();
+ int position = data.readInt();
+ positionTaskInStack(taskId, stackId, position);
+ reply.writeNoException();
+ return true;
+ }
+
case GET_ALL_STACK_INFOS_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
List<StackInfo> list = getAllStackInfos();
@@ -3469,6 +3479,20 @@
reply.recycle();
}
@Override
+ public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(taskId);
+ data.writeInt(stackId);
+ data.writeInt(position);
+ mRemote.transact(POSITION_TASK_IN_STACK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ @Override
public List<StackInfo> getAllStackInfos() throws RemoteException
{
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c3e55f1..4944bfc 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -139,6 +139,7 @@
public void moveTaskBackwards(int task) throws RemoteException;
public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
public void resizeStack(int stackId, Rect bounds) throws RemoteException;
+ public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
public List<StackInfo> getAllStackInfos() throws RemoteException;
public StackInfo getStackInfo(int stackId) throws RemoteException;
public boolean isInHomeStack(int taskId) throws RemoteException;
@@ -874,4 +875,5 @@
// Start of N transactions
int START_BINDER_TRACKING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 340;
int STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 341;
+ int POSITION_TASK_IN_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 342;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 266e3c7..7b50999 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.app.ActivityManager.HOME_STACK_ID;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -28,7 +29,6 @@
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
import static com.android.server.am.ActivityManagerDebugConfig.*;
-import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -8898,7 +8898,8 @@
"createStackOnDisplay()");
synchronized (this) {
final int stackId = mStackSupervisor.getNextStackId();
- final ActivityStack stack = mStackSupervisor.createStackOnDisplay(stackId, displayId);
+ final ActivityStack stack =
+ mStackSupervisor.createStackOnDisplay(stackId, displayId, true /*onTop*/);
if (stack == null) {
return null;
}
@@ -8952,6 +8953,28 @@
}
@Override
+ public void positionTaskInStack(int taskId, int stackId, int position) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "positionTaskInStack()");
+ if (stackId == HOME_STACK_ID) {
+ Slog.e(TAG, "positionTaskInStack: Attempt to change the position of task "
+ + taskId + " in/to home stack",
+ new RuntimeException("here").fillInStackTrace());
+ }
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (DEBUG_STACK) Slog.d(TAG_STACK,
+ "positionTaskInStack: positioning task=" + taskId
+ + " in stackId=" + stackId + " at position=" + position);
+ mStackSupervisor.positionTaskInStackLocked(taskId, stackId, position);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
public List<StackInfo> getAllStackInfos() {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"getAllStackInfos()");
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 431b433..e6c8d43 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.HOME_STACK_ID;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -23,8 +25,6 @@
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
-
import android.util.ArraySet;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
@@ -1211,6 +1211,15 @@
return true;
}
+ // Any stack that isn't the front stack is not visible, except for the case of the home
+ // stack behind the main application stack since we can have dialog activities on the
+ // main application stack that need the home stack to display behind them.
+ // TODO(multi-window): Also need to add exception for side-by-side stacks.
+ final boolean homeStack = mStackId == HOME_STACK_ID;
+ if (!homeStack) {
+ return false;
+ }
+
/**
* Start at the task above this one and go up, looking for a visible
* fullscreen activity, or a translucent activity that requested the
@@ -1218,10 +1227,10 @@
*/
for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) {
final ActivityStack stack = mStacks.get(i);
- // stack above isn't fullscreen, so, we assume we're still visible.
- if (!stack.mFullscreen) {
- continue;
+ if (stack.mStackId != FULLSCREEN_WORKSPACE_STACK_ID) {
+ return false;
}
+
final ArrayList<TaskRecord> tasks = stack.getAllTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = tasks.get(taskNdx);
@@ -2034,6 +2043,31 @@
return null;
}
+ private void insertTaskAtPosition(TaskRecord task, int position) {
+ if (position >= mTaskHistory.size()) {
+ insertTaskAtTop(task, null);
+ return;
+ }
+ // Calculate maximum possible position for this task.
+ int maxPosition = mTaskHistory.size();
+ if (!mStackSupervisor.isCurrentProfileLocked(task.userId)
+ && task.topRunningActivityLocked(null) == null) {
+ // Put non-current user tasks below current user tasks.
+ while (maxPosition > 0) {
+ final TaskRecord tmpTask = mTaskHistory.get(maxPosition - 1);
+ if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId)
+ || tmpTask.topRunningActivityLocked(null) == null) {
+ break;
+ }
+ maxPosition--;
+ }
+ }
+ position = Math.min(position, maxPosition);
+ mTaskHistory.remove(task);
+ mTaskHistory.add(position, task);
+ updateTaskMovement(task, true);
+ }
+
private void insertTaskAtTop(TaskRecord task, ActivityRecord newActivity) {
// If the moving task is over home stack, transfer its return type to next task
if (task.isOverHomeStack()) {
@@ -4363,6 +4397,17 @@
}
}
+ void positionTask(final TaskRecord task, int position, boolean moving) {
+ task.stack = this;
+ insertTaskAtPosition(task, position);
+ if (!moving && task.voiceSession != null) {
+ try {
+ task.voiceSession.taskStarted(task.intent, task.taskId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
public int getStackId() {
return mStackId;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e7f28c5..4169754 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,6 +17,12 @@
package com.android.server.am;
import static android.Manifest.permission.START_ANY_ACTIVITY;
+import static android.app.ActivityManager.FIRST_DYNAMIC_STACK_ID;
+import static android.app.ActivityManager.FIRST_STATIC_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.HOME_STACK_ID;
+import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
@@ -146,8 +152,6 @@
private static final String TAG_VISIBLE_BEHIND = TAG + POSTFIX_VISIBLE_BEHIND;
private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
- public static final int HOME_STACK_ID = 0;
-
/** How long we wait until giving up on the last activity telling us it is idle. */
static final int IDLE_TIMEOUT = 10 * 1000;
@@ -214,8 +218,8 @@
WindowManagerService mWindowManager;
DisplayManager mDisplayManager;
- /** Identifier counter for all ActivityStacks */
- private int mLastStackId = HOME_STACK_ID;
+ /** Counter for next free stack ID to use for dynamic activity stacks. */
+ private int mNextFreeStackId = FIRST_DYNAMIC_STACK_ID;
/** Task identifier that activities are currently being started in. Incremented each time a
* new task is created. */
@@ -402,7 +406,7 @@
mActivityDisplays.put(displayId, activityDisplay);
}
- createStackOnDisplay(HOME_STACK_ID, Display.DEFAULT_DISPLAY);
+ createStackOnDisplay(HOME_STACK_ID, Display.DEFAULT_DISPLAY, true);
mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
@@ -1794,8 +1798,12 @@
}
}
- // Need to create an app stack for this user.
- stack = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY);
+ // TODO (multi-window): Change to select task id based on if the task should on in
+ // fullscreen, freefrom, or sid-by-side stack.
+ stack = getStack(
+ FULLSCREEN_WORKSPACE_STACK_ID,
+ true /*createStaticStackIfNeeded*/,
+ true /*createOnTop*/);
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ r + " stackId=" + stack.mStackId);
return stack;
@@ -2782,11 +2790,19 @@
}
ActivityStack getStack(int stackId) {
+ return getStack(stackId, false /*createStaticStackIfNeeded*/, false /*createOnTop*/);
+ }
+
+ ActivityStack getStack(int stackId, boolean createStaticStackIfNeeded, boolean createOnTop) {
ActivityContainer activityContainer = mActivityContainers.get(stackId);
if (activityContainer != null) {
return activityContainer.mStack;
}
- return null;
+ if (!createStaticStackIfNeeded
+ || (stackId < FIRST_STATIC_STACK_ID || stackId > LAST_STATIC_STACK_ID)) {
+ return null;
+ }
+ return createStackOnDisplay(stackId, Display.DEFAULT_DISPLAY, createOnTop);
}
ArrayList<ActivityStack> getStacks() {
@@ -2931,7 +2947,7 @@
}
}
- ActivityStack createStackOnDisplay(int stackId, int displayId) {
+ ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay == null) {
return null;
@@ -2939,50 +2955,29 @@
ActivityContainer activityContainer = new ActivityContainer(stackId);
mActivityContainers.put(stackId, activityContainer);
- activityContainer.attachToDisplayLocked(activityDisplay);
+ activityContainer.attachToDisplayLocked(activityDisplay, onTop);
return activityContainer.mStack;
}
int getNextStackId() {
while (true) {
- if (++mLastStackId <= HOME_STACK_ID) {
- mLastStackId = HOME_STACK_ID + 1;
- }
- if (getStack(mLastStackId) == null) {
+ if (mNextFreeStackId >= FIRST_DYNAMIC_STACK_ID
+ && getStack(mNextFreeStackId) == null) {
break;
}
+ mNextFreeStackId++;
}
- return mLastStackId;
+ return mNextFreeStackId;
}
private boolean restoreRecentTaskLocked(TaskRecord task) {
- ActivityStack stack = null;
- // Determine stack to restore task to.
- if (mLeanbackOnlyDevice) {
- // There is only one stack for lean back devices.
- stack = mHomeStack;
- } else {
- // Look for the top stack on the home display that isn't the home stack.
- final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
- for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack tmpStack = homeDisplayStacks.get(stackNdx);
- if (!tmpStack.isHomeStack()) {
- stack = tmpStack;
- break;
- }
- }
- }
-
- if (stack == null) {
- // We couldn't find a stack to restore the task to. Possible if are restoring recents
- // before an application stack is created...Go ahead and create one on the default
- // display.
- stack = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY);
- // Restore home stack to top.
- moveHomeStack(true, "restoreRecentTask");
- if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
- "Created stack=" + stack + " for recents restoration.");
- }
+ // TODO (multi-window): Change to select task id based on if the task should on in
+ // fullscreen, freefrom, or sid-by-side stack.
+ // Always put task for lean back device in home stack since they only have one stack,
+ // else use the preferred stack ID to get the stack we should use if it already exists.
+ ActivityStack stack = mLeanbackOnlyDevice ? mHomeStack :
+ getStack(FULLSCREEN_WORKSPACE_STACK_ID,
+ true /*createStaticStackIfNeeded*/, false /*createOnTop*/);
if (stack == null) {
// What does this mean??? Not sure how we would get here...
@@ -3012,11 +3007,8 @@
Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
return;
}
- final ActivityStack stack = getStack(stackId);
- if (stack == null) {
- Slog.w(TAG, "moveTaskToStack: no stack for id=" + stackId);
- return;
- }
+ ActivityStack stack =
+ getStack(stackId, true /*createStaticStackIfNeeded*/, toTop /*createOnTop*/);
mWindowManager.moveTaskToStack(taskId, stackId, toTop);
if (task.stack != null) {
task.stack.removeTask(task, "moveTaskToStack", false /* notMoving */);
@@ -3028,6 +3020,26 @@
resumeTopActivitiesLocked();
}
+ void positionTaskInStackLocked(int taskId, int stackId, int position) {
+ final TaskRecord task = anyTaskForIdLocked(taskId);
+ if (task == null) {
+ Slog.w(TAG, "positionTaskInStackLocked: no task for id=" + taskId);
+ return;
+ }
+ ActivityStack stack =
+ getStack(stackId, true /*createStaticStackIfNeeded*/, false /*createOnTop*/);
+ mWindowManager.positionTaskInStack(taskId, stackId, position);
+ final boolean stackChanged = task.stack != null && task.stack != stack;
+ if (stackChanged) {
+ task.stack.removeTask(task, "moveTaskToStack", false /* notMoving */);
+ }
+ stack.positionTask(task, position, stackChanged);
+ // The task might have already been running and its visibility needs to be synchronized with
+ // the visibility of the stack / windows.
+ stack.ensureActivitiesVisibleLocked(null, 0);
+ resumeTopActivitiesLocked();
+ }
+
ActivityRecord findTaskLocked(ActivityRecord r) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -4204,15 +4216,15 @@
}
}
- void attachToDisplayLocked(ActivityDisplay activityDisplay) {
+ void attachToDisplayLocked(ActivityDisplay activityDisplay, boolean onTop) {
if (DEBUG_STACK) Slog.d(TAG_STACK, "attachToDisplayLocked: " + this
- + " to display=" + activityDisplay);
+ + " to display=" + activityDisplay + " onTop=" + onTop);
mActivityDisplay = activityDisplay;
mStack.mDisplayId = activityDisplay.mDisplayId;
mStack.mStacks = activityDisplay.mStacks;
- activityDisplay.attachActivities(mStack);
- mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId);
+ activityDisplay.attachActivities(mStack, onTop);
+ mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId, onTop);
}
@Override
@@ -4222,7 +4234,7 @@
if (activityDisplay == null) {
return;
}
- attachToDisplayLocked(activityDisplay);
+ attachToDisplayLocked(activityDisplay, true);
}
}
@@ -4435,7 +4447,7 @@
new VirtualActivityDisplay(width, height, density);
mActivityDisplay = virtualActivityDisplay;
mActivityDisplays.put(virtualActivityDisplay.mDisplayId, virtualActivityDisplay);
- attachToDisplayLocked(virtualActivityDisplay);
+ attachToDisplayLocked(virtualActivityDisplay, true);
}
if (mSurface != null) {
@@ -4521,10 +4533,15 @@
mDisplay.getDisplayInfo(mDisplayInfo);
}
- void attachActivities(ActivityStack stack) {
+ void attachActivities(ActivityStack stack, boolean onTop) {
if (DEBUG_STACK) Slog.v(TAG_STACK,
- "attachActivities: attaching " + stack + " to displayId=" + mDisplayId);
- mStacks.add(stack);
+ "attachActivities: attaching " + stack + " to displayId=" + mDisplayId
+ + " onTop=" + onTop);
+ if (onTop) {
+ mStacks.add(stack);
+ } else {
+ mStacks.add(0, stack);
+ }
}
void detachActivitiesLocked(ActivityStack stack) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d9786c8..fcf743e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,7 +16,8 @@
package com.android.server.wm;
-import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import static android.app.ActivityManager.HOME_STACK_ID;
+
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerService.TAG;
@@ -190,15 +191,19 @@
out.set(left, top, left + width, top + height);
}
- /** Refer to {@link WindowManagerService#attachStack(int, int)} */
- void attachStack(TaskStack stack) {
+ /** Refer to {@link WindowManagerService#attachStack(int, int, boolean)} */
+ void attachStack(TaskStack stack, boolean onTop) {
if (stack.mStackId == HOME_STACK_ID) {
if (mHomeStack != null) {
throw new IllegalArgumentException("attachStack: HOME_STACK_ID (0) not first.");
}
mHomeStack = stack;
}
- mStacks.add(stack);
+ if (onTop) {
+ mStacks.add(stack);
+ } else {
+ mStacks.add(0, stack);
+ }
layoutNeeded = true;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 09d70d8..79527b7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -131,6 +131,16 @@
stack.addTask(this, toTop);
}
+ void positionTaskInStack(TaskStack stack, int position) {
+ if (mStack != null && stack != mStack) {
+ if (DEBUG_STACK) Slog.i(TAG, "positionTaskInStack: removing taskId=" + mTaskId
+ + " from stack=" + mStack);
+ EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask");
+ mStack.removeTask(this);
+ }
+ stack.positionTask(this, position, showForAllUsers());
+ }
+
boolean removeAppToken(AppWindowToken wtoken) {
boolean removed = mAppTokens.remove(wtoken);
if (mAppTokens.size() == 0) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 90a173a..aef99bc 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -183,34 +183,75 @@
* @param showForAllUsers Whether to show the task regardless of the current user.
*/
void addTask(Task task, boolean toTop, boolean showForAllUsers) {
- int stackNdx;
- if (!toTop) {
- stackNdx = 0;
+ positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers);
+ }
+
+ void positionTask(Task task, int position, boolean showForAllUsers) {
+ final boolean canShowTask =
+ showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
+ mTasks.remove(task);
+ int stackSize = mTasks.size();
+ int minPosition = 0;
+ int maxPosition = stackSize;
+
+ if (canShowTask) {
+ minPosition = computeMinPosition(minPosition, stackSize);
} else {
- stackNdx = mTasks.size();
- if (!showForAllUsers && !mService.isCurrentProfileLocked(task.mUserId)) {
- // Place the task below all current user tasks.
- while (--stackNdx >= 0) {
- final Task tmpTask = mTasks.get(stackNdx);
- if (!tmpTask.showForAllUsers()
- || !mService.isCurrentProfileLocked(tmpTask.mUserId)) {
- break;
- }
- }
- // Put it above first non-current user task.
- ++stackNdx;
- }
+ maxPosition = computeMaxPosition(maxPosition);
}
- if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
- + " pos=" + stackNdx);
- mTasks.add(stackNdx, task);
+ // Reset position based on minimum/maximum possible positions.
+ position = Math.min(Math.max(position, minPosition), maxPosition);
+
+ if (DEBUG_TASK_MOVEMENT) Slog.d(TAG,
+ "positionTask: task=" + task + " position=" + position);
+ mTasks.add(position, task);
task.mStack = this;
task.updateDisplayInfo(mDisplayContent);
+ boolean toTop = position == mTasks.size() - 1;
if (toTop) {
mDisplayContent.moveStack(this, true);
}
- EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, stackNdx);
+ EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position);
+ }
+
+ /** Calculate the minimum possible position for a task that can be shown to the user.
+ * The minimum position will be above all other tasks that can't be shown.
+ * @param minPosition The minimum position the caller is suggesting.
+ * We will start adjusting up from here.
+ * @param size The size of the current task list.
+ */
+ private int computeMinPosition(int minPosition, int size) {
+ while (minPosition < size) {
+ final Task tmpTask = mTasks.get(minPosition);
+ final boolean canShowTmpTask =
+ tmpTask.showForAllUsers()
+ || mService.isCurrentProfileLocked(tmpTask.mUserId);
+ if (canShowTmpTask) {
+ break;
+ }
+ minPosition++;
+ }
+ return minPosition;
+ }
+
+ /** Calculate the maximum possible position for a task that can't be shown to the user.
+ * The maximum position will be below all other tasks that can be shown.
+ * @param maxPosition The maximum position the caller is suggesting.
+ * We will start adjusting down from here.
+ */
+ private int computeMaxPosition(int maxPosition) {
+ while (maxPosition > 0) {
+ final Task tmpTask = mTasks.get(maxPosition - 1);
+ final boolean canShowTmpTask =
+ tmpTask.showForAllUsers()
+ || mService.isCurrentProfileLocked(tmpTask.mUserId);
+ if (!canShowTmpTask) {
+ break;
+ }
+ maxPosition--;
+ }
+ return maxPosition;
}
void moveTaskToTop(Task task) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 320edbe..68bc773 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5127,8 +5127,10 @@
* Create a new TaskStack and place it on a DisplayContent.
* @param stackId The unique identifier of the new stack.
* @param displayId The unique identifier of the DisplayContent.
+ * @param onTop If true the stack will be place at the top of the display,
+ * else at the bottom
*/
- public void attachStack(int stackId, int displayId) {
+ public void attachStack(int stackId, int displayId, boolean onTop) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mWindowMap) {
@@ -5141,7 +5143,7 @@
mStackIdToStack.put(stackId, stack);
}
stack.attachDisplayContent(displayContent);
- displayContent.attachStack(stack);
+ displayContent.attachStack(stack, onTop);
moveStackWindowsLocked(displayContent);
final WindowList windows = displayContent.getWindowList();
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
@@ -5264,6 +5266,29 @@
}
}
+ public void positionTaskInStack(int taskId, int stackId, int position) {
+ synchronized (mWindowMap) {
+ if (DEBUG_STACK) Slog.i(TAG, "positionTaskInStack: positioning taskId=" + taskId
+ + " in stackId=" + stackId + " at " + position);
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null) {
+ if (DEBUG_STACK) Slog.i(TAG,
+ "positionTaskInStack: could not find taskId=" + taskId);
+ return;
+ }
+ TaskStack stack = mStackIdToStack.get(stackId);
+ if (stack == null) {
+ if (DEBUG_STACK) Slog.i(TAG,
+ "positionTaskInStack: could not find stackId=" + stackId);
+ return;
+ }
+ task.positionTaskInStack(stack, position);
+ final DisplayContent displayContent = stack.getDisplayContent();
+ displayContent.layoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ }
+
/**
* Re-sizes the specified task and its containing windows.
* Returns a {@link Configuration} object that contains configurations settings