diff options
20 files changed, 546 insertions, 680 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 3879c164a84c..1aa78311a989 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -25,6 +25,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.Nullable; +import android.app.ActivityTaskManager; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -129,6 +130,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, private int mDividerInsets; private final Display mDefaultDisplay; + private boolean mSupportSplitScreenMultiWindow; private int mDividerSize; private int mTouchElevation; @@ -282,6 +284,8 @@ public class DividerView extends FrameLayout implements OnTouchListener, final DisplayManager displayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY); + mSupportSplitScreenMultiWindow = + ActivityTaskManager.supportsSplitScreenMultiWindow(mContext); } @Override @@ -354,6 +358,11 @@ public class DividerView extends FrameLayout implements OnTouchListener, @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (!mSupportSplitScreenMultiWindow) { + super.onLayout(changed, left, top, right, bottom); + return; + } + if (mFirstLayout) { // Wait for first layout so that the ViewRootImpl surface has been created. initializeSurfaceState(); @@ -1085,6 +1094,13 @@ public class DividerView extends FrameLayout implements OnTouchListener, crop.offsetTo(-(otherTaskRect.left - otherRect.left), -(otherTaskRect.top - otherRect.top)); t.setWindowCrop(mTiles.mSecondarySurface, crop); + // Reposition home and recents surfaces or they would be positioned relatively to its + // parent (split-screen secondary task) position. + for (int i = mTiles.mHomeAndRecentsSurfaces.size() - 1; i >= 0; --i) { + t.setPosition(mTiles.mHomeAndRecentsSurfaces.get(i), + mTiles.mHomeBounds.left - otherTaskRect.left, + mTiles.mHomeBounds.top - otherTaskRect.top); + } final SurfaceControl dividerCtrl = getWindowSurfaceControl(); if (dividerCtrl != null) { if (isHorizontalDivision()) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java index c4089e5dd070..6cb7f4ff7204 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java @@ -26,12 +26,15 @@ import static android.window.WindowOrganizer.TaskOrganizer; import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; +import android.graphics.Rect; import android.os.RemoteException; import android.util.Log; import android.view.Display; -import android.window.ITaskOrganizer; import android.view.SurfaceControl; import android.view.SurfaceSession; +import android.window.ITaskOrganizer; + +import java.util.ArrayList; class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub { private static final String TAG = "SplitScreenTaskOrganizer"; @@ -43,6 +46,8 @@ class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub { SurfaceControl mSecondarySurface; SurfaceControl mPrimaryDim; SurfaceControl mSecondaryDim; + ArrayList<SurfaceControl> mHomeAndRecentsSurfaces = new ArrayList<>(); + Rect mHomeBounds = new Rect(); final Divider mDivider; SplitScreenTaskOrganizer(Divider divider) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java index 8724e490a558..6ed7afe152df 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -29,9 +29,9 @@ import android.graphics.Rect; import android.os.RemoteException; import android.util.Log; import android.view.Display; +import android.view.WindowManagerGlobal; import android.window.IWindowContainer; import android.window.WindowContainerTransaction; -import android.view.WindowManagerGlobal; import android.window.WindowOrganizer; import com.android.internal.annotations.GuardedBy; @@ -157,6 +157,7 @@ public class WindowManagerProxy { for (int i = homeStacks.size() - 1; i >= 0; --i) { wct.setBounds(homeStacks.get(i), homeBounds); } + layout.mTiles.mHomeBounds.set(homeBounds); return isHomeResizable; } @@ -180,13 +181,17 @@ public class WindowManagerProxy { if (rootTasks.isEmpty()) { return false; } + tiles.mHomeAndRecentsSurfaces.clear(); for (int i = rootTasks.size() - 1; i >= 0; --i) { - if (rootTasks.get(i).configuration.windowConfiguration.getWindowingMode() + final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i); + if (isHomeOrRecentTask(rootTask)) { + tiles.mHomeAndRecentsSurfaces.add(rootTask.token.getLeash()); + } + if (rootTask.configuration.windowConfiguration.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { continue; } - wct.reparent(rootTasks.get(i).token, tiles.mSecondary.token, - true /* onTop */); + wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */); } boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct); WindowOrganizer.applyTransaction(wct); @@ -213,6 +218,7 @@ public class WindowManagerProxy { // Set launch root first so that any task created after getChildContainers and // before reparent (pretty unlikely) are put into fullscreen. TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null); + tiles.mHomeAndRecentsSurfaces.clear(); // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished // plus specific APIs to clean this up. List<ActivityManager.RunningTaskInfo> primaryChildren = diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index e8bfe8ef63ac..6308d2596074 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -303,9 +303,6 @@ class ActivityStack extends Task { private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1; - // TODO(task-hierarchy): remove when tiles can be actual parents - TaskTile mTile = null; - private final Handler mHandler; private class ActivityStackHandler extends Handler { @@ -551,10 +548,10 @@ class ActivityStack extends Task { } ActivityStack(ActivityTaskManagerService atmService, int id, int activityType, - ActivityInfo info, Intent intent) { + ActivityInfo info, Intent intent, boolean createdByOrganizer) { this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/, null /*taskDescription*/, null /*stack*/); - + mCreatedByOrganizer = createdByOrganizer; setActivityType(activityType); } @@ -601,20 +598,11 @@ class ActivityStack extends Task { } @Override - public void resolveTileOverrideConfiguration(Configuration newParentConfig) { - super.resolveTileOverrideConfiguration(newParentConfig); - if (mTile != null) { - // If this is a virtual child of a tile, simulate the parent-child relationship - mTile.updateResolvedConfig(getResolvedOverrideConfiguration()); - } - } - - @Override public void onConfigurationChanged(Configuration newParentConfig) { // Calling Task#onConfigurationChanged() for leaf task since the ops in this method are // particularly for ActivityStack, like preventing bounds changes when inheriting certain // windowing mode. - if (!isRootTask() || this instanceof TaskTile) { + if (!isRootTask()) { super.onConfigurationChanged(newParentConfig); return; } @@ -689,6 +677,9 @@ class ActivityStack extends Task { @Override public void setWindowingMode(int windowingMode) { + // Reset the cached result of toString() + stringName = null; + // Calling Task#setWindowingMode() for leaf task since this is the a specialization of // {@link #setWindowingMode(int)} for ActivityStack. if (!isRootTask()) { @@ -742,7 +733,6 @@ class ActivityStack extends Task { final int currentOverrideMode = getRequestedOverrideWindowingMode(); final DisplayContent display = getDisplay(); final Task topTask = getTopMostTask(); - final ActivityStack splitScreenStack = display.getRootSplitScreenPrimaryTask(); int windowingMode = preferredWindowingMode; if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED && isTransientWindowingMode(currentMode)) { @@ -756,14 +746,14 @@ class ActivityStack extends Task { windowingMode = display.validateWindowingMode(windowingMode, null /* ActivityRecord */, topTask, getActivityType()); } - if (splitScreenStack == this + if (display.getRootSplitScreenPrimaryTask() == this && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { // Resolution to split-screen secondary for the primary split-screen stack means // we want to leave split-screen mode. windowingMode = mRestoreOverrideWindowingMode; } - final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryTask(); + final boolean alreadyInSplitScreenMode = display.isSplitScreenModeActivated(); // Don't send non-resizeable notifications if the windowing mode changed was a side effect // of us entering split-screen mode. @@ -831,7 +821,7 @@ class ActivityStack extends Task { return; } - if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && splitScreenStack != null) { + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && alreadyInSplitScreenMode) { // We already have a split-screen stack in this display, so just move the tasks over. // TODO: Figure-out how to do all the stuff in // AMS.setTaskWindowingModeSplitScreenPrimary @@ -1063,7 +1053,7 @@ class ActivityStack extends Task { final DisplayContent display = getDisplay(); if (inSplitScreenSecondaryWindowingMode()) { - // If the stack is in split-screen seconardy mode, we need to make sure we move the + // If the stack is in split-screen secondary mode, we need to make sure we move the // primary split-screen stack forward in the case it is currently behind a fullscreen // stack so both halves of the split-screen appear on-top and the fullscreen stack isn't // cutting between them. @@ -1085,12 +1075,13 @@ class ActivityStack extends Task { display.moveHomeStackToFront(reason + " returnToHome"); } - final boolean movingTask = task != null; - display.positionStackAtTop(this, !movingTask /* includingParents */, reason); - if (movingTask) { - // This also moves the entire hierarchy branch to top, including parents - positionChildAtTop(task); + if (isRootTask()) { + display.positionStackAtTop(this, false /* includingParents */, reason); } + if (task == null) { + task = this; + } + task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */); } /** @@ -1116,12 +1107,6 @@ class ActivityStack extends Task { } } - @Override - boolean isFocusable() { - // Special check for tile which isn't really in the hierarchy - return mTile != null ? mTile.isFocusable() : super.isFocusable(); - } - boolean isTopActivityFocusable() { final ActivityRecord r = topRunningActivity(); return r != null ? r.isFocusable() @@ -3542,6 +3527,10 @@ class ActivityStack extends Task { @Override void onChildPositionChanged(WindowContainer child) { + if (isOrganized()) { + mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */); + } + if (!mChildren.contains(child)) { return; } @@ -3572,11 +3561,6 @@ class ActivityStack extends Task { if (oldDisplay != null && oldDisplay.isRemoving()) { postReparent(); } - if (mTile != null && getSurfaceControl() != null) { - // by now, the TaskStack should already have been reparented, so we can reparent its - // surface here - reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl()); - } } void reparent(DisplayContent newParent, boolean onTop) { @@ -3614,16 +3598,7 @@ class ActivityStack extends Task { @Override void getRelativeDisplayedPosition(Point outPos) { - // check for tile which is "virtually" a parent. - if (mTile != null) { - final Rect dispBounds = getDisplayedBounds(); - outPos.set(dispBounds.left, dispBounds.top); - final Rect parentBounds = mTile.getBounds(); - outPos.offset(-parentBounds.left, -parentBounds.top); - } else { - super.getRelativeDisplayedPosition(outPos); - } - + super.getRelativeDisplayedPosition(outPos); final int outset = getStackOutset(); outPos.x -= outset; outPos.y -= outset; @@ -3633,16 +3608,6 @@ class ActivityStack extends Task { if (mSurfaceControl == null) { return; } - if (mTile != null) { - // Tile controls crop, so the app needs to be able to draw its background outside of - // the stack bounds for when the tile crop gets bigger than the stack. - if (mLastSurfaceSize.equals(0, 0)) { - return; - } - transaction.setWindowCrop(mSurfaceControl, null); - mLastSurfaceSize.set(0, 0); - return; - } final Rect stackBounds = getDisplayedBounds(); int width = stackBounds.width(); @@ -3666,9 +3631,6 @@ class ActivityStack extends Task { @Override void onDisplayChanged(DisplayContent dc) { - if (mTile != null && dc != mTile.getDisplay()) { - mTile.removeChild(this); - } super.onDisplayChanged(dc); if (isRootTask()) { updateSurfaceBounds(); @@ -3825,49 +3787,6 @@ class ActivityStack extends Task { return shouldSleepActivities() || mAtmService.mShuttingDown; } - TaskTile getTile() { - return mTile; - } - - /** - * Don't call this directly. instead use {@link TaskTile#addChild} or - * {@link TaskTile#removeChild}. - */ - void setTile(TaskTile tile) { - TaskTile origTile = mTile; - mTile = tile; - final ConfigurationContainer parent = getParent(); - if (parent != null) { - onConfigurationChanged(parent.getConfiguration()); - } - - // Reparent to tile surface or back to original parent - if (getSurfaceControl() == null) { - return; - } - if (mTile != null) { - // don't use reparentSurfaceControl because we need to bypass taskorg check - mSurfaceAnimator.reparent(getPendingTransaction(), mTile.getSurfaceControl()); - } else if (mTile == null && origTile != null) { - mSurfaceAnimator.reparent(getPendingTransaction(), getParentSurfaceControl()); - } - } - - @Override - public SurfaceControl getParentSurfaceControl() { - // Tile is a "virtual" parent, so we need to intercept the parent surface here - return mTile != null ? mTile.getSurfaceControl() : super.getParentSurfaceControl(); - } - - @Override - void removeImmediately() { - // TODO(task-hierarchy): remove this override when tiles are in hierarchy - if (mTile != null) { - mTile.removeChild(this); - } - super.removeImmediately(); - } - @Override public void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) { @@ -3912,7 +3831,7 @@ class ActivityStack extends Task { proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight()); } - proto.write(CREATED_BY_ORGANIZER, this instanceof TaskTile); + proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer); proto.end(token); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 57f357d384b5..4652f49f116c 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -423,6 +423,10 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final ActivityStack toStack = mToDisplay.getOrCreateStack( null, mTmpOptions, task, task.getActivityType(), mOnTop); + if (task == toStack) { + // The task was reused as the root task. + return; + } if (mOnTop) { final boolean isTopTask = task == mTopTask; @@ -1704,7 +1708,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mRootWindowContainer.getLaunchStack(null, aOptions, task, onTop); final WindowContainer parent = task.getParent(); - if (parent == stack) { + if (parent == stack || task == stack) { // Nothing else to do since it is already restored in the right stack. return true; } @@ -2237,7 +2241,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final boolean isSecondaryDisplayPreferred = (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY); final boolean inSplitScreenMode = actualStack != null - && actualStack.getDisplay().hasSplitScreenPrimaryTask(); + && actualStack.getDisplay().isSplitScreenModeActivated(); if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) { return; @@ -2284,16 +2288,14 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) { // Dismiss docked stack. If task appeared to be in docked stack but is not resizable - // we need to move it to top of fullscreen stack, otherwise it will be covered. - - final ActivityStack dockedStack = - task.getStack().getDisplay().getRootSplitScreenPrimaryTask(); - if (dockedStack != null) { + final DisplayContent display = task.getStack().getDisplay(); + if (display.isSplitScreenModeActivated()) { // Display a warning toast that we tried to put an app that doesn't support // split-screen in split-screen. mService.getTaskChangeNotificationController() .notifyActivityDismissingDockedStack(); - dockedStack.getDisplay().onSplitScreenModeDismissed(); - dockedStack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS, + display.onSplitScreenModeDismissed(); + display.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS, true /* notifyClients */); } return; @@ -2602,7 +2604,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { "startActivityFromRecents: Task " + taskId + " not found."); } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && task.getWindowingMode() != windowingMode) { - mService.moveTaskToSplitScreenPrimaryTile(task, true /* toTop */); + mService.moveTaskToSplitScreenPrimaryTask(task, true /* toTop */); } if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 14ca7cbae4b3..da1c045dba16 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -30,7 +30,6 @@ import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WaitResult.LAUNCH_STATE_COLD; import static android.app.WaitResult.LAUNCH_STATE_HOT; -import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -78,6 +77,7 @@ import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY; import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1566,7 +1566,7 @@ class ActivityStarter { } if (!mAvoidMoveToFront && mDoResume) { - mTargetStack.moveToFront("reuseOrNewTask"); + mTargetStack.getStack().moveToFront("reuseOrNewTask", targetTask); if (mOptions != null) { if (mPreferredWindowingMode != WINDOWING_MODE_UNDEFINED) { mTargetStack.setWindowingMode(mPreferredWindowingMode); @@ -2364,6 +2364,7 @@ class ActivityStarter { private void setTargetStackIfNeeded(ActivityRecord intentActivity) { mTargetStack = intentActivity.getRootTask(); mTargetStack.mLastPausedActivity = null; + Task intentTask = intentActivity.getTask(); // If the target task is not in the front, then we need to bring it to the front... // except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have // the same behavior as if a new instance was being started, which means not bringing it @@ -2374,7 +2375,7 @@ class ActivityStarter { final ActivityRecord curTop = (focusStack == null) ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); final Task topTask = curTop != null ? curTop.getTask() : null; - differentTopTask = topTask != intentActivity.getTask() + differentTopTask = topTask != intentTask || (focusStack != null && topTask != focusStack.getTopMostTask()); } else { // The existing task should always be different from those in other displays. @@ -2391,7 +2392,6 @@ class ActivityStarter { intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask()); } - final Task intentTask = intentActivity.getTask(); final ActivityStack launchStack = getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions); if (launchStack == null || launchStack == mTargetStack) { @@ -2400,6 +2400,14 @@ class ActivityStarter { // new intent has delivered. final boolean isSplitScreenTopStack = mTargetStack.isTopSplitScreenStack(); + // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels + // tasks hierarchies. + if (mTargetStack != intentTask + && mTargetStack != intentTask.getParent().asTask()) { + intentTask.getParent().positionChildAt(POSITION_TOP, intentTask, + false /* includingParents */); + intentTask = intentTask.getParent().asTask(); + } // We only want to move to the front, if we aren't going to launch on a // different stack. If we launch on a different stack, we will put the // task on top there. @@ -2420,8 +2428,8 @@ class ActivityStarter { // Need to update mTargetStack because if task was moved out of it, the original stack may // be destroyed. mTargetStack = intentActivity.getRootTask(); - mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), - WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack); + mSupervisor.handleNonResizableTaskIfNeeded(intentTask, WINDOWING_MODE_UNDEFINED, + DEFAULT_DISPLAY, mTargetStack); } private void resumeTargetStackIfNeeded() { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 214a6767f3b2..35492f4d145d 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -36,7 +36,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -122,6 +121,7 @@ import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_ import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import android.Manifest; import android.annotation.IntDef; @@ -144,7 +144,6 @@ import android.app.IApplicationThread; import android.app.IAssistDataReceiver; import android.app.INotificationManager; import android.app.IRequestFinishCallback; -import android.window.ITaskOrganizerController; import android.app.ITaskStackListener; import android.app.Notification; import android.app.NotificationManager; @@ -230,9 +229,9 @@ import android.util.proto.ProtoOutputStream; import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; +import android.view.WindowManager; import android.window.IWindowOrganizerController; import android.window.WindowContainerTransaction; -import android.view.WindowManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -2349,16 +2348,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final ActivityStack stack = task.getStack(); - // Convert some windowing-mode changes into root-task reparents for split-screen. - if (stack.getTile() != null) { - stack.getDisplay().onSplitScreenModeDismissed(); - } if (toTop) { stack.moveToFront("setTaskWindowingMode", task); } - stack.setWindowingMode(windowingMode); - stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS, - true /* notifyClients */); + // Convert some windowing-mode changes into root-task reparents for split-screen. + if (stack.inSplitScreenWindowingMode()) { + stack.getDisplay().onSplitScreenModeDismissed(); + + } else { + stack.setWindowingMode(windowingMode); + stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS, + true /* notifyClients */); + } return true; } finally { Binder.restoreCallingIdentity(ident); @@ -2755,24 +2756,22 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final int prevMode = task.getWindowingMode(); - moveTaskToSplitScreenPrimaryTile(task, toTop); + moveTaskToSplitScreenPrimaryTask(task, toTop); return prevMode != task.getWindowingMode(); } - void moveTaskToSplitScreenPrimaryTile(Task task, boolean toTop) { - ActivityStack stack = task.getStack(); - TaskTile tile = null; - for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) { - tile = stack.getDisplay().getStackAt(i).asTile(); - if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - break; - } + void moveTaskToSplitScreenPrimaryTask(Task task, boolean toTop) { + final DisplayContent display = task.getDisplayContent(); + final ActivityStack primarySplitTask = display.getRootSplitScreenPrimaryTask(); + if (primarySplitTask == null) { + throw new IllegalStateException("Can't enter split without associated organized task"); } - if (tile == null) { - throw new IllegalStateException("Can't enter split without associated tile"); + + if (toTop) { + display.positionStackAt(POSITION_TOP, primarySplitTask, false /* includingParents */); } WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop); + wct.reparent(task.getStack().mRemoteToken, primarySplitTask.mRemoteToken, toTop); mWindowOrganizerController.applyTransaction(wct); } @@ -3239,7 +3238,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final ActivityStack stack = r.getRootTask(); final Task task = stack.getDisplay().createStack(stack.getWindowingMode(), - stack.getActivityType(), !ON_TOP, ainfo, intent); + stack.getActivityType(), !ON_TOP, ainfo, intent, + false /* createdByOrganizer */); if (!mRecentTasks.addToBottom(task)) { // The app has too many tasks already and we can't add any more @@ -4278,19 +4278,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); - TaskTile primary = null; - TaskTile secondary = null; - for (int i = dc.getStackCount() - 1; i >= 0; --i) { - final TaskTile t = dc.getStackAt(i).asTile(); - if (t == null) { - continue; - } - if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - primary = t; - } else if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { - secondary = t; - } - } + final Task primary = dc.getRootSplitScreenPrimaryTask(); + final Task secondary = dc.getTask(t -> t.mCreatedByOrganizer && t.isRootTask() + && t.inSplitScreenSecondaryWindowingMode()); if (primary == null || secondary == null) { return; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 7df731b3efab..6365144f4f1c 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -200,7 +200,6 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.IDisplayWindowInsetsController; import android.view.ISystemGestureExclusionListener; -import android.window.ITaskOrganizer; import android.view.IWindow; import android.view.InputChannel; import android.view.InputDevice; @@ -217,6 +216,7 @@ import android.view.ViewRootImpl; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManagerPolicyConstants.PointerEventListener; +import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; @@ -456,6 +456,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final Configuration mTmpConfiguration = new Configuration(); + private ArrayList<Task> mTmpTasks = new ArrayList<>(); + /** Remove this display when animation on it has completed. */ private boolean mDeferredRemoval; @@ -654,8 +656,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final RootWindowContainer.FindTaskResult mTmpFindTaskResult = new RootWindowContainer.FindTaskResult(); - // When non-null, new stacks get put into this tile. - TaskTile mLaunchTile = null; + // When non-null, new tasks get put into this root task. + Task mLaunchRootTask = null; private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> { WindowStateAnimator winAnimator = w.mWinAnimator; @@ -2128,12 +2130,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } /** @return The primary split-screen task, and {@code null} otherwise. */ - ActivityStack getRootSplitScreenPrimaryTask() { + @Nullable ActivityStack getRootSplitScreenPrimaryTask() { return mTaskContainers.getRootSplitScreenPrimaryTask(); } - boolean hasSplitScreenPrimaryTask() { - return getRootSplitScreenPrimaryTask() != null; + boolean isSplitScreenModeActivated() { + Task task = getRootSplitScreenPrimaryTask(); + return task != null && task.hasChild(); } ActivityStack getRootPinnedTask() { @@ -2600,7 +2603,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } amendWindowTapExcludeRegion(mTouchExcludeRegion); // TODO(multi-display): Support docked stacks on secondary displays. - if (mDisplayId == DEFAULT_DISPLAY && getRootSplitScreenPrimaryTask() != null) { + if (mDisplayId == DEFAULT_DISPLAY && isSplitScreenModeActivated()) { mDividerControllerLocked.getTouchRegion(mTmpRect); mTmpRegion.set(mTmpRect); mTouchExcludeRegion.op(mTmpRegion, Op.UNION); @@ -2623,8 +2626,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // If the task is home stack and it is resizable and visible (top of its root task), we want // to exclude the docked stack from touch so we need the entire screen area and not just a // small portion which the home stack currently is resized to. - if (task.isActivityTypeHome() && task.isVisible() && task.getStack().getTile() != null - && task.isResizeable()) { + if (task.isActivityTypeHome() && task.isVisible() && task.isResizeable()) { mDisplayContent.getBounds(mTmpRect); } else { task.getDimBounds(mTmpRect); @@ -4328,15 +4330,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @VisibleForTesting ActivityStack getTopStack() { - // TODO(task-hierarchy): Just grab index -1 once tiles are in hierarchy. - for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) { - final ActivityStack child = mTaskContainers.getChildAt(i); - if (child instanceof TaskTile) { - continue; - } - return child; - } - return null; + final int count = mTaskContainers.getChildCount(); + return count > 0 ? mTaskContainers.getChildAt(count - 1) : null; } int getIndexOf(ActivityStack stack) { @@ -4375,10 +4370,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private void addStackReferenceIfNeeded(ActivityStack stack) { - // TODO(task-hierarchy): Remove when tiles are in hierarchy. - if (stack instanceof TaskTile) { - return; - } if (stack.isActivityTypeHome()) { if (mRootHomeTask != null) { if (!stack.isDescendantOf(mRootHomeTask)) { @@ -4390,27 +4381,26 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mRootHomeTask = stack; } } + + if (!stack.isRootTask()) { + return; + } final int windowingMode = stack.getWindowingMode(); if (windowingMode == WINDOWING_MODE_PINNED) { if (mRootPinnedTask != null) { - if (!stack.isDescendantOf(mRootPinnedTask)) { - throw new IllegalArgumentException( - "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask - + " already exist on display=" + this + " stack=" + stack); - } - } else { - mRootPinnedTask = stack; + throw new IllegalArgumentException( + "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask + + " already exist on display=" + this + " stack=" + stack); } + mRootPinnedTask = stack; } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { if (mRootSplitScreenPrimaryTask != null) { - if (!stack.isDescendantOf(mRootSplitScreenPrimaryTask)) { - throw new IllegalArgumentException("addStackReferenceIfNeeded:" - + " split-screen-primary" + " stack=" + mRootSplitScreenPrimaryTask - + " already exist on display=" + this + " stack=" + stack); - } - } else { - mRootSplitScreenPrimaryTask = stack; + throw new IllegalArgumentException( + "addStackReferenceIfNeeded: split screen primary stack=" + + mRootSplitScreenPrimaryTask + + " already exist on display=" + this + " stack=" + stack); } + mRootSplitScreenPrimaryTask = stack; } } @@ -4645,10 +4635,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Apps and their containers are not allowed to specify an orientation while using // root tasks...except for the home stack if it is not resizable and currently // visible (top of) its root task. - if (mRootHomeTask != null && mRootHomeTask.isVisible() - && mRootHomeTask.getTile() != null) { + if (mRootHomeTask != null && mRootHomeTask.isVisible()) { final Task topMost = mRootHomeTask.getTopMostTask(); - final boolean resizable = topMost == null && topMost.isResizeable(); + final boolean resizable = topMost != null && topMost.isResizeable(); if (!(resizable && mRootHomeTask.matchParentBounds())) { final int orientation = mRootHomeTask.getOrientation(); if (orientation != SCREEN_ORIENTATION_UNSET) { @@ -4792,17 +4781,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mSplitScreenDividerAnchor = null; } } - - @Override - void onChildPositionChanged(WindowContainer child) { - // TODO(task-hierarchy): Move functionality to TaskTile when it's a proper parent. - TaskTile tile = ((ActivityStack) child).getTile(); - if (tile == null) { - return; - } - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged( - tile, false /* force */); - } } private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> { @@ -4983,7 +4961,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private boolean skipImeWindowsDuringTraversal(DisplayContent dc) { // We skip IME windows so they're processed just above their target, except // in split-screen mode where we process the IME containers above the docked divider. - return dc.mInputMethodTarget != null && !dc.hasSplitScreenPrimaryTask(); + return dc.mInputMethodTarget != null && !dc.isSplitScreenModeActivated(); } /** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */ @@ -5657,6 +5635,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mAtmService.updateSleepIfNeededLocked(); } + void addStackReferenceIfNeeded(ActivityStack stack) { + mTaskContainers.addStackReferenceIfNeeded(stack); + } + + void removeStackReferenceIfNeeded(ActivityStack stack) { + mTaskContainers.removeStackReferenceIfNeeded(stack); + } + void onStackRemoved(ActivityStack stack) { if (ActivityTaskManagerDebugConfig.DEBUG_STACK) { Slog.v(TAG_STACK, "removeStack: detaching " + stack + " from displayId=" + mDisplayId); @@ -5703,6 +5689,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo "positionStackAt: Can only have one task on display=" + this); } + final boolean movingToTop = wasContained && position >= getStackCount() - 1; + // Reset mPreferredTopFocusableStack before positioning to top or {@link + // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top + // resumed activity. + if (movingToTop && stack.isFocusable()) { + mPreferredTopFocusableStack = null; + } + // Since positionChildAt() is called during the creation process of pinned stacks, // ActivityStack#getStack() can be null. positionStackAt(position, stack, includingParents); @@ -5712,7 +5706,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // we are looking for top focusable stack. The condition {@code wasContained} restricts the // preferred stack is set only when moving an existing stack to top instead of adding a new // stack that may be too early (e.g. in the middle of launching or reparenting). - if (wasContained && position >= getStackCount() - 1 && stack.isFocusableAndVisible()) { + if (movingToTop && stack.isFocusableAndVisible()) { mPreferredTopFocusableStack = stack; } else if (mPreferredTopFocusableStack == stack) { mPreferredTopFocusableStack = null; @@ -5755,18 +5749,54 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** * Returns an existing stack compatible with the windowing mode and activity type or creates one * if a compatible stack doesn't exist. + * @see #getOrCreateStack(int, int, boolean, Intent, Task, boolean) + */ + ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop) { + return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */, + null /* candidateTask */, false /* createdByOrganizer */); + } + + /** + * When two level tasks are required for given windowing mode and activity type, returns an + * existing compatible root task or creates a new one. + * For one level task, the candidate task would be reused to also be the root task or create + * a new root task if no candidate task. * @see #getStack(int, int) * @see #createStack(int, int, boolean) */ - ActivityStack getOrCreateStack(int windowingMode, int activityType, - boolean onTop) { + ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop, + Intent intent, Task candidateTask, boolean createdByOrganizer) { if (!alwaysCreateStack(windowingMode, activityType)) { ActivityStack stack = getStack(windowingMode, activityType); if (stack != null) { return stack; } + } else if (candidateTask != null) { + final ActivityStack stack = (ActivityStack) candidateTask; + final int position = onTop ? POSITION_TOP : POSITION_BOTTOM; + if (isSplitScreenModeActivated()) { + final Task splitRootSecondary = getTask(t -> t.mCreatedByOrganizer && t.isRootTask() + && t.inSplitScreenSecondaryWindowingMode()); + if (stack.getParent() == null) { + splitRootSecondary.addChild(stack, position); + } else if (stack.getParent() != splitRootSecondary) { + stack.reparent(splitRootSecondary, position); + } + } else if (stack.getDisplay() != this || !stack.isRootTask()) { + if (stack.getParent() == null) { + addStack(stack, position); + } else { + stack.reparent(this, onTop); + } + } + // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen. + if (candidateTask.getWindowingMode() != windowingMode) { + candidateTask.setWindowingMode(windowingMode); + } + return stack; } - return createStack(windowingMode, activityType, onTop); + return createStack(windowingMode, activityType, onTop, null /*info*/, intent, + createdByOrganizer); } /** @@ -5784,7 +5814,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // UNDEFINED windowing mode is a valid result and means that the new stack will inherit // it's display's windowing mode. windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType); - return getOrCreateStack(windowingMode, activityType, onTop); + return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */, + candidateTask, false /* createdByOrganizer */); } @VisibleForTesting @@ -5793,7 +5824,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } ActivityStack createStack(int windowingMode, int activityType, boolean onTop) { - return createStack(windowingMode, activityType, onTop, null /*info*/, null /*intent*/); + return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */, + false /* createdByOrganizer */); } /** @@ -5805,25 +5837,29 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will * be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}. * @param onTop If true the stack will be created at the top of the display, else at the bottom. + * @param info The started activity info. + * @param intent The intent that started this task. + * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false} + * otherwise. * @return The newly created stack. */ ActivityStack createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info, - Intent intent) { + Intent intent, boolean createdByOrganizer) { if (mSingleTaskInstance && getStackCount() > 0) { // Create stack on default display instead since this display can only contain 1 stack. // TODO: Kinda a hack, but better that having the decision at each call point. Hoping // this goes away once ActivityView is no longer using virtual displays. return mRootWindowContainer.getDefaultDisplay().createStack( - windowingMode, activityType, onTop, info, intent); + windowingMode, activityType, onTop, info, intent, createdByOrganizer); } - if (activityType == ACTIVITY_TYPE_UNDEFINED) { + if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) { // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants - // anything else should be passing it in anyways... + // anything else should be passing it in anyways...except for the task organizer. activityType = ACTIVITY_TYPE_STANDARD; } - if (activityType != ACTIVITY_TYPE_STANDARD) { + if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) { // For now there can be only one stack of a particular non-standard activity type on a // display. So, get that ignoring whatever windowing mode it is currently in. ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); @@ -5842,39 +5878,39 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } final int stackId = getNextStackId(); - return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent); + return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent, + createdByOrganizer); } - /** @return the tile to create the next stack in. */ - private TaskTile updateLaunchTile(int windowingMode) { + /** @return the root task to create the next task in. */ + private Task updateLaunchRootTask(int windowingMode) { if (!isSplitScreenWindowingMode(windowingMode)) { - // Only split-screen windowing modes interact with tiles. + // Only split-screen windowing modes can do this currently... return null; } for (int i = getStackCount() - 1; i >= 0; --i) { - final TaskTile t = getStackAt(i).asTile(); - if (t == null || t.getRequestedOverrideWindowingMode() != windowingMode) { + final Task t = getStackAt(i); + if (!t.mCreatedByOrganizer || t.getRequestedOverrideWindowingMode() != windowingMode) { continue; } - // If not already set, pick a launch tile which is not the one we are launching - // into. - if (mLaunchTile == null) { + // If not already set, pick a launch root which is not the one we are launching into. + if (mLaunchRootTask == null) { for (int j = 0, n = getStackCount(); j < n; ++j) { - TaskTile tt = getStackAt(j).asTile(); - if (tt != t) { - mLaunchTile = tt; + final Task tt = getStackAt(j); + if (tt.mCreatedByOrganizer && tt != t) { + mLaunchRootTask = tt; break; } } } return t; } - return mLaunchTile; + return mLaunchRootTask; } @VisibleForTesting - ActivityStack createStackUnchecked(int windowingMode, int activityType, - int stackId, boolean onTop, ActivityInfo info, Intent intent) { + ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId, + boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) { if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) { throw new IllegalArgumentException("Stack with windowing mode cannot with non standard " + "activity type."); @@ -5884,19 +5920,25 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo info.applicationInfo = new ApplicationInfo(); } - TaskTile tile = updateLaunchTile(windowingMode); - if (tile != null) { - // Since this stack will be put into a tile, its windowingMode will be inherited. + // Task created by organizer are added as root. + Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode); + if (launchRootTask != null) { + // Since this stack will be put into a root task, its windowingMode will be inherited. windowingMode = WINDOWING_MODE_UNDEFINED; } + final ActivityStack stack = (ActivityStack) Task.create(mAtmService, stackId, activityType, - info, intent); - addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM); - stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, - false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */, - true /* creating */); - if (tile != null) { - tile.addChild(stack, 0 /* index */); + info, intent, createdByOrganizer); + if (launchRootTask != null) { + launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM); + if (onTop) { + positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */); + } + } else { + addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM); + stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, + false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */, + true /* creating */); } return stack; } @@ -6031,7 +6073,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mTmpFindTaskResult.clear(); for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = getStackAt(stackNdx); - if (!r.hasCompatibleActivityType(stack)) { + if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) { if (DEBUG_TASKS) { Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack); } @@ -6103,7 +6145,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int activityType = activityTypes[j]; for (int i = getStackCount() - 1; i >= 0; --i) { final ActivityStack stack = getStackAt(i); - if (stack.getActivityType() == activityType) { + // Collect the root tasks that are currently being organized. + if (stack.isOrganized()) { + for (int k = stack.getChildCount() - 1; k >= 0; --k) { + final ActivityStack childStack = (ActivityStack) stack.getChildAt(k); + if (childStack.getActivityType() == activityType) { + stacks.add(childStack); + } + } + } else if (stack.getActivityType() == activityType) { stacks.add(stack); } } @@ -6117,13 +6167,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo void onSplitScreenModeDismissed() { mAtmService.deferWindowLayout(); try { - mLaunchTile = null; - for (int i = getStackCount() - 1; i >= 0; --i) { - final TaskTile t = getStackAt(i).asTile(); - if (t != null) { - t.removeAllChildren(); - } - } + mLaunchRootTask = null; + moveSplitScreenTasksToFullScreen(); } finally { final ActivityStack topFullscreenStack = getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN); @@ -6141,6 +6186,24 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + private void moveSplitScreenTasksToFullScreen() { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + mTmpTasks.clear(); + forAllTasks(task -> { + if (task.mCreatedByOrganizer && task.inSplitScreenWindowingMode() && task.hasChild()) { + mTmpTasks.add(task); + } + }); + + for (int i = mTmpTasks.size() - 1; i >= 0; i--) { + final Task root = mTmpTasks.get(i); + for (int j = 0; j < root.getChildCount(); j++) { + wct.reparent(root.getChildAt(j).mRemoteToken, null, true /* toTop */); + } + } + mAtmService.mWindowOrganizerController.applyTransaction(wct); + } + /** * Returns true if the {@param windowingMode} is supported based on other parameters passed in. * @param windowingMode The windowing mode we are checking support for. @@ -6253,7 +6316,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - final boolean inSplitScreenMode = hasSplitScreenPrimaryTask(); + final boolean inSplitScreenMode = isSplitScreenModeActivated(); if (!inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) { // Switch to the display's windowing mode if we are not in split-screen mode and we are @@ -6278,10 +6341,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } boolean isTopNotPinnedStack(ActivityStack stack) { - // TODO(task-hierarchy): Remove when tiles are in hierarchy. - if (stack instanceof TaskTile) { - return false; - } for (int i = getStackCount() - 1; i >= 0; --i) { final ActivityStack current = getStackAt(i); if (!current.inPinnedWindowingMode()) { @@ -6503,7 +6562,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // If default display is in split-window mode, set windowing mode of the stack // to split-screen secondary. Otherwise, set the windowing mode to undefined by // default to let stack inherited the windowing mode from the new display. - final int windowingMode = toDisplay.hasSplitScreenPrimaryTask() + final int windowingMode = toDisplay.isSplitScreenModeActivated() ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_UNDEFINED; stack.reparent(toDisplay, true /* onTop */); @@ -6607,27 +6666,35 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * already top-most. */ ActivityStack getStackAbove(ActivityStack stack) { - final int stackIndex = getIndexOf(stack) + 1; - return (stackIndex < getStackCount()) ? getStackAt(stackIndex) : null; + final WindowContainer wc = stack.getParent(); + final int index = wc.mChildren.indexOf(stack) + 1; + return (index < wc.mChildren.size()) ? (ActivityStack) wc.mChildren.get(index) : null; } /** * Adjusts the {@param stack} behind the last visible stack in the display if necessary. * Generally used in conjunction with {@link #moveStackBehindStack}. */ + // TODO(b/151575894): Remove special stack movement methods. void moveStackBehindBottomMostVisibleStack(ActivityStack stack) { if (stack.shouldBeVisible(null)) { // Skip if the stack is already visible return; } - // Move the stack to the bottom to not affect the following visibility checks - positionStackAtBottom(stack); + final boolean isRootTask = stack.isRootTask(); + if (isRootTask) { + // Move the stack to the bottom to not affect the following visibility checks + positionStackAtBottom(stack); + } else { + stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */); + } // Find the next position where the stack should be placed - final int numStacks = getStackCount(); + final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount(); for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) { - final ActivityStack s = getStackAt(stackNdx); + final ActivityStack s = isRootTask ? getStackAt(stackNdx) + : (ActivityStack) stack.getParent().getChildAt(stackNdx); if (s == stack) { continue; } @@ -6636,7 +6703,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo || winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; if (s.shouldBeVisible(null) && isValidWindowingMode) { // Move the provided stack to behind this stack - positionStackAt(stack, Math.max(0, stackNdx - 1)); + final int position = Math.max(0, stackNdx - 1); + if (isRootTask) { + positionStackAt(stack, position); + } else { + stack.getParent().positionChildAt(position, stack, false /*includingParents */); + } break; } } @@ -6652,15 +6724,25 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return; } + final WindowContainer parent = stack.getParent(); + if (parent == null || parent != behindStack.getParent()) { + return; + } + // Note that positionChildAt will first remove the given stack before inserting into the // list, so we need to adjust the insertion index to account for the removed index // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the // position internally - final int stackIndex = getIndexOf(stack); - final int behindStackIndex = getIndexOf(behindStack); + final int stackIndex = parent.mChildren.indexOf(stack); + final int behindStackIndex = parent.mChildren.indexOf(behindStack); final int insertIndex = stackIndex <= behindStackIndex ? behindStackIndex - 1 : behindStackIndex; - positionStackAt(stack, Math.max(0, insertIndex)); + final int position = Math.max(0, insertIndex); + if (stack.isRootTask()) { + positionStackAt(stack, position); + } else { + parent.positionChildAt(position, stack, false /* includingParents */); + } } void ensureActivitiesVisible(ActivityRecord starting, int configChanges, @@ -6697,19 +6779,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return getHomeActivityForUser(mRootWindowContainer.mCurrentUser); } - // TODO(task-hierarchy): Remove when tiles are in hierarchy. - void addTile(TaskTile tile) { - mTaskContainers.addChild(tile, POSITION_BOTTOM); - ITaskOrganizer organizer = mAtmService.mTaskOrganizerController.getTaskOrganizer( - tile.getWindowingMode()); - tile.setTaskOrganizer(organizer); - } - - // TODO(task-hierarchy): Remove when tiles are in hierarchy. - void removeTile(TaskTile tile) { - mTaskContainers.removeChild(tile); - } - @Nullable ActivityRecord getHomeActivityForUser(int userId) { final ActivityStack homeStack = getRootHomeTask(); diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 44034edaa4bf..6b39fd2a70e3 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -406,12 +406,11 @@ class KeyguardController { // show on top of the lock screen. In this can we want to dismiss the docked // stack since it will be complicated/risky to try to put the activity on top // of the lock screen in the right fullscreen configuration. - final ActivityStack stack = - mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask(); - if (stack == null) { + final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); + if (!display.isSplitScreenModeActivated()) { return; } - mRootWindowContainer.getDefaultDisplay().onSplitScreenModeDismissed(); + display.onSplitScreenModeDismissed(); } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index adafdec40d37..9089240fe9d1 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -388,11 +388,12 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // surfaces needs to be done immediately. mWindowManager.executeAppTransition(); - if (targetStack.getTile() != null) { + final Task rootTask = targetStack.getRootTask(); + if (rootTask.isOrganized()) { // Client state may have changed during the recents animation, so force // send task info so the client can synchronize its state. mService.mTaskOrganizerController.dispatchTaskInfoChanged( - targetStack.mTile, true /* force */); + rootTask, true /* force */); } } catch (Exception e) { Slog.e(TAG, "Failed to clean up recents activity", e); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ebf1bc988b91..d2ed48f36fab 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1970,8 +1970,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final int focusStackId = topFocusedStack != null ? topFocusedStack.getRootTaskId() : INVALID_TASK_ID; // We dismiss the docked stack whenever we switch users. - final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask(); - if (dockedStack != null) { + if (getDefaultDisplay().isSplitScreenModeActivated()) { getDefaultDisplay().onSplitScreenModeDismissed(); } // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will @@ -2110,20 +2109,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final ActivityStack stack; if (singleActivity) { stack = r.getRootTask(); + stack.setWindowingMode(WINDOWING_MODE_PINNED); } else { - // In the case of multiple activities, we will create a new stack for it and then - // move the PIP activity into the stack. - // We will then perform a windowing mode change for both scenarios. - stack = display.createStack( - r.getRootTask().getRequestedOverrideWindowingMode(), - r.getActivityType(), ON_TOP, r.info, r.intent); + // In the case of multiple activities, we will create a new task for it and then + // move the PIP activity into the task. + stack = display.createStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP, + r.info, r.intent, false /* createdByOrganizer */); + // There are multiple activities in the task and moving the top activity should // reveal/leave the other activities in their original task. r.reparent(stack, MAX_VALUE, "moveActivityToStack"); } - stack.setWindowingMode(WINDOWING_MODE_PINNED); - // Reset the state that indicates it can enter PiP while pausing after we've moved it // to the pinned stack r.supportsEnterPipOnTaskSwitch = false; @@ -2799,16 +2796,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (stack == null && r != null) { stack = r.getRootTask(); } + int windowingMode = launchParams != null ? launchParams.mWindowingMode + : WindowConfiguration.WINDOWING_MODE_UNDEFINED; if (stack != null) { display = stack.getDisplay(); if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) { - int windowingMode = launchParams != null ? launchParams.mWindowingMode - : WindowConfiguration.WINDOWING_MODE_UNDEFINED; if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { windowingMode = display.resolveWindowingMode(r, options, candidateTask, activityType); } - if (stack.isCompatible(windowingMode, activityType)) { + // Always allow organized tasks that created by organizer since the activity type + // of an organized task is decided by the activity type of its top child, which + // could be incompatible with the given windowing mode and activity type. + if (stack.isCompatible(windowingMode, activityType) || stack.mCreatedByOrganizer) { return stack; } if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY @@ -2826,6 +2826,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) { display = getDefaultDisplay(); + if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { + windowingMode = display.resolveWindowingMode(r, options, candidateTask, + activityType); + } } return display.getOrCreateStack(r, options, candidateTask, activityType, onTop); @@ -2916,10 +2920,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents(); case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant(); } - // TODO(task-hierarchy): Find another way to differentiate tile from normal stack once it is - // part of the hierarchy - if (stack instanceof TaskTile) { - // Don't launch directly into tiles. + if (stack.mCreatedByOrganizer) { + // Don't launch directly into task created by organizer...but why can't we? return false; } // There is a 1-to-1 relationship between stack and task when not in diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index cd4fb3dc7691..7dd38e1c74b5 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -114,7 +114,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.res.Configuration; -import android.graphics.Point; import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; @@ -489,6 +488,17 @@ class Task extends WindowContainer<WindowContainer> { PictureInPictureParams mPictureInPictureParams = new PictureInPictureParams.Builder().build(); /** + * This task was created by the task organizer which has the following implementations. + * <ul> + * <lis>The task won't be removed when it is empty. Removal has to be an explicit request + * from the task organizer.</li> + * <li>Unlike other non-root tasks, it's direct children are visible to the task + * organizer for ordering purposes.</li> + * </ul> + */ + boolean mCreatedByOrganizer; + + /** * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int, * ActivityInfo, Intent, TaskDescription)} instead. */ @@ -1353,7 +1363,7 @@ class Task extends WindowContainer<WindowContainer> { if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) { return applicationType; } - return getChildAt(0).getActivityType(); + return getTopChild().getActivityType(); } @Override @@ -1366,6 +1376,12 @@ class Task extends WindowContainer<WindowContainer> { ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addChild: %s at top.", this); + // A rootable task that is now being added to be the child of an organized task. Making + // sure the stack references is keep updated. + if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) { + mDisplayContent.addStackReferenceIfNeeded((ActivityStack) child); + } + // Make sure the list of display UID whitelists is updated // now that this record is in a new task. mRootWindowContainer.updateUIDsPresentOnDisplay(); @@ -1406,6 +1422,11 @@ class Task extends WindowContainer<WindowContainer> { @Override void removeChild(WindowContainer child) { + // A rootable child task that is now being removed from an organized task. Making sure + // the stack references is keep updated. + if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) { + mDisplayContent.removeStackReferenceIfNeeded((ActivityStack) child); + } removeChild(child, "removeChild"); } @@ -1453,8 +1474,9 @@ class Task extends WindowContainer<WindowContainer> { mStackSupervisor.removeTask(this, false /* killProcess */, !REMOVE_FROM_RECENTS, reason); } - } else if (!mReuseTask) { + } else if (!mReuseTask && !mCreatedByOrganizer) { // Remove entire task if it doesn't have any activity left and it isn't marked for reuse + // or created by task organizer. if (!isRootTask) { getStack().removeChild(this, reason); } @@ -1866,7 +1888,12 @@ class Task extends WindowContainer<WindowContainer> { final Task parentTask = getParent().asTask(); if (parentTask != null) { parentTask.onActivityStateChanged(record, state, reason); - return; + // We still want to update the resumed activity if the parent task is created by + // organizer in order to keep the information synced once got reparented out from the + // organized task. + if (!parentTask.mCreatedByOrganizer) { + return; + } } if (record == mResumedActivity && state != RESUMED) { @@ -2300,18 +2327,30 @@ class Task extends WindowContainer<WindowContainer> { return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize); } - void resolveTileOverrideConfiguration(Configuration newParentConfig) { + private void resolveOrganizedOverrideConfiguration(Configuration newParentConfig) { super.resolveOverrideConfiguration(newParentConfig); + if (!isOrganized()) { + return; + } + + final Task root = getRootTask(); + if (root == this) { + return; + } + + // Ensure to have the same windowing mode for the child tasks that controlled by task org. + getResolvedOverrideConfiguration().windowConfiguration + .setWindowingMode(root.getWindowingMode()); } @Override void resolveOverrideConfiguration(Configuration newParentConfig) { - if (!isLeafTask()) { - resolveTileOverrideConfiguration(newParentConfig); + if (!isLeafTask() || mCreatedByOrganizer) { + resolveOrganizedOverrideConfiguration(newParentConfig); return; } mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds()); - resolveTileOverrideConfiguration(newParentConfig); + resolveOrganizedOverrideConfiguration(newParentConfig); int windowingMode = getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode(); if (windowingMode == WINDOWING_MODE_UNDEFINED) { @@ -2400,7 +2439,9 @@ class Task extends WindowContainer<WindowContainer> { } Rect updateOverrideConfigurationFromLaunchBounds() { - final Rect bounds = getLaunchBounds(); + // If the task is controlled by another organized task, do not set override + // configurations and let its parent (organized task) to control it; + final Rect bounds = isOrganized() && !isRootTask() ? null : getLaunchBounds(); setBounds(bounds); if (bounds != null && !bounds.isEmpty()) { // TODO: Review if we actually want to do this - we are setting the launch bounds @@ -2584,7 +2625,7 @@ class Task extends WindowContainer<WindowContainer> { // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid. if (suggestedPosition == POSITION_BOTTOM && minPosition == 0) { return POSITION_BOTTOM; - } else if (suggestedPosition == POSITION_TOP && maxPosition == (size - 1)) { + } else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) { return POSITION_TOP; } // Reset position based on minimum/maximum possible positions. @@ -3368,14 +3409,12 @@ class Task extends WindowContainer<WindowContainer> { info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode(); info.configuration.setTo(getConfiguration()); info.token = mRemoteToken; - // Get's the first non-undefined activity type among this and children. Can't use - // configuration.windowConfiguration because that would only be this level. - info.topActivityType = getActivityType(); //TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child // order changes. final Task top = getTopMostTask(); info.resizeMode = top != null ? top.mResizeMode : mResizeMode; + info.topActivityType = top.getActivityType(); if (mPictureInPictureParams.empty()) { info.pictureInPictureParams = null; @@ -3406,10 +3445,6 @@ class Task extends WindowContainer<WindowContainer> { return this; } - TaskTile asTile() { - return null; - } - // TODO(task-merge): Figure-out how this should work with hierarchy tasks. boolean shouldBeVisible(ActivityRecord starting) { return true; @@ -3705,8 +3740,9 @@ class Task extends WindowContainer<WindowContainer> { } static Task create(ActivityTaskManagerService service, int taskId, int activityType, - ActivityInfo info, Intent intent) { - return getTaskFactory().create(service, taskId, activityType, info, intent); + ActivityInfo info, Intent intent, boolean createdByOrganizer) { + return getTaskFactory().create(service, taskId, activityType, info, intent, + createdByOrganizer); } static Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info, @@ -3728,8 +3764,9 @@ class Task extends WindowContainer<WindowContainer> { */ static class TaskFactory { Task create(ActivityTaskManagerService service, int taskId, int activityType, - ActivityInfo info, Intent intent) { - return new ActivityStack(service, taskId, activityType, info, intent); + ActivityInfo info, Intent intent, boolean createdByOrganizer) { + return new ActivityStack(service, taskId, activityType, info, intent, + createdByOrganizer); } Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info, @@ -4005,21 +4042,29 @@ class Task extends WindowContainer<WindowContainer> { @Override boolean isOrganized() { final Task rootTask = getRootTask(); - // if the rootTask is a "child" of a tile, then don't consider it a root task. - // TODO: remove this along with removing tile. - if (((ActivityStack) rootTask).getTile() != null) { + if (rootTask.mTaskOrganizer == null) { + // You are obviously not organized... return false; } - return rootTask == this && rootTask.mTaskOrganizer != null; + if (rootTask == this) { + // Root tasks can be organized. + return true; + } + if (rootTask.mCreatedByOrganizer && getParent() == rootTask) { + // Direct children of tasks added by the organizer can the organized. + return true; + } + + return false; } @Override protected void reparentSurfaceControl(SurfaceControl.Transaction t, SurfaceControl newParent) { /** - * Avoid yanking back control from the TaskOrganizer, which has presumably reparented the - * Surface in to its own hierarchy. + * Avoid reparenting SurfaceControl of the organized tasks that are always on top, since + * the surfaces should be controlled by the organizer itself, like bubbles. */ - if (isOrganized()) { + if (isOrganized() && isAlwaysOnTop()) { return; } super.reparentSurfaceControl(t, newParent); @@ -4054,6 +4099,9 @@ class Task extends WindowContainer<WindowContainer> { mTaskOrganizer = null; mLastTaskOrganizerWindowingMode = -1; onTaskOrganizerChanged(); + if (mCreatedByOrganizer) { + removeImmediately(); + } } /** diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 552367801678..05b721b8ee81 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; @@ -27,13 +28,14 @@ import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDO import android.annotation.Nullable; import android.app.ActivityManager.RunningTaskInfo; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; -import android.window.ITaskOrganizerController; import android.window.ITaskOrganizer; +import android.window.ITaskOrganizerController; import android.window.IWindowContainer; import com.android.internal.util.ArrayUtils; @@ -255,11 +257,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (display == null) { return null; } - final int nextId = display.getNextStackId(); - TaskTile tile = new TaskTile(mService, nextId, windowingMode); - display.addTile(tile); - RunningTaskInfo out = tile.getTaskInfo(); - mLastSentTaskInfos.put(tile, out); + + final Task task = display.getOrCreateStack(windowingMode, ACTIVITY_TYPE_UNDEFINED, + false /* onTop */, new Intent(), null /* candidateTask */, + true /* createdByOrganizer */); + RunningTaskInfo out = task.getTaskInfo(); + mLastSentTaskInfos.put(task, out); return out; } } finally { @@ -273,11 +276,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - TaskTile tile = TaskTile.forToken(token.asBinder()); - if (tile == null) { - return false; + final Task task = WindowContainer.fromBinder(token.asBinder()).asTask(); + if (task == null) return false; + if (!task.mCreatedByOrganizer) { + throw new IllegalArgumentException( + "Attempt to delete task not created by organizer task=" + task); } - tile.removeImmediately(); + task.removeImmediately(); return true; } } finally { @@ -358,12 +363,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (task == null) { return null; } - ActivityStack rootTask = (ActivityStack) task.getRootTask(); - final TaskTile tile = rootTask.getTile(); - if (tile != null) { - rootTask = tile; - } - return rootTask.mRemoteToken; + return task.getRootTask().mRemoteToken; } } finally { Binder.restoreCallingIdentity(origId); @@ -371,7 +371,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override - public void setLaunchRoot(int displayId, @Nullable IWindowContainer tile) { + public void setLaunchRoot(int displayId, @Nullable IWindowContainer token) { enforceStackPermission("setLaunchRoot()"); final long origId = Binder.clearCallingIdentity(); try { @@ -380,16 +380,21 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (display == null) { return; } - TaskTile taskTile = tile == null ? null : TaskTile.forToken(tile.asBinder()); - if (taskTile == null) { - display.mLaunchTile = null; + Task task = token == null + ? null : WindowContainer.fromBinder(token.asBinder()).asTask(); + if (task == null) { + display.mLaunchRootTask = null; return; } - if (taskTile.getDisplay() != display) { + if (!task.mCreatedByOrganizer) { + throw new IllegalArgumentException("Attempt to set task not created by " + + "organizer as launch root task=" + task); + } + if (task.getDisplayContent() != display) { throw new RuntimeException("Can't set launch root for display " + displayId - + " to task on display " + taskTile.getDisplay().getDisplayId()); + + " to task on display " + task.getDisplayContent().getDisplayId()); } - display.mLaunchTile = taskTile; + display.mLaunchRootTask = task; } } finally { Binder.restoreCallingIdentity(origId); @@ -411,25 +416,25 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { Slog.e(TAG, "Can't get children of " + parent + " because it is not valid."); return null; } - // For now, only support returning children of persistent root tasks (of which the - // only current implementation is TaskTile). - if (!(container instanceof TaskTile)) { + final Task task = container.asTask(); + if (task == null) { + Slog.e(TAG, container + " is not a task..."); + return null; + } + // For now, only support returning children of tasks created by the organizer. + if (!task.mCreatedByOrganizer) { Slog.w(TAG, "Can only get children of root tasks created via createRootTask"); return null; } ArrayList<RunningTaskInfo> out = new ArrayList<>(); - // Tiles aren't real parents, so we need to go through stacks on the display to - // ensure correct ordering. - final DisplayContent dc = container.getDisplayContent(); - for (int i = dc.getStackCount() - 1; i >= 0; --i) { - final ActivityStack as = dc.getStackAt(i); - if (as.getTile() == container) { - if (activityTypes != null - && !ArrayUtils.contains(activityTypes, as.getActivityType())) { - continue; - } - out.add(as.getTaskInfo()); + for (int i = task.getChildCount() - 1; i >= 0; --i) { + final Task child = task.getChildAt(i).asTask(); + if (child == null) continue; + if (activityTypes != null + && !ArrayUtils.contains(activityTypes, child.getActivityType())) { + continue; } + out.add(child.getTaskInfo()); } return out; } @@ -451,12 +456,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } ArrayList<RunningTaskInfo> out = new ArrayList<>(); for (int i = dc.getStackCount() - 1; i >= 0; --i) { - final ActivityStack task = dc.getStackAt(i); - if (task.getTile() != null) { - // a tile is supposed to look like a parent, so don't include their - // "children" here. They can be accessed via getChildTasks() - continue; - } + final Task task = dc.getStackAt(i); if (activityTypes != null && !ArrayUtils.contains(activityTypes, task.getActivityType())) { continue; diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java deleted file mode 100644 index 51142b1d2eb1..000000000000 --- a/services/core/java/com/android/server/wm/TaskTile.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; -import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; - -import android.app.ActivityManager; -import android.app.TaskInfo; -import android.app.WindowConfiguration; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.IBinder; -import android.util.Slog; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.function.Consumer; - -/** - * A Tile. Right now this acts as a proxy for manipulating non-child stacks. Eventually, this - * can become an actual parent. - */ -// TODO(task-hierarchy): Remove when tasks can nest >2 or when single tasks can handle their -// own lifecycles. -public class TaskTile extends ActivityStack { - private static final String TAG = "TaskTile"; - final ArrayList<WindowContainer> mChildren = new ArrayList<>(); - - private static ActivityInfo createEmptyActivityInfo() { - ActivityInfo info = new ActivityInfo(); - info.applicationInfo = new ApplicationInfo(); - return info; - } - - TaskTile(ActivityTaskManagerService atmService, int id, int windowingMode) { - super(atmService, id, new Intent() /*intent*/, null /*affinityIntent*/, null /*affinity*/, - null /*rootAffinity*/, null /*realActivity*/, null /*origActivity*/, - false /*rootWasReset*/, false /*autoRemoveRecents*/, false /*askedCompatMode*/, - 0 /*userId*/, 0 /*effectiveUid*/, null /*lastDescription*/, - System.currentTimeMillis(), true /*neverRelinquishIdentity*/, - new ActivityManager.TaskDescription(), id, INVALID_TASK_ID, INVALID_TASK_ID, - 0 /*taskAffiliationColor*/, 0 /*callingUid*/, "" /*callingPackage*/, - null /*callingFeatureId*/, RESIZE_MODE_RESIZEABLE, - false /*supportsPictureInPicture*/, false /*_realActivitySuspended*/, - false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, - createEmptyActivityInfo(), null /*voiceSession*/, null /*voiceInteractor*/, - null /*stack*/); - getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode); - } - - @Override - void onDisplayChanged(DisplayContent dc) { - mDisplayContent = null; - if (dc != null) { - dc.getPendingTransaction().merge(getPendingTransaction()); - } - mDisplayContent = dc; - // Virtual parent, so don't notify children. - } - - @Override - TaskTile asTile() { - return this; - } - - @Override - protected void addChild(WindowContainer child, Comparator<WindowContainer> comparator) { - throw new RuntimeException("Improper use of addChild() on Tile"); - } - - @Override - void addChild(WindowContainer child, int index) { - mChildren.add(child); - if (child instanceof ActivityStack) { - ((ActivityStack) child).setTile(this); - } - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged( - this, false /* force */); - } - - @Override - void removeChild(WindowContainer child) { - if (child instanceof ActivityStack) { - ((ActivityStack) child).setTile(null); - } - mChildren.remove(child); - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged( - this, false /* force */); - } - - void removeAllChildren() { - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowContainer child = mChildren.get(i); - if (child instanceof ActivityStack) { - ((ActivityStack) child).setTile(null); - } - } - mChildren.clear(); - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged( - this, false /* force */); - } - - @Override - protected int getChildCount() { - // Currently 0 as this isn't a proper hierarchy member yet. - return 0; - } - - @Override - public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) { - Configuration c = new Configuration(getRequestedOverrideConfiguration()); - c.windowConfiguration.setWindowingMode(windowingMode); - onRequestedOverrideConfigurationChanged(c); - } - - @Override - public void onConfigurationChanged(Configuration newParentConfig) { - super.onConfigurationChanged(newParentConfig); - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowContainer child = mChildren.get(i); - child.onConfigurationChanged(child.getParent().getConfiguration()); - } - } - - void forAllTileActivities(Consumer<ActivityRecord> callback) { - for (int i = mChildren.size() - 1; i >= 0; --i) { - mChildren.get(i).forAllActivities(callback, true /* traverseTopToBottom */); - } - } - - /** - * Until this can be part of the hierarchy, the Stack level can use this utility during - * resolveOverrideConfig to simulate inheritance. - */ - void updateResolvedConfig(Configuration inOutResolvedConfig) { - Rect resolveBounds = inOutResolvedConfig.windowConfiguration.getBounds(); - if (resolveBounds.isEmpty()) { - resolveBounds.set(getRequestedOverrideBounds()); - } - int stackMode = inOutResolvedConfig.windowConfiguration.getWindowingMode(); - if (stackMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED - || stackMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) { - // Also replace FULLSCREEN because we interpret FULLSCREEN as "fill parent" - inOutResolvedConfig.windowConfiguration.setWindowingMode( - getRequestedOverrideWindowingMode()); - } - if (inOutResolvedConfig.smallestScreenWidthDp - == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { - inOutResolvedConfig.smallestScreenWidthDp = - getRequestedOverrideConfiguration().smallestScreenWidthDp; - } - if (inOutResolvedConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { - inOutResolvedConfig.screenWidthDp = getRequestedOverrideConfiguration().screenWidthDp; - } - if (inOutResolvedConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - inOutResolvedConfig.screenHeightDp = getRequestedOverrideConfiguration().screenHeightDp; - } - Rect resolveAppBounds = inOutResolvedConfig.windowConfiguration.getAppBounds(); - if (resolveAppBounds == null || resolveAppBounds.isEmpty()) { - inOutResolvedConfig.windowConfiguration.setAppBounds( - getRequestedOverrideConfiguration().windowConfiguration.getAppBounds()); - } - } - - @Override - void fillTaskInfo(TaskInfo info) { - super.fillTaskInfo(info); - WindowContainer top = null; - // Check mChildren.isEmpty directly because hasChild() -> getChildCount() always returns 0 - if (!mChildren.isEmpty()) { - // Find the top-most root task which is a virtual child of this Tile. Because this is a - // virtual parent, the mChildren order here isn't changed during hierarchy operations. - WindowContainer parent = mChildren.get(0).getParent(); - for (int i = parent.getChildCount() - 1; i >= 0; --i) { - if (mChildren.contains(parent.getChildAt(i))) { - top = parent.getChildAt(i); - break; - } - } - } - final Task topTask = top == null ? null : top.getTopMostTask(); - boolean isResizable = topTask == null || topTask.isResizeable(); - info.resizeMode = isResizable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE; - info.topActivityType = top == null ? ACTIVITY_TYPE_UNDEFINED : top.getActivityType(); - } - - @Override - void removeImmediately() { - removeAllChildren(); - super.removeImmediately(); - } - - @Override - void taskOrganizerDied() { - super.taskOrganizerDied(); - removeImmediately(); - } - - static TaskTile forToken(IBinder token) { - try { - return (TaskTile) ((RemoteToken) token).getContainer(); - } catch (ClassCastException e) { - Slog.w(TAG, "Bad tile token: " + token, e); - return null; - } - } -} diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index e416e8073a75..5f21e1799958 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -159,17 +159,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub false /* preserveWindow */); try { for (int i = haveConfigChanges.size() - 1; i >= 0; --i) { - final WindowContainer wc = haveConfigChanges.valueAt(i); - final Task task = wc.asTask(); - final TaskTile tile = task != null ? task.asTile() : null; - if (tile != null) { - // Special case for tile. Can't override normal forAllActivities - // because it generates duplicate calls and messes up existing - // code-paths. - tile.forAllTileActivities(f); - } else { - wc.forAllActivities(f); - } + haveConfigChanges.valueAt(i).forAllActivities(f); } } finally { f.recycle(); @@ -223,51 +213,65 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub private int sanitizeAndApplyHierarchyOp(WindowContainer container, WindowContainerTransaction.HierarchyOp hop) { - if (!(container instanceof Task)) { + final Task task = container.asTask(); + if (task == null) { throw new IllegalArgumentException("Invalid container in hierarchy op"); } - if (container.getDisplayContent() == null) { - Slog.w(TAG, "Container is no longer attached: " + container); + final DisplayContent dc = task.getDisplayContent(); + if (dc == null) { + Slog.w(TAG, "Container is no longer attached: " + task); return 0; } + final ActivityStack as = (ActivityStack) task; + if (hop.isReparent()) { - // special case for tiles since they are "virtual" parents - if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) { - ActivityStack as = (ActivityStack) container; - TaskTile newParent = hop.getNewParent() == null ? null - : (TaskTile) WindowContainer.fromBinder(hop.getNewParent()); - if (as.getTile() != newParent) { - if (as.getTile() != null) { - as.getTile().removeChild(as); - } - if (newParent != null) { - if (!as.affectedBySplitScreenResize()) { - return 0; - } - newParent.addChild(as, POSITION_TOP); + final boolean isNonOrganizedRootableTask = + (task.isRootTask() && !task.mCreatedByOrganizer) + || task.getParent().asTask().mCreatedByOrganizer; + if (isNonOrganizedRootableTask) { + Task newParent = hop.getNewParent() == null ? null + : WindowContainer.fromBinder(hop.getNewParent()).asTask(); + if (task.getParent() != newParent) { + if (newParent == null) { + // Re-parent task to display as a root task. + dc.moveStackToDisplay(as, hop.getToTop()); + } else if (newParent.inMultiWindowMode() && !task.isResizeable() + && task.isLeafTask()) { + Slog.w(TAG, "Can't support task that doesn't support multi-window mode in" + + " multi-window mode... newParent=" + newParent + " task=" + task); + return 0; + } else { + // Clear the window crop on root task since it may not be updated after + // reparent (no longer be a root task) + task.getSurfaceControl().setWindowCrop(null); + task.reparent((ActivityStack) newParent, + hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, + false /*moveParents*/, "sanitizeAndApplyHierarchyOp"); } - } - if (hop.getToTop()) { - as.getDisplay().positionStackAtTop(as, false /* includingParents */); } else { - as.getDisplay().positionStackAtBottom(as); + final ActivityStack rootTask = + (ActivityStack) (newParent != null ? newParent : task.getRootTask()); + if (hop.getToTop()) { + as.getDisplay().positionStackAtTop(rootTask, false /* includingParents */); + } else { + as.getDisplay().positionStackAtBottom(rootTask); + } } - } else if (container instanceof Task) { + } else { throw new RuntimeException("Reparenting leaf Tasks is not supported now."); } } else { // Ugh, of course ActivityStack has its own special reorder logic... - if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) { - ActivityStack as = (ActivityStack) container; + if (task.isRootTask()) { if (hop.getToTop()) { - as.getDisplay().positionStackAtTop(as, false /* includingParents */); + dc.positionStackAtTop(as, false /* includingParents */); } else { - as.getDisplay().positionStackAtBottom(as); + dc.positionStackAtBottom(as); } } else { - container.getParent().positionChildAt( + task.getParent().positionChildAt( hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, - container, false /* includingParents */); + task, false /* includingParents */); } } return TRANSACT_EFFECTS_LIFECYCLE; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 161152ba0d74..0338cc3096ea 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -131,6 +131,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT; import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION; import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET; @@ -138,7 +139,6 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT; -import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT; import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; @@ -3338,7 +3338,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } final ActivityStack stack = task.getStack(); - if (stack == null) { + if (stack == null || stack.mCreatedByOrganizer) { return; } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 6ec0f919ced7..ed400ea8e992 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -82,9 +82,8 @@ import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.service.voice.IVoiceInteractionSession; import android.view.Gravity; -import android.window.IWindowContainer; -import android.view.SurfaceControl; import android.window.ITaskOrganizer; +import android.window.IWindowContainer; import androidx.test.filters.SmallTest; @@ -975,7 +974,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // Move activity to split-screen-primary stack and make sure it has the focus. TestSplitOrganizer splitOrg = new TestSplitOrganizer(mService, top.getDisplayId()); - splitOrg.mPrimary.addChild(top.getRootTask(), 0 /* index */); + top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM); top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent"); // Activity must landed on split-screen-secondary when launch adjacent. @@ -1001,8 +1000,8 @@ public class ActivityStarterTests extends ActivityTestsBase { static class TestSplitOrganizer extends ITaskOrganizer.Stub { final ActivityTaskManagerService mService; - TaskTile mPrimary; - TaskTile mSecondary; + Task mPrimary; + Task mSecondary; boolean mInSplit = false; int mDisplayId; TestSplitOrganizer(ActivityTaskManagerService service, int displayId) { @@ -1014,10 +1013,10 @@ public class ActivityStarterTests extends ActivityTestsBase { WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); IWindowContainer primary = mService.mTaskOrganizerController.createRootTask( displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token; - mPrimary = TaskTile.forToken(primary.asBinder()); + mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask(); IWindowContainer secondary = mService.mTaskOrganizerController.createRootTask( displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token; - mSecondary = TaskTile.forToken(secondary.asBinder()); + mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask(); } @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo info) { @@ -1042,7 +1041,7 @@ public class ActivityStarterTests extends ActivityTestsBase { for (int i = dc.getStackCount() - 1; i >= 0; --i) { if (!WindowConfiguration.isSplitScreenWindowingMode( dc.getStackAt(i).getWindowingMode())) { - mSecondary.addChild(dc.getStackAt(i), 0); + dc.getStackAt(i).reparent(mSecondary, POSITION_BOTTOM); } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 4634e2d71573..716369d49036 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -464,7 +464,7 @@ class ActivityTestsBase extends SystemServiceTestsBase { ActivityStack build() { final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId(); final ActivityStack stack = mDisplay.createStackUnchecked(mWindowingMode, - mActivityType, stackId, mOnTop, mInfo, mIntent); + mActivityType, stackId, mOnTop, mInfo, mIntent, false /* createdByOrganizer */); final ActivityStackSupervisor supervisor = mRootWindowContainer.mStackSupervisor; if (mCreateActivity) { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java index a96f40143e0f..ed635ce3f69e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -37,6 +37,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -347,45 +348,62 @@ public class TaskOrganizerTests extends WindowTestsBase { assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType); DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY); - List<TaskTile> infos = getTaskTiles(dc); + List<Task> infos = getTasksCreatedByOrganizer(dc); assertEquals(2, infos.size()); assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token)); - infos = getTaskTiles(dc); + infos = getTasksCreatedByOrganizer(dc); assertEquals(1, infos.size()); assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode()); } @Test public void testTileAddRemoveChild() { + ITaskOrganizer listener = new ITaskOrganizer.Stub() { + @Override + public void onTaskAppeared(RunningTaskInfo taskInfo) { } + + @Override + public void onTaskVanished(RunningTaskInfo container) { } + + @Override + public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException { + } + }; + mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); final ActivityStack stack = createTaskStackOnDisplay( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent); assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode()); - TaskTile tile1 = TaskTile.forToken(info1.token.asBinder()); - tile1.addChild(stack, 0 /* index */); + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertEquals(info1.configuration.windowConfiguration.getWindowingMode(), stack.getWindowingMode()); // Info should reflect new membership - List<TaskTile> tiles = getTaskTiles(mDisplayContent); - info1 = tiles.get(0).getTaskInfo(); + List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent); + info1 = infos.get(0).getTaskInfo(); assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType); // Children inherit configuration Rect newSize = new Rect(10, 10, 300, 300); - Configuration c = new Configuration(tile1.getRequestedOverrideConfiguration()); + Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); + Configuration c = new Configuration(task1.getRequestedOverrideConfiguration()); c.windowConfiguration.setBounds(newSize); doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any()); - tile1.onRequestedOverrideConfigurationChanged(c); + task1.onRequestedOverrideConfigurationChanged(c); assertEquals(newSize, stack.getBounds()); - tile1.removeChild(stack); + wct = new WindowContainerTransaction(); + wct.reparent(stack.mRemoteToken, null, true /* onTop */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode()); - tiles = getTaskTiles(mDisplayContent); - info1 = tiles.get(0).getTaskInfo(); + infos = getTasksCreatedByOrganizer(mDisplayContent); + info1 = infos.get(0).getTaskInfo(); assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType); } @@ -415,8 +433,10 @@ public class TaskOrganizerTests extends WindowTestsBase { final ActivityStack stack = createTaskStackOnDisplay( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent); - TaskTile tile1 = TaskTile.forToken(info1.token.asBinder()); - tile1.addChild(stack, 0 /* index */); + Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); @@ -424,19 +444,24 @@ public class TaskOrganizerTests extends WindowTestsBase { called[0] = false; final ActivityStack stack2 = createTaskStackOnDisplay( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent); - tile1.addChild(stack2, 0 /* index */); + wct = new WindowContainerTransaction(); + wct.reparent(stack2.mRemoteToken, info1.token, true /* onTop */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType); lastReportedTiles.clear(); called[0] = false; - mDisplayContent.positionStackAtTop(stack, false /* includingParents */); + task1.positionChildAt(POSITION_TOP, stack, false /* includingParents */); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); lastReportedTiles.clear(); called[0] = false; - tile1.removeAllChildren(); + wct = new WindowContainerTransaction(); + wct.reparent(stack.mRemoteToken, null, true /* onTop */); + wct.reparent(stack2.mRemoteToken, null, true /* onTop */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType); } @@ -457,9 +482,11 @@ public class TaskOrganizerTests extends WindowTestsBase { } }; mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer( + listener, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer( listener, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); @@ -522,13 +549,11 @@ public class TaskOrganizerTests extends WindowTestsBase { lastReportedTiles.get(info1.token.asBinder()).topActivityType); } - private List<TaskTile> getTaskTiles(DisplayContent dc) { - ArrayList<TaskTile> out = new ArrayList<>(); + private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) { + ArrayList<Task> out = new ArrayList<>(); for (int i = dc.getStackCount() - 1; i >= 0; --i) { - final TaskTile t = dc.getStackAt(i).asTile(); - if (t != null) { - out.add(t); - } + final Task t = dc.getStackAt(i); + if (t.mCreatedByOrganizer) out.add(t); } return out; } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index a25acae3c036..e72f7df78162 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -151,7 +151,7 @@ public class TaskRecordTests extends ActivityTestsBase { assertFalse(factory.mCreated); Task.create(mService, 0 /*taskId*/, 0 /*activityType*/, - new ActivityInfo(), new Intent()); + new ActivityInfo(), new Intent(), false /* createdByOrganizer */); assertTrue(factory.mCreated); } finally { @@ -1000,7 +1000,7 @@ public class TaskRecordTests extends ActivityTestsBase { @Override Task create(ActivityTaskManagerService service, int taskId, int activityType, - ActivityInfo info, Intent intent) { + ActivityInfo info, Intent intent, boolean createdByOrganizer) { mCreated = true; return null; } |