diff options
| author | 2020-03-29 17:20:33 +0000 | |
|---|---|---|
| committer | 2020-03-29 17:20:33 +0000 | |
| commit | dddb9dd8535ebc9667b4533b311e95c5947ec18f (patch) | |
| tree | 50062fd2da54edb22abe424cac9394a78feaf071 | |
| parent | 520ae0a35874e35c5228c9b3a35efeecb0591c83 (diff) | |
| parent | 526ad439c31445eab8d384c5be7a1f1d6507bde3 (diff) | |
Merge "1/n Move TaskContainers out of DisplayContent class" into rvc-dev
5 files changed, 590 insertions, 561 deletions
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 99605ad64cb8..a871047a0602 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -193,12 +193,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowState.java" }, - "-1741065110": { - "message": "No app is requesting an orientation, return %d for display id=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "-1730156332": { "message": "Display id=%d rotation changed to %d from %d, lastOrientation=%d", "level": "VERBOSE", @@ -547,6 +541,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, + "-993446393": { + "message": "App is requesting an orientation, return %d for display id=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/TaskContainers.java" + }, "-993378225": { "message": "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING %s in %s", "level": "VERBOSE", @@ -715,12 +715,6 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "-650040763": { - "message": "rotationForOrientation(orient=%d, last=%d); user=%d %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, "-639305784": { "message": "Could not report config changes to the window token client.", "level": "WARN", @@ -1021,12 +1015,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "44438983": { - "message": "performLayout: Activity exiting now removed %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "45285419": { "message": "startingWindow was set but startingSurface==null, couldn't remove", "level": "VERBOSE", @@ -1087,6 +1075,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowSurfaceController.java" }, + "137835146": { + "message": "No app is requesting an orientation, return %d for display id=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/TaskContainers.java" + }, "140319294": { "message": "IME target changed within ActivityRecord", "level": "DEBUG", @@ -1531,12 +1525,6 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "845234215": { - "message": "App is requesting an orientation, return %d for display id=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "853091290": { "message": "Moved stack=%s behind stack=%s", "level": "DEBUG", @@ -1927,6 +1915,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1685441447": { + "message": "performLayout: Activity exiting now removed %s", + "level": "VERBOSE", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/TaskContainers.java" + }, "1720229827": { "message": "Creating animation bounds layer", "level": "INFO", diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 0ec0c7b53875..3c083e17931d 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -19,8 +19,6 @@ package com.android.server.wm; import android.content.res.Resources; import android.text.TextUtils; -import com.android.server.wm.DisplayContent.TaskContainers; - /** * Policy that manages DisplayAreas. */ diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 9fc90daaccfc..5900ae853a0b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -32,9 +32,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.isSplitScreenWindowingMode; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; @@ -79,7 +77,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_TASK_OPEN; import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; -import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; @@ -108,7 +105,6 @@ import static com.android.server.wm.DisplayContentProto.ROTATION; import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE; import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER; -import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; @@ -289,7 +285,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** The containers below are the only child containers {@link #mWindowContainers} can have. */ // Contains all window containers that are related to apps (Activities) - private final TaskContainers mTaskContainers = new TaskContainers(mWmService); + private final TaskContainers mTaskContainers = new TaskContainers(this, mWmService); // Contains all IME window containers. Note that the z-ordering of the IME windows will depend // on the IME target. We mainly have this container grouping so we can keep track of all the IME @@ -579,10 +575,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Last systemUiVisibility we dispatched to windows. private int mLastDispatchedSystemUiVisibility = 0; - private final ArrayList<ActivityStack> mTmpAlwaysOnTopStacks = new ArrayList<>(); - private final ArrayList<ActivityStack> mTmpNormalStacks = new ArrayList<>(); - private final ArrayList<ActivityStack> mTmpHomeStacks = new ArrayList<>(); - /** Corner radius that windows should have in order to match the display. */ private final float mWindowCornerRadius; @@ -4260,531 +4252,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - /** - * Window container class that contains all containers on this display relating to Apps. - * I.e Activities. - */ - final class TaskContainers extends DisplayArea<ActivityStack> { - /** - * A control placed at the appropriate level for transitions to occur. - */ - SurfaceControl mAppAnimationLayer = null; - SurfaceControl mBoostedAppAnimationLayer = null; - SurfaceControl mHomeAppAnimationLayer = null; - - /** - * Given that the split-screen divider does not have an AppWindowToken, it - * will have to live inside of a "NonAppWindowContainer". However, in visual Z order - * it will need to be interleaved with some of our children, appearing on top of - * both docked stacks but underneath any assistant stacks. - * - * To solve this problem we have this anchor control, which will always exist so - * we can always assign it the correct value in our {@link #assignChildLayers}. - * Likewise since it always exists, we can always - * assign the divider a layer relative to it. This way we prevent linking lifecycle - * events between tasks and the divider window. - */ - SurfaceControl mSplitScreenDividerAnchor = null; - - // Cached reference to some special tasks we tend to get a lot so we don't need to loop - // through the list to find them. - private ActivityStack mRootHomeTask = null; - private ActivityStack mRootPinnedTask = null; - private ActivityStack mRootSplitScreenPrimaryTask = null; - - TaskContainers(WindowManagerService service) { - super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER); - } - - /** - * Returns the topmost stack on the display that is compatible with the input windowing mode - * and activity type. Null is no compatible stack on the display. - */ - ActivityStack getStack(int windowingMode, int activityType) { - if (activityType == ACTIVITY_TYPE_HOME) { - return mRootHomeTask; - } - if (windowingMode == WINDOWING_MODE_PINNED) { - return mRootPinnedTask; - } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - return mRootSplitScreenPrimaryTask; - } - for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) { - final ActivityStack stack = mTaskContainers.getChildAt(i); - if (activityType == ACTIVITY_TYPE_UNDEFINED - && windowingMode == stack.getWindowingMode()) { - // Passing in undefined type means we want to match the topmost stack with the - // windowing mode. - return stack; - } - if (stack.isCompatible(windowingMode, activityType)) { - return stack; - } - } - return null; - } - - @VisibleForTesting - ActivityStack getTopStack() { - final int count = mTaskContainers.getChildCount(); - return count > 0 ? mTaskContainers.getChildAt(count - 1) : null; - } - - int getIndexOf(ActivityStack stack) { - return mTaskContainers.mChildren.indexOf(stack); - } - - ActivityStack getRootHomeTask() { - return mRootHomeTask; - } - - ActivityStack getRootPinnedTask() { - return mRootPinnedTask; - } - - ActivityStack getRootSplitScreenPrimaryTask() { - return mRootSplitScreenPrimaryTask; - } - - ArrayList<Task> getVisibleTasks() { - final ArrayList<Task> visibleTasks = new ArrayList<>(); - forAllTasks(task -> { - if (task.isLeafTask() && task.isVisible()) { - visibleTasks.add(task); - } - }); - return visibleTasks; - } - - void onStackWindowingModeChanged(ActivityStack stack) { - removeStackReferenceIfNeeded(stack); - addStackReferenceIfNeeded(stack); - if (stack == mRootPinnedTask && getTopStack() != stack) { - // Looks like this stack changed windowing mode to pinned. Move it to the top. - positionChildAt(POSITION_TOP, stack, false /* includingParents */); - } - } - - private void addStackReferenceIfNeeded(ActivityStack stack) { - if (stack.isActivityTypeHome()) { - if (mRootHomeTask != null) { - if (!stack.isDescendantOf(mRootHomeTask)) { - throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack=" - + mRootHomeTask + " already exist on display=" + this - + " stack=" + stack); - } - } else { - mRootHomeTask = stack; - } - } - - if (!stack.isRootTask()) { - return; - } - final int windowingMode = stack.getWindowingMode(); - if (windowingMode == WINDOWING_MODE_PINNED) { - if (mRootPinnedTask != null) { - 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) { - throw new IllegalArgumentException( - "addStackReferenceIfNeeded: split screen primary stack=" - + mRootSplitScreenPrimaryTask - + " already exist on display=" + this + " stack=" + stack); - } - mRootSplitScreenPrimaryTask = stack; - } - } - - void removeStackReferenceIfNeeded(ActivityStack stack) { - if (stack == mRootHomeTask) { - mRootHomeTask = null; - } else if (stack == mRootPinnedTask) { - mRootPinnedTask = null; - } else if (stack == mRootSplitScreenPrimaryTask) { - mRootSplitScreenPrimaryTask = null; - } - } - - @Override - void addChild(ActivityStack stack, int position) { - addStackReferenceIfNeeded(stack); - position = findPositionForStack(position, stack, true /* adding */); - - super.addChild(stack, position); - mAtmService.updateSleepIfNeededLocked(); - - // The reparenting case is handled in WindowContainer. - if (!stack.mReparenting) { - setLayoutNeeded(); - } - } - - @Override - protected void removeChild(ActivityStack stack) { - super.removeChild(stack); - mDisplayContent.onStackRemoved(stack); - mAtmService.updateSleepIfNeededLocked(); - removeStackReferenceIfNeeded(stack); - } - - @Override - boolean isOnTop() { - // Considered always on top - return true; - } - - @Override - void positionChildAt(int position, ActivityStack child, boolean includingParents) { - final boolean moveToTop = (position == POSITION_TOP || position == getChildCount()); - final boolean moveToBottom = (position == POSITION_BOTTOM || position == 0); - if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) { - // This stack is always-on-top, override the default behavior. - Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom"); - - // Moving to its current position, as we must call super but we don't want to - // perform any meaningful action. - final int currentPosition = mChildren.indexOf(child); - super.positionChildAt(currentPosition, child, false /* includingParents */); - return; - } - // We don't allow untrusted display to top when task stack moves to top, - // until user tapping this display to change display position as top intentionally. - if (isUntrustedVirtualDisplay() && !getParent().isOnTop()) { - includingParents = false; - } - final int targetPosition = findPositionForStack(position, child, false /* adding */); - super.positionChildAt(targetPosition, child, false /* includingParents */); - - if (includingParents && (moveToTop || moveToBottom)) { - // The DisplayContent children do not re-order, but we still want to move the - // display of this stack container because the intention of positioning is to have - // higher z-order to gain focus. - positionDisplayAt(moveToTop ? POSITION_TOP : POSITION_BOTTOM, - true /* includingParents */); - } - - child.updateTaskMovement(moveToTop); - - setLayoutNeeded(); - } - - /** - * When stack is added or repositioned, find a proper position for it. - * This will make sure that pinned stack always stays on top. - * @param requestedPosition Position requested by caller. - * @param stack Stack to be added or positioned. - * @param adding Flag indicates whether we're adding a new stack or positioning an existing. - * @return The proper position for the stack. - */ - private int findPositionForStack(int requestedPosition, ActivityStack stack, - boolean adding) { - if (stack.isActivityTypeDream()) { - return POSITION_TOP; - } - - if (stack.inPinnedWindowingMode()) { - return POSITION_TOP; - } - - final int topChildPosition = mChildren.size() - 1; - int belowAlwaysOnTopPosition = POSITION_BOTTOM; - for (int i = topChildPosition; i >= 0; --i) { - // Since a stack could be repositioned while being one of the child, return - // current index if that's the same stack we are positioning and it is always on - // top. - final boolean sameStack = getStacks().get(i) == stack; - if ((sameStack && stack.isAlwaysOnTop()) - || (!sameStack && !getStacks().get(i).isAlwaysOnTop())) { - belowAlwaysOnTopPosition = i; - break; - } - } - - // The max possible position we can insert the stack at. - int maxPosition = POSITION_TOP; - // The min possible position we can insert the stack at. - int minPosition = POSITION_BOTTOM; - - if (stack.isAlwaysOnTop()) { - if (hasPinnedTask()) { - // Always-on-top stacks go below the pinned stack. - maxPosition = getStacks().indexOf(mRootPinnedTask) - 1; - } - // Always-on-top stacks need to be above all other stacks. - minPosition = belowAlwaysOnTopPosition != - POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition; - } else { - // Other stacks need to be below the always-on-top stacks. - maxPosition = belowAlwaysOnTopPosition != - POSITION_BOTTOM ? belowAlwaysOnTopPosition : 0; - } - - // Cap the requested position to something reasonable for the previous position check - // below. - if (requestedPosition == POSITION_TOP) { - requestedPosition = mChildren.size(); - } else if (requestedPosition == POSITION_BOTTOM) { - requestedPosition = 0; - } - - int targetPosition = requestedPosition; - targetPosition = Math.min(targetPosition, maxPosition); - targetPosition = Math.max(targetPosition, minPosition); - - int prevPosition = getStacks().indexOf(stack); - // The positions we calculated above (maxPosition, minPosition) do not take into - // consideration the following edge cases. - // 1) We need to adjust the position depending on the value "adding". - // 2) When we are moving a stack to another position, we also need to adjust the - // position depending on whether the stack is moving to a higher or lower position. - if ((targetPosition != requestedPosition) && - (adding || targetPosition < prevPosition)) { - targetPosition++; - } - - return targetPosition; - } - - @Override - boolean forAllWindows(ToBooleanFunction<WindowState> callback, - boolean traverseTopToBottom) { - if (traverseTopToBottom) { - if (super.forAllWindows(callback, traverseTopToBottom)) { - return true; - } - if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) { - return true; - } - } else { - if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) { - return true; - } - if (super.forAllWindows(callback, traverseTopToBottom)) { - return true; - } - } - return false; - } - - private boolean forAllExitingAppTokenWindows(ToBooleanFunction<WindowState> callback, - boolean traverseTopToBottom) { - // For legacy reasons we process the TaskStack.mExitingActivities first here before the - // app tokens. - // TODO: Investigate if we need to continue to do this or if we can just process them - // in-order. - if (traverseTopToBottom) { - for (int i = mChildren.size() - 1; i >= 0; --i) { - final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities; - for (int j = activities.size() - 1; j >= 0; --j) { - if (activities.get(j).forAllWindowsUnchecked(callback, - traverseTopToBottom)) { - return true; - } - } - } - } else { - final int count = mChildren.size(); - for (int i = 0; i < count; ++i) { - final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities; - final int appTokensCount = activities.size(); - for (int j = 0; j < appTokensCount; j++) { - if (activities.get(j).forAllWindowsUnchecked(callback, - traverseTopToBottom)) { - return true; - } - } - } - } - return false; - } - - void setExitingTokensHasVisible(boolean hasVisible) { - for (int i = mChildren.size() - 1; i >= 0; --i) { - final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities; - for (int j = activities.size() - 1; j >= 0; --j) { - activities.get(j).hasVisible = hasVisible; - } - } - } - - void removeExistingAppTokensIfPossible() { - for (int i = mChildren.size() - 1; i >= 0; --i) { - final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities; - for (int j = activities.size() - 1; j >= 0; --j) { - final ActivityRecord activity = activities.get(j); - if (!activity.hasVisible && !mClosingApps.contains(activity) - && (!activity.mIsExiting || activity.isEmpty())) { - // Make sure there is no animation running on this activity, so any windows - // associated with it will be removed as soon as their animations are - // complete. - cancelAnimation(); - ProtoLog.v(WM_DEBUG_ADD_REMOVE, - "performLayout: Activity exiting now removed %s", activity); - activity.removeIfPossible(); - } - } - } - } - - @Override - int getOrientation(int candidate) { - if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { - // 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()) { - final Task topMost = mRootHomeTask.getTopMostTask(); - final boolean resizable = topMost != null && topMost.isResizeable(); - if (!(resizable && mRootHomeTask.matchParentBounds())) { - final int orientation = mRootHomeTask.getOrientation(); - if (orientation != SCREEN_ORIENTATION_UNSET) { - return orientation; - } - } - } - return SCREEN_ORIENTATION_UNSPECIFIED; - } - - final int orientation = super.getOrientation(candidate); - if (orientation != SCREEN_ORIENTATION_UNSET - && orientation != SCREEN_ORIENTATION_BEHIND) { - ProtoLog.v(WM_DEBUG_ORIENTATION, - "App is requesting an orientation, return %d for display id=%d", - orientation, mDisplayId); - return orientation; - } - - ProtoLog.v(WM_DEBUG_ORIENTATION, - "No app is requesting an orientation, return %d for display id=%d", - getLastOrientation(), mDisplayId); - // The next app has not been requested to be visible, so we keep the current orientation - // to prevent freezing/unfreezing the display too early. - return getLastOrientation(); - } - - @Override - void assignChildLayers(SurfaceControl.Transaction t) { - assignStackOrdering(t); - - for (int i = 0; i < mChildren.size(); i++) { - final ActivityStack s = mChildren.get(i); - s.assignChildLayers(t); - } - } - - void assignStackOrdering(SurfaceControl.Transaction t) { - if (getParent() == null) { - return; - } - mTmpAlwaysOnTopStacks.clear(); - mTmpHomeStacks.clear(); - mTmpNormalStacks.clear(); - for (int i = 0; i < mChildren.size(); ++i) { - final ActivityStack s = mChildren.get(i); - if (s.isAlwaysOnTop()) { - mTmpAlwaysOnTopStacks.add(s); - } else if (s.isActivityTypeHome()) { - mTmpHomeStacks.add(s); - } else { - mTmpNormalStacks.add(s); - } - } - - int layer = 0; - // Place home stacks to the bottom. - for (int i = 0; i < mTmpHomeStacks.size(); i++) { - mTmpHomeStacks.get(i).assignLayer(t, layer++); - } - // The home animation layer is between the home stacks and the normal stacks. - final int layerForHomeAnimationLayer = layer++; - int layerForSplitScreenDividerAnchor = layer++; - int layerForAnimationLayer = layer++; - for (int i = 0; i < mTmpNormalStacks.size(); i++) { - final ActivityStack s = mTmpNormalStacks.get(i); - s.assignLayer(t, layer++); - if (s.inSplitScreenWindowingMode()) { - // The split screen divider anchor is located above the split screen window. - layerForSplitScreenDividerAnchor = layer++; - } - if (s.isTaskAnimating() || s.isAppTransitioning()) { - // The animation layer is located above the highest animating stack and no - // higher. - layerForAnimationLayer = layer++; - } - } - // The boosted animation layer is between the normal stacks and the always on top - // stacks. - final int layerForBoostedAnimationLayer = layer++; - for (int i = 0; i < mTmpAlwaysOnTopStacks.size(); i++) { - mTmpAlwaysOnTopStacks.get(i).assignLayer(t, layer++); - } - - t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer); - t.setLayer(mAppAnimationLayer, layerForAnimationLayer); - t.setLayer(mSplitScreenDividerAnchor, layerForSplitScreenDividerAnchor); - t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer); - } - - @Override - SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) { - switch (animationLayer) { - case ANIMATION_LAYER_BOOSTED: - return mBoostedAppAnimationLayer; - case ANIMATION_LAYER_HOME: - return mHomeAppAnimationLayer; - case ANIMATION_LAYER_STANDARD: - default: - return mAppAnimationLayer; - } - } - - SurfaceControl getSplitScreenDividerAnchor() { - return mSplitScreenDividerAnchor; - } - - @Override - void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { - if (getParent() != null) { - super.onParentChanged(newParent, oldParent, () -> { - mAppAnimationLayer = makeChildSurface(null) - .setName("animationLayer") - .build(); - mBoostedAppAnimationLayer = makeChildSurface(null) - .setName("boostedAnimationLayer") - .build(); - mHomeAppAnimationLayer = makeChildSurface(null) - .setName("homeAnimationLayer") - .build(); - mSplitScreenDividerAnchor = makeChildSurface(null) - .setName("splitScreenDividerAnchor") - .build(); - getPendingTransaction() - .show(mAppAnimationLayer) - .show(mBoostedAppAnimationLayer) - .show(mHomeAppAnimationLayer) - .show(mSplitScreenDividerAnchor); - }); - } else { - super.onParentChanged(newParent, oldParent); - mWmService.mTransactionFactory.get() - .remove(mAppAnimationLayer) - .remove(mBoostedAppAnimationLayer) - .remove(mHomeAppAnimationLayer) - .remove(mSplitScreenDividerAnchor) - .apply(); - mAppAnimationLayer = null; - mBoostedAppAnimationLayer = null; - mHomeAppAnimationLayer = null; - mSplitScreenDividerAnchor = null; - } - } - } - private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> { private final String mName; diff --git a/services/core/java/com/android/server/wm/TaskContainers.java b/services/core/java/com/android/server/wm/TaskContainers.java new file mode 100644 index 000000000000..a95994245c4f --- /dev/null +++ b/services/core/java/com/android/server/wm/TaskContainers.java @@ -0,0 +1,570 @@ +/* + * 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.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER; + +import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; +import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import android.util.Slog; +import android.view.SurfaceControl; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ToBooleanFunction; +import com.android.server.protolog.common.ProtoLog; + +import java.util.ArrayList; +import java.util.List; + +/** + * Window container class that contains all containers on this display relating to Apps. + * I.e Activities. + */ +final class TaskContainers extends DisplayArea<ActivityStack> { + private DisplayContent mDisplayContent; + /** + * A control placed at the appropriate level for transitions to occur. + */ + private SurfaceControl mAppAnimationLayer; + private SurfaceControl mBoostedAppAnimationLayer; + private SurfaceControl mHomeAppAnimationLayer; + + /** + * Given that the split-screen divider does not have an AppWindowToken, it + * will have to live inside of a "NonAppWindowContainer". However, in visual Z order + * it will need to be interleaved with some of our children, appearing on top of + * both docked stacks but underneath any assistant stacks. + * + * To solve this problem we have this anchor control, which will always exist so + * we can always assign it the correct value in our {@link #assignChildLayers}. + * Likewise since it always exists, we can always + * assign the divider a layer relative to it. This way we prevent linking lifecycle + * events between tasks and the divider window. + */ + private SurfaceControl mSplitScreenDividerAnchor; + + // Cached reference to some special tasks we tend to get a lot so we don't need to loop + // through the list to find them. + private ActivityStack mRootHomeTask; + private ActivityStack mRootPinnedTask; + private ActivityStack mRootSplitScreenPrimaryTask; + + private final ArrayList<ActivityStack> mTmpAlwaysOnTopStacks = new ArrayList<>(); + private final ArrayList<ActivityStack> mTmpNormalStacks = new ArrayList<>(); + private final ArrayList<ActivityStack> mTmpHomeStacks = new ArrayList<>(); + + TaskContainers(DisplayContent displayContent, WindowManagerService service) { + super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER); + mDisplayContent = displayContent; + } + + /** + * Returns the topmost stack on the display that is compatible with the input windowing mode + * and activity type. Null is no compatible stack on the display. + */ + ActivityStack getStack(int windowingMode, int activityType) { + if (activityType == ACTIVITY_TYPE_HOME) { + return mRootHomeTask; + } + if (windowingMode == WINDOWING_MODE_PINNED) { + return mRootPinnedTask; + } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + return mRootSplitScreenPrimaryTask; + } + for (int i = getChildCount() - 1; i >= 0; --i) { + final ActivityStack stack = getChildAt(i); + if (activityType == ACTIVITY_TYPE_UNDEFINED + && windowingMode == stack.getWindowingMode()) { + // Passing in undefined type means we want to match the topmost stack with the + // windowing mode. + return stack; + } + if (stack.isCompatible(windowingMode, activityType)) { + return stack; + } + } + return null; + } + + @VisibleForTesting + ActivityStack getTopStack() { + final int count = getChildCount(); + return count > 0 ? getChildAt(count - 1) : null; + } + + int getIndexOf(ActivityStack stack) { + return mChildren.indexOf(stack); + } + + ActivityStack getRootHomeTask() { + return mRootHomeTask; + } + + ActivityStack getRootPinnedTask() { + return mRootPinnedTask; + } + + ActivityStack getRootSplitScreenPrimaryTask() { + return mRootSplitScreenPrimaryTask; + } + + ArrayList<Task> getVisibleTasks() { + final ArrayList<Task> visibleTasks = new ArrayList<>(); + forAllTasks(task -> { + if (task.isLeafTask() && task.isVisible()) { + visibleTasks.add(task); + } + }); + return visibleTasks; + } + + void onStackWindowingModeChanged(ActivityStack stack) { + removeStackReferenceIfNeeded(stack); + addStackReferenceIfNeeded(stack); + if (stack == mRootPinnedTask && getTopStack() != stack) { + // Looks like this stack changed windowing mode to pinned. Move it to the top. + positionChildAt(POSITION_TOP, stack, false /* includingParents */); + } + } + + void addStackReferenceIfNeeded(ActivityStack stack) { + if (stack.isActivityTypeHome()) { + if (mRootHomeTask != null) { + if (!stack.isDescendantOf(mRootHomeTask)) { + throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack=" + + mRootHomeTask + " already exist on display=" + this + + " stack=" + stack); + } + } else { + mRootHomeTask = stack; + } + } + + if (!stack.isRootTask()) { + return; + } + final int windowingMode = stack.getWindowingMode(); + if (windowingMode == WINDOWING_MODE_PINNED) { + if (mRootPinnedTask != null) { + 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) { + throw new IllegalArgumentException( + "addStackReferenceIfNeeded: split screen primary stack=" + + mRootSplitScreenPrimaryTask + + " already exist on display=" + this + " stack=" + stack); + } + mRootSplitScreenPrimaryTask = stack; + } + } + + void removeStackReferenceIfNeeded(ActivityStack stack) { + if (stack == mRootHomeTask) { + mRootHomeTask = null; + } else if (stack == mRootPinnedTask) { + mRootPinnedTask = null; + } else if (stack == mRootSplitScreenPrimaryTask) { + mRootSplitScreenPrimaryTask = null; + } + } + + @Override + void addChild(ActivityStack stack, int position) { + addStackReferenceIfNeeded(stack); + position = findPositionForStack(position, stack, true /* adding */); + + super.addChild(stack, position); + mDisplayContent.mAtmService.updateSleepIfNeededLocked(); + + // The reparenting case is handled in WindowContainer. + if (!stack.mReparenting) { + mDisplayContent.setLayoutNeeded(); + } + } + + @Override + protected void removeChild(ActivityStack stack) { + super.removeChild(stack); + mDisplayContent.onStackRemoved(stack); + mDisplayContent.mAtmService.updateSleepIfNeededLocked(); + removeStackReferenceIfNeeded(stack); + } + + @Override + boolean isOnTop() { + // Considered always on top + return true; + } + + @Override + void positionChildAt(int position, ActivityStack child, boolean includingParents) { + final boolean moveToTop = (position == POSITION_TOP || position == getChildCount()); + final boolean moveToBottom = (position == POSITION_BOTTOM || position == 0); + if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) { + // This stack is always-on-top, override the default behavior. + Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom"); + + // Moving to its current position, as we must call super but we don't want to + // perform any meaningful action. + final int currentPosition = mChildren.indexOf(child); + super.positionChildAt(currentPosition, child, false /* includingParents */); + return; + } + // We don't allow untrusted display to top when task stack moves to top, + // until user tapping this display to change display position as top intentionally. + if (mDisplayContent.isUntrustedVirtualDisplay() && !getParent().isOnTop()) { + includingParents = false; + } + final int targetPosition = findPositionForStack(position, child, false /* adding */); + super.positionChildAt(targetPosition, child, false /* includingParents */); + + if (includingParents && (moveToTop || moveToBottom)) { + // The DisplayContent children do not re-order, but we still want to move the + // display of this stack container because the intention of positioning is to have + // higher z-order to gain focus. + mDisplayContent.positionDisplayAt(moveToTop ? POSITION_TOP : POSITION_BOTTOM, + true /* includingParents */); + } + + child.updateTaskMovement(moveToTop); + + mDisplayContent.setLayoutNeeded(); + } + + /** + * When stack is added or repositioned, find a proper position for it. + * This will make sure that pinned stack always stays on top. + * @param requestedPosition Position requested by caller. + * @param stack Stack to be added or positioned. + * @param adding Flag indicates whether we're adding a new stack or positioning an existing. + * @return The proper position for the stack. + */ + private int findPositionForStack(int requestedPosition, ActivityStack stack, + boolean adding) { + if (stack.isActivityTypeDream()) { + return POSITION_TOP; + } + + if (stack.inPinnedWindowingMode()) { + return POSITION_TOP; + } + + final int topChildPosition = mChildren.size() - 1; + int belowAlwaysOnTopPosition = POSITION_BOTTOM; + for (int i = topChildPosition; i >= 0; --i) { + // Since a stack could be repositioned while being one of the child, return + // current index if that's the same stack we are positioning and it is always on + // top. + final boolean sameStack = mDisplayContent.getStacks().get(i) == stack; + if ((sameStack && stack.isAlwaysOnTop()) + || (!sameStack && !mDisplayContent.getStacks().get(i).isAlwaysOnTop())) { + belowAlwaysOnTopPosition = i; + break; + } + } + + // The max possible position we can insert the stack at. + int maxPosition = POSITION_TOP; + // The min possible position we can insert the stack at. + int minPosition = POSITION_BOTTOM; + + if (stack.isAlwaysOnTop()) { + if (mDisplayContent.hasPinnedTask()) { + // Always-on-top stacks go below the pinned stack. + maxPosition = mDisplayContent.getStacks().indexOf(mRootPinnedTask) - 1; + } + // Always-on-top stacks need to be above all other stacks. + minPosition = belowAlwaysOnTopPosition + != POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition; + } else { + // Other stacks need to be below the always-on-top stacks. + maxPosition = belowAlwaysOnTopPosition + != POSITION_BOTTOM ? belowAlwaysOnTopPosition : 0; + } + + // Cap the requested position to something reasonable for the previous position check + // below. + if (requestedPosition == POSITION_TOP) { + requestedPosition = mChildren.size(); + } else if (requestedPosition == POSITION_BOTTOM) { + requestedPosition = 0; + } + + int targetPosition = requestedPosition; + targetPosition = Math.min(targetPosition, maxPosition); + targetPosition = Math.max(targetPosition, minPosition); + + int prevPosition = mDisplayContent.getStacks().indexOf(stack); + // The positions we calculated above (maxPosition, minPosition) do not take into + // consideration the following edge cases. + // 1) We need to adjust the position depending on the value "adding". + // 2) When we are moving a stack to another position, we also need to adjust the + // position depending on whether the stack is moving to a higher or lower position. + if ((targetPosition != requestedPosition) && (adding || targetPosition < prevPosition)) { + targetPosition++; + } + + return targetPosition; + } + + @Override + boolean forAllWindows(ToBooleanFunction<WindowState> callback, + boolean traverseTopToBottom) { + if (traverseTopToBottom) { + if (super.forAllWindows(callback, traverseTopToBottom)) { + return true; + } + if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) { + return true; + } + } else { + if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) { + return true; + } + if (super.forAllWindows(callback, traverseTopToBottom)) { + return true; + } + } + return false; + } + + private boolean forAllExitingAppTokenWindows(ToBooleanFunction<WindowState> callback, + boolean traverseTopToBottom) { + // For legacy reasons we process the TaskStack.mExitingActivities first here before the + // app tokens. + // TODO: Investigate if we need to continue to do this or if we can just process them + // in-order. + if (traverseTopToBottom) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities; + for (int j = activities.size() - 1; j >= 0; --j) { + if (activities.get(j).forAllWindowsUnchecked(callback, + traverseTopToBottom)) { + return true; + } + } + } + } else { + final int count = mChildren.size(); + for (int i = 0; i < count; ++i) { + final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities; + final int appTokensCount = activities.size(); + for (int j = 0; j < appTokensCount; j++) { + if (activities.get(j).forAllWindowsUnchecked(callback, + traverseTopToBottom)) { + return true; + } + } + } + } + return false; + } + + void setExitingTokensHasVisible(boolean hasVisible) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities; + for (int j = activities.size() - 1; j >= 0; --j) { + activities.get(j).hasVisible = hasVisible; + } + } + } + + void removeExistingAppTokensIfPossible() { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities; + for (int j = activities.size() - 1; j >= 0; --j) { + final ActivityRecord activity = activities.get(j); + if (!activity.hasVisible && !mDisplayContent.mClosingApps.contains(activity) + && (!activity.mIsExiting || activity.isEmpty())) { + // Make sure there is no animation running on this activity, so any windows + // associated with it will be removed as soon as their animations are + // complete. + cancelAnimation(); + ProtoLog.v(WM_DEBUG_ADD_REMOVE, + "performLayout: Activity exiting now removed %s", activity); + activity.removeIfPossible(); + } + } + } + } + + @Override + int getOrientation(int candidate) { + if (mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { + // 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()) { + final Task topMost = mRootHomeTask.getTopMostTask(); + final boolean resizable = topMost != null && topMost.isResizeable(); + if (!(resizable && mRootHomeTask.matchParentBounds())) { + final int orientation = mRootHomeTask.getOrientation(); + if (orientation != SCREEN_ORIENTATION_UNSET) { + return orientation; + } + } + } + return SCREEN_ORIENTATION_UNSPECIFIED; + } + + final int orientation = super.getOrientation(candidate); + if (orientation != SCREEN_ORIENTATION_UNSET + && orientation != SCREEN_ORIENTATION_BEHIND) { + ProtoLog.v(WM_DEBUG_ORIENTATION, + "App is requesting an orientation, return %d for display id=%d", + orientation, mDisplayContent.mDisplayId); + return orientation; + } + + ProtoLog.v(WM_DEBUG_ORIENTATION, + "No app is requesting an orientation, return %d for display id=%d", + mDisplayContent.getLastOrientation(), mDisplayContent.mDisplayId); + // The next app has not been requested to be visible, so we keep the current orientation + // to prevent freezing/unfreezing the display too early. + return mDisplayContent.getLastOrientation(); + } + + @Override + void assignChildLayers(SurfaceControl.Transaction t) { + assignStackOrdering(t); + + for (int i = 0; i < mChildren.size(); i++) { + final ActivityStack s = mChildren.get(i); + s.assignChildLayers(t); + } + } + + void assignStackOrdering(SurfaceControl.Transaction t) { + if (getParent() == null) { + return; + } + mTmpAlwaysOnTopStacks.clear(); + mTmpHomeStacks.clear(); + mTmpNormalStacks.clear(); + for (int i = 0; i < mChildren.size(); ++i) { + final ActivityStack s = mChildren.get(i); + if (s.isAlwaysOnTop()) { + mTmpAlwaysOnTopStacks.add(s); + } else if (s.isActivityTypeHome()) { + mTmpHomeStacks.add(s); + } else { + mTmpNormalStacks.add(s); + } + } + + int layer = 0; + // Place home stacks to the bottom. + for (int i = 0; i < mTmpHomeStacks.size(); i++) { + mTmpHomeStacks.get(i).assignLayer(t, layer++); + } + // The home animation layer is between the home stacks and the normal stacks. + final int layerForHomeAnimationLayer = layer++; + int layerForSplitScreenDividerAnchor = layer++; + int layerForAnimationLayer = layer++; + for (int i = 0; i < mTmpNormalStacks.size(); i++) { + final ActivityStack s = mTmpNormalStacks.get(i); + s.assignLayer(t, layer++); + if (s.inSplitScreenWindowingMode()) { + // The split screen divider anchor is located above the split screen window. + layerForSplitScreenDividerAnchor = layer++; + } + if (s.isTaskAnimating() || s.isAppTransitioning()) { + // The animation layer is located above the highest animating stack and no + // higher. + layerForAnimationLayer = layer++; + } + } + // The boosted animation layer is between the normal stacks and the always on top + // stacks. + final int layerForBoostedAnimationLayer = layer++; + for (int i = 0; i < mTmpAlwaysOnTopStacks.size(); i++) { + mTmpAlwaysOnTopStacks.get(i).assignLayer(t, layer++); + } + + t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer); + t.setLayer(mAppAnimationLayer, layerForAnimationLayer); + t.setLayer(mSplitScreenDividerAnchor, layerForSplitScreenDividerAnchor); + t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer); + } + + @Override + SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) { + switch (animationLayer) { + case ANIMATION_LAYER_BOOSTED: + return mBoostedAppAnimationLayer; + case ANIMATION_LAYER_HOME: + return mHomeAppAnimationLayer; + case ANIMATION_LAYER_STANDARD: + default: + return mAppAnimationLayer; + } + } + + SurfaceControl getSplitScreenDividerAnchor() { + return mSplitScreenDividerAnchor; + } + + @Override + void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { + if (getParent() != null) { + super.onParentChanged(newParent, oldParent, () -> { + mAppAnimationLayer = makeChildSurface(null) + .setName("animationLayer") + .build(); + mBoostedAppAnimationLayer = makeChildSurface(null) + .setName("boostedAnimationLayer") + .build(); + mHomeAppAnimationLayer = makeChildSurface(null) + .setName("homeAnimationLayer") + .build(); + mSplitScreenDividerAnchor = makeChildSurface(null) + .setName("splitScreenDividerAnchor") + .build(); + getPendingTransaction() + .show(mAppAnimationLayer) + .show(mBoostedAppAnimationLayer) + .show(mHomeAppAnimationLayer) + .show(mSplitScreenDividerAnchor); + }); + } else { + super.onParentChanged(newParent, oldParent); + mWmService.mTransactionFactory.get() + .remove(mAppAnimationLayer) + .remove(mBoostedAppAnimationLayer) + .remove(mHomeAppAnimationLayer) + .remove(mSplitScreenDividerAnchor) + .apply(); + mAppAnimationLayer = null; + mBoostedAppAnimationLayer = null; + mHomeAppAnimationLayer = null; + mSplitScreenDividerAnchor = null; + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java index 31206315618e..32d7a0773b5e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java @@ -78,7 +78,7 @@ public class DisplayAreaProviderTest { @Override public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer, - DisplayContent.TaskContainers taskContainers) { + TaskContainers taskContainers) { throw new RuntimeException("test stub"); } } |