blob: a95994245c4f19c10c545e16d4b55255d21f6d5e [file] [log] [blame]
Andrii Kulian526ad432020-03-27 12:19:51 -07001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
19import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
20import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
21import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
22import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
23import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
24import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
25import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
26import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER;
27
28import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
29import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
30import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
31
32import android.util.Slog;
33import android.view.SurfaceControl;
34
35import com.android.internal.annotations.VisibleForTesting;
36import com.android.internal.util.ToBooleanFunction;
37import com.android.server.protolog.common.ProtoLog;
38
39import java.util.ArrayList;
40import java.util.List;
41
42/**
43 * Window container class that contains all containers on this display relating to Apps.
44 * I.e Activities.
45 */
46final class TaskContainers extends DisplayArea<ActivityStack> {
47 private DisplayContent mDisplayContent;
48 /**
49 * A control placed at the appropriate level for transitions to occur.
50 */
51 private SurfaceControl mAppAnimationLayer;
52 private SurfaceControl mBoostedAppAnimationLayer;
53 private SurfaceControl mHomeAppAnimationLayer;
54
55 /**
56 * Given that the split-screen divider does not have an AppWindowToken, it
57 * will have to live inside of a "NonAppWindowContainer". However, in visual Z order
58 * it will need to be interleaved with some of our children, appearing on top of
59 * both docked stacks but underneath any assistant stacks.
60 *
61 * To solve this problem we have this anchor control, which will always exist so
62 * we can always assign it the correct value in our {@link #assignChildLayers}.
63 * Likewise since it always exists, we can always
64 * assign the divider a layer relative to it. This way we prevent linking lifecycle
65 * events between tasks and the divider window.
66 */
67 private SurfaceControl mSplitScreenDividerAnchor;
68
69 // Cached reference to some special tasks we tend to get a lot so we don't need to loop
70 // through the list to find them.
71 private ActivityStack mRootHomeTask;
72 private ActivityStack mRootPinnedTask;
73 private ActivityStack mRootSplitScreenPrimaryTask;
74
75 private final ArrayList<ActivityStack> mTmpAlwaysOnTopStacks = new ArrayList<>();
76 private final ArrayList<ActivityStack> mTmpNormalStacks = new ArrayList<>();
77 private final ArrayList<ActivityStack> mTmpHomeStacks = new ArrayList<>();
78
79 TaskContainers(DisplayContent displayContent, WindowManagerService service) {
80 super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER);
81 mDisplayContent = displayContent;
82 }
83
84 /**
85 * Returns the topmost stack on the display that is compatible with the input windowing mode
86 * and activity type. Null is no compatible stack on the display.
87 */
88 ActivityStack getStack(int windowingMode, int activityType) {
89 if (activityType == ACTIVITY_TYPE_HOME) {
90 return mRootHomeTask;
91 }
92 if (windowingMode == WINDOWING_MODE_PINNED) {
93 return mRootPinnedTask;
94 } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
95 return mRootSplitScreenPrimaryTask;
96 }
97 for (int i = getChildCount() - 1; i >= 0; --i) {
98 final ActivityStack stack = getChildAt(i);
99 if (activityType == ACTIVITY_TYPE_UNDEFINED
100 && windowingMode == stack.getWindowingMode()) {
101 // Passing in undefined type means we want to match the topmost stack with the
102 // windowing mode.
103 return stack;
104 }
105 if (stack.isCompatible(windowingMode, activityType)) {
106 return stack;
107 }
108 }
109 return null;
110 }
111
112 @VisibleForTesting
113 ActivityStack getTopStack() {
114 final int count = getChildCount();
115 return count > 0 ? getChildAt(count - 1) : null;
116 }
117
118 int getIndexOf(ActivityStack stack) {
119 return mChildren.indexOf(stack);
120 }
121
122 ActivityStack getRootHomeTask() {
123 return mRootHomeTask;
124 }
125
126 ActivityStack getRootPinnedTask() {
127 return mRootPinnedTask;
128 }
129
130 ActivityStack getRootSplitScreenPrimaryTask() {
131 return mRootSplitScreenPrimaryTask;
132 }
133
134 ArrayList<Task> getVisibleTasks() {
135 final ArrayList<Task> visibleTasks = new ArrayList<>();
136 forAllTasks(task -> {
137 if (task.isLeafTask() && task.isVisible()) {
138 visibleTasks.add(task);
139 }
140 });
141 return visibleTasks;
142 }
143
144 void onStackWindowingModeChanged(ActivityStack stack) {
145 removeStackReferenceIfNeeded(stack);
146 addStackReferenceIfNeeded(stack);
147 if (stack == mRootPinnedTask && getTopStack() != stack) {
148 // Looks like this stack changed windowing mode to pinned. Move it to the top.
149 positionChildAt(POSITION_TOP, stack, false /* includingParents */);
150 }
151 }
152
153 void addStackReferenceIfNeeded(ActivityStack stack) {
154 if (stack.isActivityTypeHome()) {
155 if (mRootHomeTask != null) {
156 if (!stack.isDescendantOf(mRootHomeTask)) {
157 throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
158 + mRootHomeTask + " already exist on display=" + this
159 + " stack=" + stack);
160 }
161 } else {
162 mRootHomeTask = stack;
163 }
164 }
165
166 if (!stack.isRootTask()) {
167 return;
168 }
169 final int windowingMode = stack.getWindowingMode();
170 if (windowingMode == WINDOWING_MODE_PINNED) {
171 if (mRootPinnedTask != null) {
172 throw new IllegalArgumentException(
173 "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
174 + " already exist on display=" + this + " stack=" + stack);
175 }
176 mRootPinnedTask = stack;
177 } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
178 if (mRootSplitScreenPrimaryTask != null) {
179 throw new IllegalArgumentException(
180 "addStackReferenceIfNeeded: split screen primary stack="
181 + mRootSplitScreenPrimaryTask
182 + " already exist on display=" + this + " stack=" + stack);
183 }
184 mRootSplitScreenPrimaryTask = stack;
185 }
186 }
187
188 void removeStackReferenceIfNeeded(ActivityStack stack) {
189 if (stack == mRootHomeTask) {
190 mRootHomeTask = null;
191 } else if (stack == mRootPinnedTask) {
192 mRootPinnedTask = null;
193 } else if (stack == mRootSplitScreenPrimaryTask) {
194 mRootSplitScreenPrimaryTask = null;
195 }
196 }
197
198 @Override
199 void addChild(ActivityStack stack, int position) {
200 addStackReferenceIfNeeded(stack);
201 position = findPositionForStack(position, stack, true /* adding */);
202
203 super.addChild(stack, position);
204 mDisplayContent.mAtmService.updateSleepIfNeededLocked();
205
206 // The reparenting case is handled in WindowContainer.
207 if (!stack.mReparenting) {
208 mDisplayContent.setLayoutNeeded();
209 }
210 }
211
212 @Override
213 protected void removeChild(ActivityStack stack) {
214 super.removeChild(stack);
215 mDisplayContent.onStackRemoved(stack);
216 mDisplayContent.mAtmService.updateSleepIfNeededLocked();
217 removeStackReferenceIfNeeded(stack);
218 }
219
220 @Override
221 boolean isOnTop() {
222 // Considered always on top
223 return true;
224 }
225
226 @Override
227 void positionChildAt(int position, ActivityStack child, boolean includingParents) {
228 final boolean moveToTop = (position == POSITION_TOP || position == getChildCount());
229 final boolean moveToBottom = (position == POSITION_BOTTOM || position == 0);
230 if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
231 // This stack is always-on-top, override the default behavior.
232 Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
233
234 // Moving to its current position, as we must call super but we don't want to
235 // perform any meaningful action.
236 final int currentPosition = mChildren.indexOf(child);
237 super.positionChildAt(currentPosition, child, false /* includingParents */);
238 return;
239 }
240 // We don't allow untrusted display to top when task stack moves to top,
241 // until user tapping this display to change display position as top intentionally.
242 if (mDisplayContent.isUntrustedVirtualDisplay() && !getParent().isOnTop()) {
243 includingParents = false;
244 }
245 final int targetPosition = findPositionForStack(position, child, false /* adding */);
246 super.positionChildAt(targetPosition, child, false /* includingParents */);
247
248 if (includingParents && (moveToTop || moveToBottom)) {
249 // The DisplayContent children do not re-order, but we still want to move the
250 // display of this stack container because the intention of positioning is to have
251 // higher z-order to gain focus.
252 mDisplayContent.positionDisplayAt(moveToTop ? POSITION_TOP : POSITION_BOTTOM,
253 true /* includingParents */);
254 }
255
256 child.updateTaskMovement(moveToTop);
257
258 mDisplayContent.setLayoutNeeded();
259 }
260
261 /**
262 * When stack is added or repositioned, find a proper position for it.
263 * This will make sure that pinned stack always stays on top.
264 * @param requestedPosition Position requested by caller.
265 * @param stack Stack to be added or positioned.
266 * @param adding Flag indicates whether we're adding a new stack or positioning an existing.
267 * @return The proper position for the stack.
268 */
269 private int findPositionForStack(int requestedPosition, ActivityStack stack,
270 boolean adding) {
271 if (stack.isActivityTypeDream()) {
272 return POSITION_TOP;
273 }
274
275 if (stack.inPinnedWindowingMode()) {
276 return POSITION_TOP;
277 }
278
279 final int topChildPosition = mChildren.size() - 1;
280 int belowAlwaysOnTopPosition = POSITION_BOTTOM;
281 for (int i = topChildPosition; i >= 0; --i) {
282 // Since a stack could be repositioned while being one of the child, return
283 // current index if that's the same stack we are positioning and it is always on
284 // top.
285 final boolean sameStack = mDisplayContent.getStacks().get(i) == stack;
286 if ((sameStack && stack.isAlwaysOnTop())
287 || (!sameStack && !mDisplayContent.getStacks().get(i).isAlwaysOnTop())) {
288 belowAlwaysOnTopPosition = i;
289 break;
290 }
291 }
292
293 // The max possible position we can insert the stack at.
294 int maxPosition = POSITION_TOP;
295 // The min possible position we can insert the stack at.
296 int minPosition = POSITION_BOTTOM;
297
298 if (stack.isAlwaysOnTop()) {
299 if (mDisplayContent.hasPinnedTask()) {
300 // Always-on-top stacks go below the pinned stack.
301 maxPosition = mDisplayContent.getStacks().indexOf(mRootPinnedTask) - 1;
302 }
303 // Always-on-top stacks need to be above all other stacks.
304 minPosition = belowAlwaysOnTopPosition
305 != POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition;
306 } else {
307 // Other stacks need to be below the always-on-top stacks.
308 maxPosition = belowAlwaysOnTopPosition
309 != POSITION_BOTTOM ? belowAlwaysOnTopPosition : 0;
310 }
311
312 // Cap the requested position to something reasonable for the previous position check
313 // below.
314 if (requestedPosition == POSITION_TOP) {
315 requestedPosition = mChildren.size();
316 } else if (requestedPosition == POSITION_BOTTOM) {
317 requestedPosition = 0;
318 }
319
320 int targetPosition = requestedPosition;
321 targetPosition = Math.min(targetPosition, maxPosition);
322 targetPosition = Math.max(targetPosition, minPosition);
323
324 int prevPosition = mDisplayContent.getStacks().indexOf(stack);
325 // The positions we calculated above (maxPosition, minPosition) do not take into
326 // consideration the following edge cases.
327 // 1) We need to adjust the position depending on the value "adding".
328 // 2) When we are moving a stack to another position, we also need to adjust the
329 // position depending on whether the stack is moving to a higher or lower position.
330 if ((targetPosition != requestedPosition) && (adding || targetPosition < prevPosition)) {
331 targetPosition++;
332 }
333
334 return targetPosition;
335 }
336
337 @Override
338 boolean forAllWindows(ToBooleanFunction<WindowState> callback,
339 boolean traverseTopToBottom) {
340 if (traverseTopToBottom) {
341 if (super.forAllWindows(callback, traverseTopToBottom)) {
342 return true;
343 }
344 if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) {
345 return true;
346 }
347 } else {
348 if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) {
349 return true;
350 }
351 if (super.forAllWindows(callback, traverseTopToBottom)) {
352 return true;
353 }
354 }
355 return false;
356 }
357
358 private boolean forAllExitingAppTokenWindows(ToBooleanFunction<WindowState> callback,
359 boolean traverseTopToBottom) {
360 // For legacy reasons we process the TaskStack.mExitingActivities first here before the
361 // app tokens.
362 // TODO: Investigate if we need to continue to do this or if we can just process them
363 // in-order.
364 if (traverseTopToBottom) {
365 for (int i = mChildren.size() - 1; i >= 0; --i) {
366 final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
367 for (int j = activities.size() - 1; j >= 0; --j) {
368 if (activities.get(j).forAllWindowsUnchecked(callback,
369 traverseTopToBottom)) {
370 return true;
371 }
372 }
373 }
374 } else {
375 final int count = mChildren.size();
376 for (int i = 0; i < count; ++i) {
377 final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
378 final int appTokensCount = activities.size();
379 for (int j = 0; j < appTokensCount; j++) {
380 if (activities.get(j).forAllWindowsUnchecked(callback,
381 traverseTopToBottom)) {
382 return true;
383 }
384 }
385 }
386 }
387 return false;
388 }
389
390 void setExitingTokensHasVisible(boolean hasVisible) {
391 for (int i = mChildren.size() - 1; i >= 0; --i) {
392 final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
393 for (int j = activities.size() - 1; j >= 0; --j) {
394 activities.get(j).hasVisible = hasVisible;
395 }
396 }
397 }
398
399 void removeExistingAppTokensIfPossible() {
400 for (int i = mChildren.size() - 1; i >= 0; --i) {
401 final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
402 for (int j = activities.size() - 1; j >= 0; --j) {
403 final ActivityRecord activity = activities.get(j);
404 if (!activity.hasVisible && !mDisplayContent.mClosingApps.contains(activity)
405 && (!activity.mIsExiting || activity.isEmpty())) {
406 // Make sure there is no animation running on this activity, so any windows
407 // associated with it will be removed as soon as their animations are
408 // complete.
409 cancelAnimation();
410 ProtoLog.v(WM_DEBUG_ADD_REMOVE,
411 "performLayout: Activity exiting now removed %s", activity);
412 activity.removeIfPossible();
413 }
414 }
415 }
416 }
417
418 @Override
419 int getOrientation(int candidate) {
420 if (mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
421 // Apps and their containers are not allowed to specify an orientation while using
422 // root tasks...except for the home stack if it is not resizable and currently
423 // visible (top of) its root task.
424 if (mRootHomeTask != null && mRootHomeTask.isVisible()) {
425 final Task topMost = mRootHomeTask.getTopMostTask();
426 final boolean resizable = topMost != null && topMost.isResizeable();
427 if (!(resizable && mRootHomeTask.matchParentBounds())) {
428 final int orientation = mRootHomeTask.getOrientation();
429 if (orientation != SCREEN_ORIENTATION_UNSET) {
430 return orientation;
431 }
432 }
433 }
434 return SCREEN_ORIENTATION_UNSPECIFIED;
435 }
436
437 final int orientation = super.getOrientation(candidate);
438 if (orientation != SCREEN_ORIENTATION_UNSET
439 && orientation != SCREEN_ORIENTATION_BEHIND) {
440 ProtoLog.v(WM_DEBUG_ORIENTATION,
441 "App is requesting an orientation, return %d for display id=%d",
442 orientation, mDisplayContent.mDisplayId);
443 return orientation;
444 }
445
446 ProtoLog.v(WM_DEBUG_ORIENTATION,
447 "No app is requesting an orientation, return %d for display id=%d",
448 mDisplayContent.getLastOrientation(), mDisplayContent.mDisplayId);
449 // The next app has not been requested to be visible, so we keep the current orientation
450 // to prevent freezing/unfreezing the display too early.
451 return mDisplayContent.getLastOrientation();
452 }
453
454 @Override
455 void assignChildLayers(SurfaceControl.Transaction t) {
456 assignStackOrdering(t);
457
458 for (int i = 0; i < mChildren.size(); i++) {
459 final ActivityStack s = mChildren.get(i);
460 s.assignChildLayers(t);
461 }
462 }
463
464 void assignStackOrdering(SurfaceControl.Transaction t) {
465 if (getParent() == null) {
466 return;
467 }
468 mTmpAlwaysOnTopStacks.clear();
469 mTmpHomeStacks.clear();
470 mTmpNormalStacks.clear();
471 for (int i = 0; i < mChildren.size(); ++i) {
472 final ActivityStack s = mChildren.get(i);
473 if (s.isAlwaysOnTop()) {
474 mTmpAlwaysOnTopStacks.add(s);
475 } else if (s.isActivityTypeHome()) {
476 mTmpHomeStacks.add(s);
477 } else {
478 mTmpNormalStacks.add(s);
479 }
480 }
481
482 int layer = 0;
483 // Place home stacks to the bottom.
484 for (int i = 0; i < mTmpHomeStacks.size(); i++) {
485 mTmpHomeStacks.get(i).assignLayer(t, layer++);
486 }
487 // The home animation layer is between the home stacks and the normal stacks.
488 final int layerForHomeAnimationLayer = layer++;
489 int layerForSplitScreenDividerAnchor = layer++;
490 int layerForAnimationLayer = layer++;
491 for (int i = 0; i < mTmpNormalStacks.size(); i++) {
492 final ActivityStack s = mTmpNormalStacks.get(i);
493 s.assignLayer(t, layer++);
494 if (s.inSplitScreenWindowingMode()) {
495 // The split screen divider anchor is located above the split screen window.
496 layerForSplitScreenDividerAnchor = layer++;
497 }
498 if (s.isTaskAnimating() || s.isAppTransitioning()) {
499 // The animation layer is located above the highest animating stack and no
500 // higher.
501 layerForAnimationLayer = layer++;
502 }
503 }
504 // The boosted animation layer is between the normal stacks and the always on top
505 // stacks.
506 final int layerForBoostedAnimationLayer = layer++;
507 for (int i = 0; i < mTmpAlwaysOnTopStacks.size(); i++) {
508 mTmpAlwaysOnTopStacks.get(i).assignLayer(t, layer++);
509 }
510
511 t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
512 t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
513 t.setLayer(mSplitScreenDividerAnchor, layerForSplitScreenDividerAnchor);
514 t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
515 }
516
517 @Override
518 SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
519 switch (animationLayer) {
520 case ANIMATION_LAYER_BOOSTED:
521 return mBoostedAppAnimationLayer;
522 case ANIMATION_LAYER_HOME:
523 return mHomeAppAnimationLayer;
524 case ANIMATION_LAYER_STANDARD:
525 default:
526 return mAppAnimationLayer;
527 }
528 }
529
530 SurfaceControl getSplitScreenDividerAnchor() {
531 return mSplitScreenDividerAnchor;
532 }
533
534 @Override
535 void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
536 if (getParent() != null) {
537 super.onParentChanged(newParent, oldParent, () -> {
538 mAppAnimationLayer = makeChildSurface(null)
539 .setName("animationLayer")
540 .build();
541 mBoostedAppAnimationLayer = makeChildSurface(null)
542 .setName("boostedAnimationLayer")
543 .build();
544 mHomeAppAnimationLayer = makeChildSurface(null)
545 .setName("homeAnimationLayer")
546 .build();
547 mSplitScreenDividerAnchor = makeChildSurface(null)
548 .setName("splitScreenDividerAnchor")
549 .build();
550 getPendingTransaction()
551 .show(mAppAnimationLayer)
552 .show(mBoostedAppAnimationLayer)
553 .show(mHomeAppAnimationLayer)
554 .show(mSplitScreenDividerAnchor);
555 });
556 } else {
557 super.onParentChanged(newParent, oldParent);
558 mWmService.mTransactionFactory.get()
559 .remove(mAppAnimationLayer)
560 .remove(mBoostedAppAnimationLayer)
561 .remove(mHomeAppAnimationLayer)
562 .remove(mSplitScreenDividerAnchor)
563 .apply();
564 mAppAnimationLayer = null;
565 mBoostedAppAnimationLayer = null;
566 mHomeAppAnimationLayer = null;
567 mSplitScreenDividerAnchor = null;
568 }
569 }
570}