blob: 9356ec200f206ca409cca1788392ba8040b11e07 [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;
Andrii Kulian9ea12da2020-03-27 17:16:38 -070020import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
Andrii Kulian526ad432020-03-27 12:19:51 -070021import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
Andrii Kulian9ea12da2020-03-27 17:16:38 -070022import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
23import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
24import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
25import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
Andrii Kulian526ad432020-03-27 12:19:51 -070026import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
27import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
Andrii Kulian9ea12da2020-03-27 17:16:38 -070028import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
29import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
30import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
Andrii Kulian526ad432020-03-27 12:19:51 -070031import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
32import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
33import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
Wale Ogunwaleadf116e2020-03-27 16:36:01 -070034import static android.window.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER;
Andrii Kulian526ad432020-03-27 12:19:51 -070035
Andrii Kulian9ea12da2020-03-27 17:16:38 -070036import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
37import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
38import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
39import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
40import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
41import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
42import static com.android.server.wm.DisplayContent.alwaysCreateStack;
Andrii Kulian526ad432020-03-27 12:19:51 -070043import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
44import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
Andrii Kulian9ea12da2020-03-27 17:16:38 -070045import static com.android.server.wm.RootWindowContainer.TAG_STATES;
Andrii Kulian86d676c2020-03-27 19:34:54 -070046import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
Andrii Kulian526ad432020-03-27 12:19:51 -070047import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
48
Andrii Kulian9ea12da2020-03-27 17:16:38 -070049import android.annotation.Nullable;
50import android.app.ActivityOptions;
51import android.app.WindowConfiguration;
52import android.content.Intent;
53import android.content.pm.ActivityInfo;
54import android.content.pm.ApplicationInfo;
55import android.os.UserHandle;
Andrii Kulian526ad432020-03-27 12:19:51 -070056import android.util.Slog;
57import android.view.SurfaceControl;
Andrii Kulian9ea12da2020-03-27 17:16:38 -070058import android.window.WindowContainerTransaction;
Andrii Kulian526ad432020-03-27 12:19:51 -070059
60import com.android.internal.annotations.VisibleForTesting;
61import com.android.internal.util.ToBooleanFunction;
Andrii Kulian9ea12da2020-03-27 17:16:38 -070062import com.android.internal.util.function.pooled.PooledLambda;
63import com.android.internal.util.function.pooled.PooledPredicate;
Andrii Kulian526ad432020-03-27 12:19:51 -070064import com.android.server.protolog.common.ProtoLog;
65
66import java.util.ArrayList;
67import java.util.List;
68
69/**
Andrii Kulian86d676c2020-03-27 19:34:54 -070070 * {@link DisplayArea} that represents a section of a screen that contains app window containers.
Andrii Kulian526ad432020-03-27 12:19:51 -070071 */
Andrii Kulian86d676c2020-03-27 19:34:54 -070072final class TaskDisplayArea extends DisplayArea<ActivityStack> {
73 DisplayContent mDisplayContent;
Andrii Kulian526ad432020-03-27 12:19:51 -070074 /**
75 * A control placed at the appropriate level for transitions to occur.
76 */
77 private SurfaceControl mAppAnimationLayer;
78 private SurfaceControl mBoostedAppAnimationLayer;
79 private SurfaceControl mHomeAppAnimationLayer;
80
81 /**
82 * Given that the split-screen divider does not have an AppWindowToken, it
83 * will have to live inside of a "NonAppWindowContainer". However, in visual Z order
84 * it will need to be interleaved with some of our children, appearing on top of
85 * both docked stacks but underneath any assistant stacks.
86 *
87 * To solve this problem we have this anchor control, which will always exist so
88 * we can always assign it the correct value in our {@link #assignChildLayers}.
89 * Likewise since it always exists, we can always
90 * assign the divider a layer relative to it. This way we prevent linking lifecycle
91 * events between tasks and the divider window.
92 */
93 private SurfaceControl mSplitScreenDividerAnchor;
94
95 // Cached reference to some special tasks we tend to get a lot so we don't need to loop
96 // through the list to find them.
97 private ActivityStack mRootHomeTask;
98 private ActivityStack mRootPinnedTask;
99 private ActivityStack mRootSplitScreenPrimaryTask;
100
101 private final ArrayList<ActivityStack> mTmpAlwaysOnTopStacks = new ArrayList<>();
102 private final ArrayList<ActivityStack> mTmpNormalStacks = new ArrayList<>();
103 private final ArrayList<ActivityStack> mTmpHomeStacks = new ArrayList<>();
104
Andrii Kulian9ea12da2020-03-27 17:16:38 -0700105 private ArrayList<Task> mTmpTasks = new ArrayList<>();
106
107 private ActivityTaskManagerService mAtmService;
108
109 private RootWindowContainer mRootWindowContainer;
110
111 // When non-null, new tasks get put into this root task.
Andrii Kulian86d676c2020-03-27 19:34:54 -0700112 Task mLaunchRootTask = null;
Andrii Kulian9ea12da2020-03-27 17:16:38 -0700113
114 /**
115 * A focusable stack that is purposely to be positioned at the top. Although the stack may not
116 * have the topmost index, it is used as a preferred candidate to prevent being unable to resume
117 * target stack properly when there are other focusable always-on-top stacks.
118 */
Andrii Kulian86d676c2020-03-27 19:34:54 -0700119 ActivityStack mPreferredTopFocusableStack;
Andrii Kulian9ea12da2020-03-27 17:16:38 -0700120
121 private final RootWindowContainer.FindTaskResult
122 mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
123
124 /**
125 * If this is the same as {@link #getFocusedStack} then the activity on the top of the focused
126 * stack has been resumed. If stacks are changing position this will hold the old stack until
127 * the new stack becomes resumed after which it will be set to current focused stack.
128 */
129 ActivityStack mLastFocusedStack;
130
Andrii Kulian86d676c2020-03-27 19:34:54 -0700131 TaskDisplayArea(DisplayContent displayContent, WindowManagerService service) {
Andrii Kulian526ad432020-03-27 12:19:51 -0700132 super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER);
133 mDisplayContent = displayContent;
Andrii Kulian9ea12da2020-03-27 17:16:38 -0700134 mRootWindowContainer = service.mRoot;
135 mAtmService = service.mAtmService;
Andrii Kulian526ad432020-03-27 12:19:51 -0700136 }
137
138 /**
139 * Returns the topmost stack on the display that is compatible with the input windowing mode
140 * and activity type. Null is no compatible stack on the display.
141 */
142 ActivityStack getStack(int windowingMode, int activityType) {
143 if (activityType == ACTIVITY_TYPE_HOME) {
144 return mRootHomeTask;
145 }
146 if (windowingMode == WINDOWING_MODE_PINNED) {
147 return mRootPinnedTask;
148 } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
149 return mRootSplitScreenPrimaryTask;
150 }
151 for (int i = getChildCount() - 1; i >= 0; --i) {
152 final ActivityStack stack = getChildAt(i);
153 if (activityType == ACTIVITY_TYPE_UNDEFINED
154 && windowingMode == stack.getWindowingMode()) {
155 // Passing in undefined type means we want to match the topmost stack with the
156 // windowing mode.
157 return stack;
158 }
159 if (stack.isCompatible(windowingMode, activityType)) {
160 return stack;
161 }
162 }
163 return null;
164 }
165
166 @VisibleForTesting
167 ActivityStack getTopStack() {
168 final int count = getChildCount();
169 return count > 0 ? getChildAt(count - 1) : null;
170 }
171
172 int getIndexOf(ActivityStack stack) {
173 return mChildren.indexOf(stack);
174 }
175
176 ActivityStack getRootHomeTask() {
177 return mRootHomeTask;
178 }
179
180 ActivityStack getRootPinnedTask() {
181 return mRootPinnedTask;
182 }
183
184 ActivityStack getRootSplitScreenPrimaryTask() {
185 return mRootSplitScreenPrimaryTask;
186 }
187
188 ArrayList<Task> getVisibleTasks() {
189 final ArrayList<Task> visibleTasks = new ArrayList<>();
190 forAllTasks(task -> {
191 if (task.isLeafTask() && task.isVisible()) {
192 visibleTasks.add(task);
193 }
194 });
195 return visibleTasks;
196 }
197
198 void onStackWindowingModeChanged(ActivityStack stack) {
199 removeStackReferenceIfNeeded(stack);
200 addStackReferenceIfNeeded(stack);
201 if (stack == mRootPinnedTask && getTopStack() != stack) {
202 // Looks like this stack changed windowing mode to pinned. Move it to the top.
203 positionChildAt(POSITION_TOP, stack, false /* includingParents */);
204 }
205 }
206
207 void addStackReferenceIfNeeded(ActivityStack stack) {
208 if (stack.isActivityTypeHome()) {
209 if (mRootHomeTask != null) {
210 if (!stack.isDescendantOf(mRootHomeTask)) {
211 throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
212 + mRootHomeTask + " already exist on display=" + this
213 + " stack=" + stack);
214 }
215 } else {
216 mRootHomeTask = stack;
217 }
218 }
219
220 if (!stack.isRootTask()) {
221 return;
222 }
223 final int windowingMode = stack.getWindowingMode();
224 if (windowingMode == WINDOWING_MODE_PINNED) {
225 if (mRootPinnedTask != null) {
226 throw new IllegalArgumentException(
227 "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
228 + " already exist on display=" + this + " stack=" + stack);
229 }
230 mRootPinnedTask = stack;
231 } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
232 if (mRootSplitScreenPrimaryTask != null) {
233 throw new IllegalArgumentException(
234 "addStackReferenceIfNeeded: split screen primary stack="
235 + mRootSplitScreenPrimaryTask
236 + " already exist on display=" + this + " stack=" + stack);
237 }
238 mRootSplitScreenPrimaryTask = stack;
239 }
240 }
241
242 void removeStackReferenceIfNeeded(ActivityStack stack) {
243 if (stack == mRootHomeTask) {
244 mRootHomeTask = null;
245 } else if (stack == mRootPinnedTask) {
246 mRootPinnedTask = null;
247 } else if (stack == mRootSplitScreenPrimaryTask) {
248 mRootSplitScreenPrimaryTask = null;
249 }
250 }
251
252 @Override
253 void addChild(ActivityStack stack, int position) {
254 addStackReferenceIfNeeded(stack);
255 position = findPositionForStack(position, stack, true /* adding */);
256
257 super.addChild(stack, position);
Andrii Kulian9ea12da2020-03-27 17:16:38 -0700258 mAtmService.updateSleepIfNeededLocked();
Andrii Kulian526ad432020-03-27 12:19:51 -0700259
260 // The reparenting case is handled in WindowContainer.
261 if (!stack.mReparenting) {
262 mDisplayContent.setLayoutNeeded();
263 }
264 }
265
266 @Override
267 protected void removeChild(ActivityStack stack) {
268 super.removeChild(stack);
Andrii Kulian9ea12da2020-03-27 17:16:38 -0700269 onStackRemoved(stack);
270 mAtmService.updateSleepIfNeededLocked();
Andrii Kulian526ad432020-03-27 12:19:51 -0700271 removeStackReferenceIfNeeded(stack);
272 }
273
274 @Override
275 boolean isOnTop() {
276 // Considered always on top
277 return true;
278 }
279
280 @Override
281 void positionChildAt(int position, ActivityStack child, boolean includingParents) {
282 final boolean moveToTop = (position == POSITION_TOP || position == getChildCount());
283 final boolean moveToBottom = (position == POSITION_BOTTOM || position == 0);
284 if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
285 // This stack is always-on-top, override the default behavior.
286 Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
287
288 // Moving to its current position, as we must call super but we don't want to
289 // perform any meaningful action.
290 final int currentPosition = mChildren.indexOf(child);
291 super.positionChildAt(currentPosition, child, false /* includingParents */);
292 return;
293 }
294 // We don't allow untrusted display to top when task stack moves to top,
295 // until user tapping this display to change display position as top intentionally.
296 if (mDisplayContent.isUntrustedVirtualDisplay() && !getParent().isOnTop()) {
297 includingParents = false;
298 }
299 final int targetPosition = findPositionForStack(position, child, false /* adding */);
300 super.positionChildAt(targetPosition, child, false /* includingParents */);
301
302 if (includingParents && (moveToTop || moveToBottom)) {
303 // The DisplayContent children do not re-order, but we still want to move the
304 // display of this stack container because the intention of positioning is to have
305 // higher z-order to gain focus.
306 mDisplayContent.positionDisplayAt(moveToTop ? POSITION_TOP : POSITION_BOTTOM,
307 true /* includingParents */);
308 }
309
310 child.updateTaskMovement(moveToTop);
311
312 mDisplayContent.setLayoutNeeded();
313 }
314
315 /**
316 * When stack is added or repositioned, find a proper position for it.
317 * This will make sure that pinned stack always stays on top.
318 * @param requestedPosition Position requested by caller.
319 * @param stack Stack to be added or positioned.
320 * @param adding Flag indicates whether we're adding a new stack or positioning an existing.
321 * @return The proper position for the stack.
322 */
323 private int findPositionForStack(int requestedPosition, ActivityStack stack,
324 boolean adding) {
325 if (stack.isActivityTypeDream()) {
326 return POSITION_TOP;
327 }
328
329 if (stack.inPinnedWindowingMode()) {
330 return POSITION_TOP;
331 }
332
333 final int topChildPosition = mChildren.size() - 1;
334 int belowAlwaysOnTopPosition = POSITION_BOTTOM;
335 for (int i = topChildPosition; i >= 0; --i) {
336 // Since a stack could be repositioned while being one of the child, return
337 // current index if that's the same stack we are positioning and it is always on
338 // top.
339 final boolean sameStack = mDisplayContent.getStacks().get(i) == stack;
340 if ((sameStack && stack.isAlwaysOnTop())
341 || (!sameStack && !mDisplayContent.getStacks().get(i).isAlwaysOnTop())) {
342 belowAlwaysOnTopPosition = i;
343 break;
344 }
345 }
346
347 // The max possible position we can insert the stack at.
348 int maxPosition = POSITION_TOP;
349 // The min possible position we can insert the stack at.
350 int minPosition = POSITION_BOTTOM;
351
352 if (stack.isAlwaysOnTop()) {
Andrii Kulian86d676c2020-03-27 19:34:54 -0700353 if (hasPinnedTask()) {
Andrii Kulian526ad432020-03-27 12:19:51 -0700354 // Always-on-top stacks go below the pinned stack.
355 maxPosition = mDisplayContent.getStacks().indexOf(mRootPinnedTask) - 1;
356 }
357 // Always-on-top stacks need to be above all other stacks.
358 minPosition = belowAlwaysOnTopPosition
359 != POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition;
360 } else {
361 // Other stacks need to be below the always-on-top stacks.
362 maxPosition = belowAlwaysOnTopPosition
363 != POSITION_BOTTOM ? belowAlwaysOnTopPosition : 0;
364 }
365
366 // Cap the requested position to something reasonable for the previous position check
367 // below.
368 if (requestedPosition == POSITION_TOP) {
369 requestedPosition = mChildren.size();
370 } else if (requestedPosition == POSITION_BOTTOM) {
371 requestedPosition = 0;
372 }
373
374 int targetPosition = requestedPosition;
375 targetPosition = Math.min(targetPosition, maxPosition);
376 targetPosition = Math.max(targetPosition, minPosition);
377
378 int prevPosition = mDisplayContent.getStacks().indexOf(stack);
379 // The positions we calculated above (maxPosition, minPosition) do not take into
380 // consideration the following edge cases.
381 // 1) We need to adjust the position depending on the value "adding".
382 // 2) When we are moving a stack to another position, we also need to adjust the
383 // position depending on whether the stack is moving to a higher or lower position.
384 if ((targetPosition != requestedPosition) && (adding || targetPosition < prevPosition)) {
385 targetPosition++;
386 }
387
388 return targetPosition;
389 }
390
391 @Override
392 boolean forAllWindows(ToBooleanFunction<WindowState> callback,
393 boolean traverseTopToBottom) {
394 if (traverseTopToBottom) {
395 if (super.forAllWindows(callback, traverseTopToBottom)) {
396 return true;
397 }
398 if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) {
399 return true;
400 }
401 } else {
402 if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) {
403 return true;
404 }
405 if (super.forAllWindows(callback, traverseTopToBottom)) {
406 return true;
407 }
408 }
409 return false;
410 }
411
412 private boolean forAllExitingAppTokenWindows(ToBooleanFunction<WindowState> callback,
413 boolean traverseTopToBottom) {
414 // For legacy reasons we process the TaskStack.mExitingActivities first here before the
415 // app tokens.
416 // TODO: Investigate if we need to continue to do this or if we can just process them
417 // in-order.
418 if (traverseTopToBottom) {
419 for (int i = mChildren.size() - 1; i >= 0; --i) {
420 final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
421 for (int j = activities.size() - 1; j >= 0; --j) {
422 if (activities.get(j).forAllWindowsUnchecked(callback,
423 traverseTopToBottom)) {
424 return true;
425 }
426 }
427 }
428 } else {
429 final int count = mChildren.size();
430 for (int i = 0; i < count; ++i) {
431 final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
432 final int appTokensCount = activities.size();
433 for (int j = 0; j < appTokensCount; j++) {
434 if (activities.get(j).forAllWindowsUnchecked(callback,
435 traverseTopToBottom)) {
436 return true;
437 }
438 }
439 }
440 }
441 return false;
442 }
443
444 void setExitingTokensHasVisible(boolean hasVisible) {
445 for (int i = mChildren.size() - 1; i >= 0; --i) {
446 final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
447 for (int j = activities.size() - 1; j >= 0; --j) {
448 activities.get(j).hasVisible = hasVisible;
449 }
450 }
451 }
452
453 void removeExistingAppTokensIfPossible() {
454 for (int i = mChildren.size() - 1; i >= 0; --i) {
455 final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
456 for (int j = activities.size() - 1; j >= 0; --j) {
457 final ActivityRecord activity = activities.get(j);
458 if (!activity.hasVisible && !mDisplayContent.mClosingApps.contains(activity)
459 && (!activity.mIsExiting || activity.isEmpty())) {
460 // Make sure there is no animation running on this activity, so any windows
461 // associated with it will be removed as soon as their animations are
462 // complete.
463 cancelAnimation();
464 ProtoLog.v(WM_DEBUG_ADD_REMOVE,
465 "performLayout: Activity exiting now removed %s", activity);
466 activity.removeIfPossible();
467 }
468 }
469 }
470 }
471
472 @Override
473 int getOrientation(int candidate) {
474 if (mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
475 // Apps and their containers are not allowed to specify an orientation while using
476 // root tasks...except for the home stack if it is not resizable and currently
477 // visible (top of) its root task.
478 if (mRootHomeTask != null && mRootHomeTask.isVisible()) {
479 final Task topMost = mRootHomeTask.getTopMostTask();
480 final boolean resizable = topMost != null && topMost.isResizeable();
481 if (!(resizable && mRootHomeTask.matchParentBounds())) {
482 final int orientation = mRootHomeTask.getOrientation();
483 if (orientation != SCREEN_ORIENTATION_UNSET) {
484 return orientation;
485 }
486 }
487 }
488 return SCREEN_ORIENTATION_UNSPECIFIED;
489 }
490
491 final int orientation = super.getOrientation(candidate);
492 if (orientation != SCREEN_ORIENTATION_UNSET
493 && orientation != SCREEN_ORIENTATION_BEHIND) {
494 ProtoLog.v(WM_DEBUG_ORIENTATION,
495 "App is requesting an orientation, return %d for display id=%d",
496 orientation, mDisplayContent.mDisplayId);
497 return orientation;
498 }
499
500 ProtoLog.v(WM_DEBUG_ORIENTATION,
501 "No app is requesting an orientation, return %d for display id=%d",
502 mDisplayContent.getLastOrientation(), mDisplayContent.mDisplayId);
503 // The next app has not been requested to be visible, so we keep the current orientation
504 // to prevent freezing/unfreezing the display too early.
505 return mDisplayContent.getLastOrientation();
506 }
507
508 @Override
509 void assignChildLayers(SurfaceControl.Transaction t) {
510 assignStackOrdering(t);
511
512 for (int i = 0; i < mChildren.size(); i++) {
513 final ActivityStack s = mChildren.get(i);
514 s.assignChildLayers(t);
515 }
516 }
517
518 void assignStackOrdering(SurfaceControl.Transaction t) {
519 if (getParent() == null) {
520 return;
521 }
522 mTmpAlwaysOnTopStacks.clear();
523 mTmpHomeStacks.clear();
524 mTmpNormalStacks.clear();
525 for (int i = 0; i < mChildren.size(); ++i) {
526 final ActivityStack s = mChildren.get(i);
527 if (s.isAlwaysOnTop()) {
528 mTmpAlwaysOnTopStacks.add(s);
529 } else if (s.isActivityTypeHome()) {
530 mTmpHomeStacks.add(s);
531 } else {
532 mTmpNormalStacks.add(s);
533 }
534 }
535
536 int layer = 0;
537 // Place home stacks to the bottom.
538 for (int i = 0; i < mTmpHomeStacks.size(); i++) {
539 mTmpHomeStacks.get(i).assignLayer(t, layer++);
540 }
541 // The home animation layer is between the home stacks and the normal stacks.
542 final int layerForHomeAnimationLayer = layer++;
543 int layerForSplitScreenDividerAnchor = layer++;
544 int layerForAnimationLayer = layer++;
545 for (int i = 0; i < mTmpNormalStacks.size(); i++) {
546 final ActivityStack s = mTmpNormalStacks.get(i);
547 s.assignLayer(t, layer++);
548 if (s.inSplitScreenWindowingMode()) {
549 // The split screen divider anchor is located above the split screen window.
550 layerForSplitScreenDividerAnchor = layer++;
551 }
552 if (s.isTaskAnimating() || s.isAppTransitioning()) {
553 // The animation layer is located above the highest animating stack and no
554 // higher.
555 layerForAnimationLayer = layer++;
556 }
557 }
558 // The boosted animation layer is between the normal stacks and the always on top
559 // stacks.
560 final int layerForBoostedAnimationLayer = layer++;
561 for (int i = 0; i < mTmpAlwaysOnTopStacks.size(); i++) {
562 mTmpAlwaysOnTopStacks.get(i).assignLayer(t, layer++);
563 }
564
565 t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
566 t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
567 t.setLayer(mSplitScreenDividerAnchor, layerForSplitScreenDividerAnchor);
568 t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
569 }
570
571 @Override
572 SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
573 switch (animationLayer) {
574 case ANIMATION_LAYER_BOOSTED:
575 return mBoostedAppAnimationLayer;
576 case ANIMATION_LAYER_HOME:
577 return mHomeAppAnimationLayer;
578 case ANIMATION_LAYER_STANDARD:
579 default:
580 return mAppAnimationLayer;
581 }
582 }
583
584 SurfaceControl getSplitScreenDividerAnchor() {
585 return mSplitScreenDividerAnchor;
586 }
587
588 @Override
589 void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
590 if (getParent() != null) {
591 super.onParentChanged(newParent, oldParent, () -> {
592 mAppAnimationLayer = makeChildSurface(null)
593 .setName("animationLayer")
594 .build();
595 mBoostedAppAnimationLayer = makeChildSurface(null)
596 .setName("boostedAnimationLayer")
597 .build();
598 mHomeAppAnimationLayer = makeChildSurface(null)
599 .setName("homeAnimationLayer")
600 .build();
601 mSplitScreenDividerAnchor = makeChildSurface(null)
602 .setName("splitScreenDividerAnchor")
603 .build();
604 getPendingTransaction()
605 .show(mAppAnimationLayer)
606 .show(mBoostedAppAnimationLayer)
607 .show(mHomeAppAnimationLayer)
608 .show(mSplitScreenDividerAnchor);
609 });
610 } else {
611 super.onParentChanged(newParent, oldParent);
612 mWmService.mTransactionFactory.get()
613 .remove(mAppAnimationLayer)
614 .remove(mBoostedAppAnimationLayer)
615 .remove(mHomeAppAnimationLayer)
616 .remove(mSplitScreenDividerAnchor)
617 .apply();
618 mAppAnimationLayer = null;
619 mBoostedAppAnimationLayer = null;
620 mHomeAppAnimationLayer = null;
621 mSplitScreenDividerAnchor = null;
622 }
623 }
Andrii Kulian9ea12da2020-03-27 17:16:38 -0700624
625 void addStack(ActivityStack stack, int position) {
Andrii Kulian86d676c2020-03-27 19:34:54 -0700626 if (DEBUG_STACK) Slog.d(TAG_WM, "Set stack=" + stack + " on taskDisplayArea=" + this);
627 addChild(stack, position);
Andrii Kulian9ea12da2020-03-27 17:16:38 -0700628 positionStackAt(stack, position);
629 }
630
631 void onStackRemoved(ActivityStack stack) {
632 if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
633 Slog.v(TAG_STACK, "removeStack: detaching " + stack + " from displayId="
634 + mDisplayContent.mDisplayId);
635 }
636 if (mPreferredTopFocusableStack == stack) {
637 mPreferredTopFocusableStack = null;
638 }
639 mDisplayContent.releaseSelfIfNeeded();
640 mDisplayContent.onStackOrderChanged(stack);
641 }
642
643 void positionStackAt(int position, ActivityStack child, boolean includingParents) {
644 positionChildAt(position, child, includingParents);
645 mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
646 }
647
648 void positionStackAtTop(ActivityStack stack, boolean includingParents) {
649 positionStackAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
650 }
651
652 void positionStackAtTop(ActivityStack stack, boolean includingParents,
653 String updateLastFocusedStackReason) {
654 positionStackAt(stack, getStackCount(), includingParents,
655 updateLastFocusedStackReason);
656 }
657
658 void positionStackAtBottom(ActivityStack stack) {
659 positionStackAtBottom(stack, null /* updateLastFocusedStackReason */);
660 }
661
662 void positionStackAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
663 positionStackAt(stack, 0, false /* includingParents */,
664 updateLastFocusedStackReason);
665 }
666
667 void positionStackAt(ActivityStack stack, int position) {
668 positionStackAt(stack, position, false /* includingParents */,
669 null /* updateLastFocusedStackReason */);
670 }
671
672 void positionStackAt(ActivityStack stack, int position, boolean includingParents,
673 String updateLastFocusedStackReason) {
674 // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
675 // the position internally, also update the logic here
676 final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
677 ? getFocusedStack() : null;
678 final boolean wasContained = getIndexOf(stack) >= 0;
679 if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1 && !wasContained) {
680 throw new IllegalStateException(
681 "positionStackAt: Can only have one task on display=" + this);
682 }
683
684 final boolean movingToTop = wasContained && position >= getStackCount() - 1;
685 // Reset mPreferredTopFocusableStack before positioning to top or {@link
686 // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
687 // resumed activity.
688 if (movingToTop && stack.isFocusable()) {
689 mPreferredTopFocusableStack = null;
690 }
691
692 // Since positionChildAt() is called during the creation process of pinned stacks,
693 // ActivityStack#getStack() can be null.
694 positionStackAt(position, stack, includingParents);
695
696 // The insert position may be adjusted to non-top when there is always-on-top stack. Since
697 // the original position is preferred to be top, the stack should have higher priority when
698 // we are looking for top focusable stack. The condition {@code wasContained} restricts the
699 // preferred stack is set only when moving an existing stack to top instead of adding a new
700 // stack that may be too early (e.g. in the middle of launching or reparenting).
701 if (movingToTop && stack.isFocusableAndVisible()) {
702 mPreferredTopFocusableStack = stack;
703 } else if (mPreferredTopFocusableStack == stack) {
704 mPreferredTopFocusableStack = null;
705 }
706
707 if (updateLastFocusedStackReason != null) {
708 final ActivityStack currentFocusedStack = getFocusedStack();
709 if (currentFocusedStack != prevFocusedStack) {
710 mLastFocusedStack = prevFocusedStack;
711 EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
712 mDisplayContent.mDisplayId,
713 currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
714 mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
715 updateLastFocusedStackReason);
716 }
717 }
718
719 mDisplayContent.onStackOrderChanged(stack);
720 }
721
722 ActivityStack getStack(int rootTaskId) {
723 for (int i = getStackCount() - 1; i >= 0; --i) {
724 final ActivityStack stack = getStackAt(i);
725 if (stack.getRootTaskId() == rootTaskId) {
726 return stack;
727 }
728 }
729 return null;
730 }
731
732 /**
733 * Returns an existing stack compatible with the windowing mode and activity type or creates one
734 * if a compatible stack doesn't exist.
735 * @see #getOrCreateStack(int, int, boolean, Intent, Task, boolean)
736 */
737 ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop) {
738 return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
739 null /* candidateTask */, false /* createdByOrganizer */);
740 }
741
742 /**
743 * When two level tasks are required for given windowing mode and activity type, returns an
744 * existing compatible root task or creates a new one.
745 * For one level task, the candidate task would be reused to also be the root task or create
746 * a new root task if no candidate task.
747 * @see #getStack(int, int)
748 * @see #createStack(int, int, boolean)
749 */
750 ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop,
751 Intent intent, Task candidateTask, boolean createdByOrganizer) {
752 if (!alwaysCreateStack(windowingMode, activityType)) {
753 ActivityStack stack = getStack(windowingMode, activityType);
754 if (stack != null) {
755 return stack;
756 }
757 } else if (candidateTask != null) {
758 final ActivityStack stack = (ActivityStack) candidateTask;
759 final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
760 if (isSplitScreenModeActivated()) {
761 final Task splitRootSecondary = getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
762 && t.inSplitScreenSecondaryWindowingMode());
763 if (stack.getParent() == null) {
764 splitRootSecondary.addChild(stack, position);
765 } else if (stack.getParent() != splitRootSecondary) {
766 stack.reparent(splitRootSecondary, position);
767 }
768 } else if (stack.getDisplay() != mDisplayContent || !stack.isRootTask()) {
769 if (stack.getParent() == null) {
770 addStack(stack, position);
771 } else {
772 stack.reparent(mDisplayContent, onTop);
773 }
774 }
775 // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen.
776 if (candidateTask.getWindowingMode() != windowingMode) {
777 candidateTask.setWindowingMode(windowingMode);
778 }
779 return stack;
780 }
781 return createStack(windowingMode, activityType, onTop, null /*info*/, intent,
782 createdByOrganizer);
783 }
784
785 /**
786 * Returns an existing stack compatible with the input params or creates one
787 * if a compatible stack doesn't exist.
788 * @see #getOrCreateStack(int, int, boolean)
789 */
790 ActivityStack getOrCreateStack(@Nullable ActivityRecord r,
791 @Nullable ActivityOptions options, @Nullable Task candidateTask, int activityType,
792 boolean onTop) {
793 // First preference is the windowing mode in the activity options if set.
794 int windowingMode = (options != null)
795 ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
796 // Validate that our desired windowingMode will work under the current conditions.
797 // UNDEFINED windowing mode is a valid result and means that the new stack will inherit
798 // it's display's windowing mode.
799 windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
800 return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
801 candidateTask, false /* createdByOrganizer */);
802 }
803
804 @VisibleForTesting
805 int getNextStackId() {
806 return mAtmService.mStackSupervisor.getNextTaskIdForUser();
807 }
808
809 ActivityStack createStack(int windowingMode, int activityType, boolean onTop) {
810 return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */,
811 false /* createdByOrganizer */);
812 }
813
814 /**
815 * Creates a stack matching the input windowing mode and activity type on this display.
816 * @param windowingMode The windowing mode the stack should be created in. If
817 * {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will
818 * inherit its parent's windowing mode.
819 * @param activityType The activityType the stack should be created in. If
820 * {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
821 * be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
822 * @param onTop If true the stack will be created at the top of the display, else at the bottom.
823 * @param info The started activity info.
824 * @param intent The intent that started this task.
825 * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false}
826 * otherwise.
827 * @return The newly created stack.
828 */
829 ActivityStack createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
830 Intent intent, boolean createdByOrganizer) {
831 if (mDisplayContent.mSingleTaskInstance && getStackCount() > 0) {
832 // Create stack on default display instead since this display can only contain 1 stack.
833 // TODO: Kinda a hack, but better that having the decision at each call point. Hoping
834 // this goes away once ActivityView is no longer using virtual displays.
835 return mRootWindowContainer.getDefaultDisplay().mTaskContainers.createStack(
836 windowingMode, activityType, onTop, info, intent, createdByOrganizer);
837 }
838
839 if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
840 // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
841 // anything else should be passing it in anyways...except for the task organizer.
842 activityType = ACTIVITY_TYPE_STANDARD;
843 }
844
845 if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) {
846 // For now there can be only one stack of a particular non-standard activity type on a
847 // display. So, get that ignoring whatever windowing mode it is currently in.
848 ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
849 if (stack != null) {
850 throw new IllegalArgumentException("Stack=" + stack + " of activityType="
851 + activityType + " already on display=" + this + ". Can't have multiple.");
852 }
853 }
854
855 if (!isWindowingModeSupported(windowingMode, mAtmService.mSupportsMultiWindow,
856 mAtmService.mSupportsSplitScreenMultiWindow,
857 mAtmService.mSupportsFreeformWindowManagement,
858 mAtmService.mSupportsPictureInPicture, activityType)) {
859 throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
860 + windowingMode);
861 }
862
863 final int stackId = getNextStackId();
864 return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
865 createdByOrganizer);
866 }
867
868 /** @return the root task to create the next task in. */
869 private Task updateLaunchRootTask(int windowingMode) {
870 if (!isSplitScreenWindowingMode(windowingMode)) {
871 // Only split-screen windowing modes can do this currently...
872 return null;
873 }
874 for (int i = getStackCount() - 1; i >= 0; --i) {
875 final Task t = getStackAt(i);
876 if (!t.mCreatedByOrganizer || t.getRequestedOverrideWindowingMode() != windowingMode) {
877 continue;
878 }
879 // If not already set, pick a launch root which is not the one we are launching into.
880 if (mLaunchRootTask == null) {
881 for (int j = 0, n = getStackCount(); j < n; ++j) {
882 final Task tt = getStackAt(j);
883 if (tt.mCreatedByOrganizer && tt != t) {
884 mLaunchRootTask = tt;
885 break;
886 }
887 }
888 }
889 return t;
890 }
891 return mLaunchRootTask;
892 }
893
894 @VisibleForTesting
895 ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId,
896 boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {
897 if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
898 throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
899 + "activity type.");
900 }
901 if (info == null) {
902 info = new ActivityInfo();
903 info.applicationInfo = new ApplicationInfo();
904 }
905
906 // Task created by organizer are added as root.
907 Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode);
908 if (launchRootTask != null) {
909 // Since this stack will be put into a root task, its windowingMode will be inherited.
910 windowingMode = WINDOWING_MODE_UNDEFINED;
911 }
912
913 final ActivityStack stack = (ActivityStack) Task.create(mAtmService, stackId, activityType,
914 info, intent, createdByOrganizer);
915 if (launchRootTask != null) {
916 launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
917 if (onTop) {
918 positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */);
919 }
920 } else {
921 addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
922 stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
923 false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
924 true /* creating */);
925 }
926 return stack;
927 }
928
929 /**
930 * Get the preferred focusable stack in priority. If the preferred stack does not exist, find a
931 * focusable and visible stack from the top of stacks in this display.
932 */
933 ActivityStack getFocusedStack() {
934 if (mPreferredTopFocusableStack != null) {
935 return mPreferredTopFocusableStack;
936 }
937
938 for (int i = getStackCount() - 1; i >= 0; --i) {
939 final ActivityStack stack = getStackAt(i);
940 if (stack.isFocusableAndVisible()) {
941 return stack;
942 }
943 }
944
945 return null;
946 }
947
948 ActivityStack getNextFocusableStack(ActivityStack currentFocus, boolean ignoreCurrent) {
949 final int currentWindowingMode = currentFocus != null
950 ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
951
952 ActivityStack candidate = null;
953 for (int i = getStackCount() - 1; i >= 0; --i) {
954 final ActivityStack stack = getStackAt(i);
955 if (ignoreCurrent && stack == currentFocus) {
956 continue;
957 }
958 if (!stack.isFocusableAndVisible()) {
959 continue;
960 }
961
962 if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
963 && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) {
964 // If the currently focused stack is in split-screen secondary we save off the
965 // top primary split-screen stack as a candidate for focus because we might
966 // prefer focus to move to an other stack to avoid primary split-screen stack
967 // overlapping with a fullscreen stack when a fullscreen stack is higher in z
968 // than the next split-screen stack. Assistant stack, I am looking at you...
969 // We only move the focus to the primary-split screen stack if there isn't a
970 // better alternative.
971 candidate = stack;
972 continue;
973 }
974 if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) {
975 // Use the candidate stack since we are now at the secondary split-screen.
976 return candidate;
977 }
978 return stack;
979 }
980 return candidate;
981 }
982
983 ActivityRecord getResumedActivity() {
984 final ActivityStack focusedStack = getFocusedStack();
985 if (focusedStack == null) {
986 return null;
987 }
988 // TODO(b/111541062): Move this into ActivityStack#getResumedActivity()
989 // Check if the focused stack has the resumed activity
990 ActivityRecord resumedActivity = focusedStack.getResumedActivity();
991 if (resumedActivity == null || resumedActivity.app == null) {
992 // If there is no registered resumed activity in the stack or it is not running -
993 // try to use previously resumed one.
994 resumedActivity = focusedStack.mPausingActivity;
995 if (resumedActivity == null || resumedActivity.app == null) {
996 // If previously resumed activity doesn't work either - find the topmost running
997 // activity that can be focused.
998 resumedActivity = focusedStack.topRunningActivity(true /* focusableOnly */);
999 }
1000 }
1001 return resumedActivity;
1002 }
1003
1004 ActivityStack getLastFocusedStack() {
1005 return mLastFocusedStack;
1006 }
1007
1008 boolean allResumedActivitiesComplete() {
1009 for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
1010 final ActivityRecord r = getStackAt(stackNdx).getResumedActivity();
1011 if (r != null && !r.isState(RESUMED)) {
1012 return false;
1013 }
1014 }
1015 final ActivityStack currentFocusedStack = getFocusedStack();
1016 if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
1017 Slog.d(TAG_STACK, "allResumedActivitiesComplete: mLastFocusedStack changing from="
1018 + mLastFocusedStack + " to=" + currentFocusedStack);
1019 }
1020 mLastFocusedStack = currentFocusedStack;
1021 return true;
1022 }
1023
1024 /**
1025 * Pause all activities in either all of the stacks or just the back stacks. This is done before
1026 * resuming a new activity and to make sure that previously active activities are
1027 * paused in stacks that are no longer visible or in pinned windowing mode. This does not
1028 * pause activities in visible stacks, so if an activity is launched within the same stack/task,
1029 * then we should explicitly pause that stack's top activity.
1030 * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
1031 * @param resuming The resuming activity.
1032 * @return {@code true} if any activity was paused as a result of this call.
1033 */
1034 boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming) {
1035 boolean someActivityPaused = false;
1036 for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
1037 final ActivityStack stack = getStackAt(stackNdx);
1038 final ActivityRecord resumedActivity = stack.getResumedActivity();
1039 if (resumedActivity != null
1040 && (stack.getVisibility(resuming) != STACK_VISIBILITY_VISIBLE
1041 || !stack.isTopActivityFocusable())) {
1042 if (DEBUG_STATES) {
1043 Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack
1044 + " mResumedActivity=" + resumedActivity);
1045 }
1046 someActivityPaused |= stack.startPausingLocked(userLeaving, false /* uiSleeping*/,
1047 resuming);
1048 }
1049 }
1050 return someActivityPaused;
1051 }
1052
1053 /**
1054 * Find task for putting the Activity in.
1055 */
1056 void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplay,
1057 RootWindowContainer.FindTaskResult result) {
1058 mTmpFindTaskResult.clear();
1059 for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
1060 final ActivityStack stack = getStackAt(stackNdx);
1061 if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) {
1062 if (DEBUG_TASKS) {
1063 Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
1064 }
1065 continue;
1066 }
1067
1068 mTmpFindTaskResult.process(r, stack);
1069 // It is possible to have tasks in multiple stacks with the same root affinity, so
1070 // we should keep looking after finding an affinity match to see if there is a
1071 // better match in another stack. Also, task affinity isn't a good enough reason
1072 // to target a display which isn't the source of the intent, so skip any affinity
1073 // matches not on the specified display.
1074 if (mTmpFindTaskResult.mRecord != null) {
1075 if (mTmpFindTaskResult.mIdealMatch) {
1076 result.setTo(mTmpFindTaskResult);
1077 return;
1078 } else if (isPreferredDisplay) {
1079 // Note: since the traversing through the stacks is top down, the floating
1080 // tasks should always have lower priority than any affinity-matching tasks
1081 // in the fullscreen stacks
1082 result.setTo(mTmpFindTaskResult);
1083 }
1084 }
1085 }
1086 }
1087
1088 /**
1089 * Removes stacks in the input windowing modes from the system if they are of activity type
1090 * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
1091 */
1092 void removeStacksInWindowingModes(int... windowingModes) {
1093 if (windowingModes == null || windowingModes.length == 0) {
1094 return;
1095 }
1096
1097 // Collect the stacks that are necessary to be removed instead of performing the removal
1098 // by looping mStacks, so that we don't miss any stacks after the stack size changed or
1099 // stacks reordered.
1100 final ArrayList<ActivityStack> stacks = new ArrayList<>();
1101 for (int j = windowingModes.length - 1; j >= 0; --j) {
1102 final int windowingMode = windowingModes[j];
1103 for (int i = getStackCount() - 1; i >= 0; --i) {
1104 final ActivityStack stack = getStackAt(i);
1105 if (!stack.isActivityTypeStandardOrUndefined()) {
1106 continue;
1107 }
1108 if (stack.getWindowingMode() != windowingMode) {
1109 continue;
1110 }
1111 stacks.add(stack);
1112 }
1113 }
1114
1115 for (int i = stacks.size() - 1; i >= 0; --i) {
1116 mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i));
1117 }
1118 }
1119
1120 void removeStacksWithActivityTypes(int... activityTypes) {
1121 if (activityTypes == null || activityTypes.length == 0) {
1122 return;
1123 }
1124
1125 // Collect the stacks that are necessary to be removed instead of performing the removal
1126 // by looping mStacks, so that we don't miss any stacks after the stack size changed or
1127 // stacks reordered.
1128 final ArrayList<ActivityStack> stacks = new ArrayList<>();
1129 for (int j = activityTypes.length - 1; j >= 0; --j) {
1130 final int activityType = activityTypes[j];
1131 for (int i = getStackCount() - 1; i >= 0; --i) {
1132 final ActivityStack stack = getStackAt(i);
1133 // Collect the root tasks that are currently being organized.
1134 if (stack.isOrganized()) {
1135 for (int k = stack.getChildCount() - 1; k >= 0; --k) {
1136 final ActivityStack childStack = (ActivityStack) stack.getChildAt(k);
1137 if (childStack.getActivityType() == activityType) {
1138 stacks.add(childStack);
1139 }
1140 }
1141 } else if (stack.getActivityType() == activityType) {
1142 stacks.add(stack);
1143 }
1144 }
1145 }
1146
1147 for (int i = stacks.size() - 1; i >= 0; --i) {
1148 mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i));
1149 }
1150 }
1151
1152 void onSplitScreenModeDismissed() {
1153 mAtmService.deferWindowLayout();
1154 try {
1155 mLaunchRootTask = null;
1156 moveSplitScreenTasksToFullScreen();
1157 } finally {
1158 final ActivityStack topFullscreenStack =
1159 getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
1160 final ActivityStack homeStack = getOrCreateRootHomeTask();
1161 if (topFullscreenStack != null && homeStack != null && !isTopStack(homeStack)) {
1162 // Whenever split-screen is dismissed we want the home stack directly behind the
1163 // current top fullscreen stack so it shows up when the top stack is finished.
1164 // TODO: Would be better to use ActivityDisplay.positionChildAt() for this, however
1165 // ActivityDisplay doesn't have a direct controller to WM side yet. We can switch
1166 // once we have that.
1167 homeStack.moveToFront("onSplitScreenModeDismissed");
1168 topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
1169 }
1170 mAtmService.continueWindowLayout();
1171 }
1172 }
1173
1174 private void moveSplitScreenTasksToFullScreen() {
1175 final WindowContainerTransaction wct = new WindowContainerTransaction();
1176 mTmpTasks.clear();
1177 forAllTasks(task -> {
1178 if (task.mCreatedByOrganizer && task.inSplitScreenWindowingMode() && task.hasChild()) {
1179 mTmpTasks.add(task);
1180 }
1181 });
1182
1183 for (int i = mTmpTasks.size() - 1; i >= 0; i--) {
1184 final Task root = mTmpTasks.get(i);
1185 for (int j = 0; j < root.getChildCount(); j++) {
Wale Ogunwaleadf116e2020-03-27 16:36:01 -07001186 wct.reparent(root.getChildAt(j).mRemoteToken.toWindowContainerToken(),
1187 null, true /* toTop */);
Andrii Kulian9ea12da2020-03-27 17:16:38 -07001188 }
1189 }
1190 mAtmService.mWindowOrganizerController.applyTransaction(wct);
1191 }
1192
1193 /**
1194 * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
1195 * @param windowingMode The windowing mode we are checking support for.
1196 * @param supportsMultiWindow If we should consider support for multi-window mode in general.
1197 * @param supportsSplitScreen If we should consider support for split-screen multi-window.
1198 * @param supportsFreeform If we should consider support for freeform multi-window.
1199 * @param supportsPip If we should consider support for picture-in-picture mutli-window.
1200 * @param activityType The activity type under consideration.
1201 * @return true if the windowing mode is supported.
1202 */
1203 private boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
1204 boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
1205 int activityType) {
1206
1207 if (windowingMode == WINDOWING_MODE_UNDEFINED
1208 || windowingMode == WINDOWING_MODE_FULLSCREEN) {
1209 return true;
1210 }
1211 if (!supportsMultiWindow) {
1212 return false;
1213 }
1214
1215 if (windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
1216 return true;
1217 }
1218
1219 final int displayWindowingMode = getWindowingMode();
1220 if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
1221 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
1222 return supportsSplitScreen
1223 && WindowConfiguration.supportSplitScreenWindowingMode(activityType)
1224 // Freeform windows and split-screen windows don't mix well, so prevent
1225 // split windowing modes on freeform displays.
1226 && displayWindowingMode != WINDOWING_MODE_FREEFORM;
1227 }
1228
1229 if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
1230 return false;
1231 }
1232
1233 if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
1234 return false;
1235 }
1236 return true;
1237 }
1238
1239 /**
1240 * Resolves the windowing mode that an {@link ActivityRecord} would be in if started on this
1241 * display with the provided parameters.
1242 *
1243 * @param r The ActivityRecord in question.
1244 * @param options Options to start with.
1245 * @param task The task within-which the activity would start.
1246 * @param activityType The type of activity to start.
1247 * @return The resolved (not UNDEFINED) windowing-mode that the activity would be in.
1248 */
1249 int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
1250 @Nullable Task task, int activityType) {
1251
1252 // First preference if the windowing mode in the activity options if set.
1253 int windowingMode = (options != null)
1254 ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
1255
1256 // If windowing mode is unset, then next preference is the candidate task, then the
1257 // activity record.
1258 if (windowingMode == WINDOWING_MODE_UNDEFINED) {
1259 if (task != null) {
1260 windowingMode = task.getWindowingMode();
1261 }
1262 if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) {
1263 windowingMode = r.getWindowingMode();
1264 }
1265 if (windowingMode == WINDOWING_MODE_UNDEFINED) {
1266 // Use the display's windowing mode.
1267 windowingMode = getWindowingMode();
1268 }
1269 }
1270 windowingMode = validateWindowingMode(windowingMode, r, task, activityType);
1271 return windowingMode != WINDOWING_MODE_UNDEFINED
1272 ? windowingMode : WINDOWING_MODE_FULLSCREEN;
1273 }
1274
1275 /**
1276 * Check that the requested windowing-mode is appropriate for the specified task and/or activity
1277 * on this display.
1278 *
1279 * @param windowingMode The windowing-mode to validate.
1280 * @param r The {@link ActivityRecord} to check against.
1281 * @param task The {@link Task} to check against.
1282 * @param activityType An activity type.
1283 * @return The provided windowingMode or the closest valid mode which is appropriate.
1284 */
1285 int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r, @Nullable Task task,
1286 int activityType) {
1287 // Make sure the windowing mode we are trying to use makes sense for what is supported.
1288 boolean supportsMultiWindow = mAtmService.mSupportsMultiWindow;
1289 boolean supportsSplitScreen = mAtmService.mSupportsSplitScreenMultiWindow;
1290 boolean supportsFreeform = mAtmService.mSupportsFreeformWindowManagement;
1291 boolean supportsPip = mAtmService.mSupportsPictureInPicture;
1292 if (supportsMultiWindow) {
1293 if (task != null) {
1294 supportsMultiWindow = task.isResizeable();
1295 supportsSplitScreen = task.supportsSplitScreenWindowingMode();
1296 // TODO: Do we need to check for freeform and Pip support here?
1297 } else if (r != null) {
1298 supportsMultiWindow = r.isResizeable();
1299 supportsSplitScreen = r.supportsSplitScreenWindowingMode();
1300 supportsFreeform = r.supportsFreeform();
1301 supportsPip = r.supportsPictureInPicture();
1302 }
1303 }
1304
1305 final boolean inSplitScreenMode = isSplitScreenModeActivated();
1306 if (!inSplitScreenMode
1307 && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
1308 // Switch to the display's windowing mode if we are not in split-screen mode and we are
1309 // trying to launch in split-screen secondary.
1310 windowingMode = WINDOWING_MODE_UNDEFINED;
1311 } else if (inSplitScreenMode && (windowingMode == WINDOWING_MODE_FULLSCREEN
1312 || windowingMode == WINDOWING_MODE_UNDEFINED)
1313 && supportsSplitScreen) {
1314 windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
1315 }
1316
1317 if (windowingMode != WINDOWING_MODE_UNDEFINED
1318 && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
1319 supportsFreeform, supportsPip, activityType)) {
1320 return windowingMode;
1321 }
1322 return WINDOWING_MODE_UNDEFINED;
1323 }
1324
1325 boolean isTopStack(ActivityStack stack) {
1326 return stack == getTopStack();
1327 }
1328
1329 boolean isTopNotPinnedStack(ActivityStack stack) {
1330 for (int i = getStackCount() - 1; i >= 0; --i) {
1331 final ActivityStack current = getStackAt(i);
1332 if (!current.inPinnedWindowingMode()) {
1333 return current == stack;
1334 }
1335 }
1336 return false;
1337 }
1338
Andrii Kulian86d676c2020-03-27 19:34:54 -07001339 ActivityRecord topRunningActivity() {
1340 return topRunningActivity(false /* considerKeyguardState */);
1341 }
1342
Andrii Kulian9ea12da2020-03-27 17:16:38 -07001343 /**
1344 * Returns the top running activity in the focused stack. In the case the focused stack has no
1345 * such activity, the next focusable stack on this display is returned.
1346 *
1347 * @param considerKeyguardState Indicates whether the locked state should be considered. if
1348 * {@code true} and the keyguard is locked, only activities that
1349 * can be shown on top of the keyguard will be considered.
1350 * @return The top running activity. {@code null} if none is available.
1351 */
1352 ActivityRecord topRunningActivity(boolean considerKeyguardState) {
1353 ActivityRecord topRunning = null;
1354 final ActivityStack focusedStack = getFocusedStack();
1355 if (focusedStack != null) {
1356 topRunning = focusedStack.topRunningActivity();
1357 }
1358
1359 // Look in other focusable stacks.
1360 if (topRunning == null) {
1361 for (int i = getStackCount() - 1; i >= 0; --i) {
1362 final ActivityStack stack = getStackAt(i);
1363 // Only consider focusable stacks other than the current focused one.
1364 if (stack == focusedStack || !stack.isTopActivityFocusable()) {
1365 continue;
1366 }
1367 topRunning = stack.topRunningActivity();
1368 if (topRunning != null) {
1369 break;
1370 }
1371 }
1372 }
1373
1374 // This activity can be considered the top running activity if we are not considering
1375 // the locked state, the keyguard isn't locked, or we can show when locked.
1376 if (topRunning != null && considerKeyguardState
1377 && mRootWindowContainer.mStackSupervisor.getKeyguardController()
1378 .isKeyguardLocked()
1379 && !topRunning.canShowWhenLocked()) {
1380 return null;
1381 }
1382
1383 return topRunning;
1384 }
1385
1386 protected int getStackCount() {
1387 return mChildren.size();
1388 }
1389
1390 protected ActivityStack getStackAt(int index) {
1391 return mChildren.get(index);
1392 }
1393
1394 /**
1395 * Returns the existing home stack or creates and returns a new one if it should exist for the
1396 * display.
1397 */
1398 @Nullable
1399 ActivityStack getOrCreateRootHomeTask() {
1400 ActivityStack homeTask = getRootHomeTask();
1401 if (homeTask == null && mDisplayContent.supportsSystemDecorations()
1402 && !mDisplayContent.isUntrustedVirtualDisplay()) {
1403 homeTask = createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME,
1404 false /* onTop */);
1405 }
1406 return homeTask;
1407 }
1408
1409 boolean isSplitScreenModeActivated() {
1410 Task task = getRootSplitScreenPrimaryTask();
1411 return task != null && task.hasChild();
1412 }
1413
1414 /**
1415 * Returns the topmost stack on the display that is compatible with the input windowing mode.
1416 * Null is no compatible stack on the display.
1417 */
1418 ActivityStack getTopStackInWindowingMode(int windowingMode) {
1419 return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED);
1420 }
1421
1422 void moveHomeStackToFront(String reason) {
1423 final ActivityStack homeStack = getOrCreateRootHomeTask();
1424 if (homeStack != null) {
1425 homeStack.moveToFront(reason);
1426 }
1427 }
1428
1429 /**
1430 * Moves the focusable home activity to top. If there is no such activity, the home stack will
1431 * still move to top.
1432 */
1433 void moveHomeActivityToTop(String reason) {
1434 final ActivityRecord top = getHomeActivity();
1435 if (top == null) {
1436 moveHomeStackToFront(reason);
1437 return;
1438 }
1439 top.moveFocusableActivityToTop(reason);
1440 }
1441
1442 @Nullable
1443 ActivityRecord getHomeActivity() {
1444 return getHomeActivityForUser(mRootWindowContainer.mCurrentUser);
1445 }
1446
1447 @Nullable
1448 ActivityRecord getHomeActivityForUser(int userId) {
1449 final ActivityStack homeStack = getRootHomeTask();
1450 if (homeStack == null) {
1451 return null;
1452 }
1453
1454 final PooledPredicate p = PooledLambda.obtainPredicate(
Andrii Kulian86d676c2020-03-27 19:34:54 -07001455 TaskDisplayArea::isHomeActivityForUser, PooledLambda.__(ActivityRecord.class),
Andrii Kulian9ea12da2020-03-27 17:16:38 -07001456 userId);
1457 final ActivityRecord r = homeStack.getActivity(p);
1458 p.recycle();
1459 return r;
1460 }
1461
1462 private static boolean isHomeActivityForUser(ActivityRecord r, int userId) {
1463 return r.isActivityTypeHome() && (userId == UserHandle.USER_ALL || r.mUserId == userId);
1464 }
1465
1466 /**
1467 * Adjusts the {@param stack} behind the last visible stack in the display if necessary.
1468 * Generally used in conjunction with {@link #moveStackBehindStack}.
1469 */
1470 // TODO(b/151575894): Remove special stack movement methods.
1471 void moveStackBehindBottomMostVisibleStack(ActivityStack stack) {
1472 if (stack.shouldBeVisible(null)) {
1473 // Skip if the stack is already visible
1474 return;
1475 }
1476
1477 final boolean isRootTask = stack.isRootTask();
1478 if (isRootTask) {
1479 // Move the stack to the bottom to not affect the following visibility checks
1480 positionStackAtBottom(stack);
1481 } else {
1482 stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
1483 }
1484
1485 // Find the next position where the stack should be placed
1486 final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
1487 for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
1488 final ActivityStack s = isRootTask ? getStackAt(stackNdx)
1489 : (ActivityStack) stack.getParent().getChildAt(stackNdx);
1490 if (s == stack) {
1491 continue;
1492 }
1493 final int winMode = s.getWindowingMode();
1494 final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN
1495 || winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
1496 if (s.shouldBeVisible(null) && isValidWindowingMode) {
1497 // Move the provided stack to behind this stack
1498 final int position = Math.max(0, stackNdx - 1);
1499 if (isRootTask) {
1500 positionStackAt(stack, position);
1501 } else {
1502 stack.getParent().positionChildAt(position, stack, false /*includingParents */);
1503 }
1504 break;
1505 }
1506 }
1507 }
1508
1509 /**
1510 * Moves the {@param stack} behind the given {@param behindStack} if possible. If
1511 * {@param behindStack} is not currently in the display, then then the stack is moved to the
1512 * back. Generally used in conjunction with {@link #moveStackBehindBottomMostVisibleStack}.
1513 */
1514 void moveStackBehindStack(ActivityStack stack, ActivityStack behindStack) {
1515 if (behindStack == null || behindStack == stack) {
1516 return;
1517 }
1518
1519 final WindowContainer parent = stack.getParent();
1520 if (parent == null || parent != behindStack.getParent()) {
1521 return;
1522 }
1523
1524 // Note that positionChildAt will first remove the given stack before inserting into the
1525 // list, so we need to adjust the insertion index to account for the removed index
1526 // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
1527 // position internally
1528 final int stackIndex = parent.mChildren.indexOf(stack);
1529 final int behindStackIndex = parent.mChildren.indexOf(behindStack);
1530 final int insertIndex = stackIndex <= behindStackIndex
1531 ? behindStackIndex - 1 : behindStackIndex;
1532 final int position = Math.max(0, insertIndex);
1533 if (stack.isRootTask()) {
1534 positionStackAt(stack, position);
1535 } else {
1536 parent.positionChildAt(position, stack, false /* includingParents */);
1537 }
1538 }
Andrii Kulian86d676c2020-03-27 19:34:54 -07001539
1540 boolean hasPinnedTask() {
1541 return getRootPinnedTask() != null;
1542 }
1543
1544 /**
1545 * @return the stack currently above the {@param stack}. Can be null if the {@param stack} is
1546 * already top-most.
1547 */
1548 static ActivityStack getStackAbove(ActivityStack stack) {
1549 final WindowContainer wc = stack.getParent();
1550 final int index = wc.mChildren.indexOf(stack) + 1;
1551 return (index < wc.mChildren.size()) ? (ActivityStack) wc.mChildren.get(index) : null;
1552 }
1553
1554 int getDisplayId() {
1555 return mDisplayContent.getDisplayId();
1556 }
1557
1558 boolean isRemoved() {
1559 return mDisplayContent.isRemoved();
1560 }
Andrii Kulian526ad432020-03-27 12:19:51 -07001561}