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