diff options
11 files changed, 336 insertions, 89 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index b1e47015be9d..3ed72e5528b8 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 @@ public class Am extends BaseCommand { " 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 @@ public class Am extends BaseCommand { " 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 @@ public class Am extends BaseCommand { 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 @@ public class Am extends BaseCommand { } } + 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 5974fcfe949a..b7a7d075afc7 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -404,6 +404,42 @@ public class ActivityManager { */ 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 facaee4b4ca5..abfe2156d837 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -752,6 +752,16 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM 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 @@ class ActivityManagerProxy implements IActivityManager 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 c3e55f1b566e..4944bfcfbc14 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -139,6 +139,7 @@ public interface IActivityManager extends IInterface { 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 @@ public interface IActivityManager extends IInterface { // 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 266e3c71c2f6..7b50999a90eb 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 @@ package com.android.server.am; 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.writeIntAttribute; 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 @@ public final class ActivityManagerService extends ActivityManagerNative "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 @@ public final class ActivityManagerService extends ActivityManagerNative } @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 431b4338552b..e6c8d4382ab5 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.ActivityManagerDebugConfig.*; 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { */ 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { } } + 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 e7f28c5cd3df..4169754d61e7 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - // 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 @@ public final class ActivityStackSupervisor implements DisplayListener { } 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 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - 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 @@ public final class ActivityStackSupervisor implements DisplayListener { if (activityDisplay == null) { return; } - attachToDisplayLocked(activityDisplay); + attachToDisplayLocked(activityDisplay, true); } } @@ -4435,7 +4447,7 @@ public final class ActivityStackSupervisor implements DisplayListener { new VirtualActivityDisplay(width, height, density); mActivityDisplay = virtualActivityDisplay; mActivityDisplays.put(virtualActivityDisplay.mDisplayId, virtualActivityDisplay); - attachToDisplayLocked(virtualActivityDisplay); + attachToDisplayLocked(virtualActivityDisplay, true); } if (mSurface != null) { @@ -4521,10 +4533,15 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 d9786c813c31..fcf743e634cf 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 @@ class DisplayContent { 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 09d70d88cdef..79527b785466 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 @@ class Task implements DimLayer.DimLayerUser { 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 90a173ab8ccc..aef99bc2d6b1 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 @@ public class TaskStack implements DimLayer.DimLayerUser { * @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 320edbe6bf8c..68bc77328ffc 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 @@ public class WindowManagerService extends IWindowManager.Stub * 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 class WindowManagerService extends IWindowManager.Stub } } + 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 |