• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.wm.shell.splitscreen;
18 
19 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
22 import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
23 import static android.view.RemoteAnimationTarget.MODE_OPENING;
24 
25 import static com.android.wm.shell.Flags.enableFlexibleSplit;
26 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
27 import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
28 import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
29 import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
30 import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
31 
32 import android.annotation.CallSuper;
33 import android.annotation.Nullable;
34 import android.app.ActivityManager;
35 import android.app.TaskInfo;
36 import android.content.Context;
37 import android.graphics.Rect;
38 import android.os.IBinder;
39 import android.util.SparseArray;
40 import android.view.RemoteAnimationTarget;
41 import android.view.SurfaceControl;
42 import android.window.WindowContainerToken;
43 import android.window.WindowContainerTransaction;
44 
45 import androidx.annotation.NonNull;
46 
47 import com.android.internal.protolog.ProtoLog;
48 import com.android.internal.util.ArrayUtils;
49 import com.android.launcher3.icons.IconProvider;
50 import com.android.wm.shell.ShellTaskOrganizer;
51 import com.android.wm.shell.common.SurfaceUtils;
52 import com.android.wm.shell.common.SyncTransactionQueue;
53 import com.android.wm.shell.common.split.SplitDecorManager;
54 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
55 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
56 
57 import java.io.PrintWriter;
58 import java.util.Optional;
59 import java.util.function.Consumer;
60 import java.util.function.Predicate;
61 
62 /**
63  * Base class that handle common task org. related for split-screen stages.
64  * Note that this class and its sub-class do not directly perform hierarchy operations.
65  * They only serve to hold a collection of tasks and provide APIs like
66  * {@link #addTask(ActivityManager.RunningTaskInfo, WindowContainerTransaction)} for the centralized
67  * {@link StageCoordinator} to perform hierarchy operations in-sync with other containers.
68  *
69  * @see StageCoordinator
70  */
71 public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
72     private static final String TAG = StageTaskListener.class.getSimpleName();
73 
74     // No current way to enforce this but if enableFlexibleSplit() is enabled, then only 1 of the
75     // stages should have this be set/being used
76     private boolean mIsActive;
77     /** Unique identifier for this state, > 0 */
78     @StageType private final int mId;
79     /** Callback interface for listening to changes in a split-screen stage. */
80     public interface StageListenerCallbacks {
onRootTaskAppeared()81         void onRootTaskAppeared();
82 
onStageVisibilityChanged(StageTaskListener stageTaskListener)83         void onStageVisibilityChanged(StageTaskListener stageTaskListener);
84 
onChildTaskStatusChanged(StageTaskListener stage, int taskId, boolean present, boolean visible)85         void onChildTaskStatusChanged(StageTaskListener stage, int taskId, boolean present,
86                 boolean visible);
87 
onRootTaskVanished()88         void onRootTaskVanished();
89 
onNoLongerSupportMultiWindow(StageTaskListener stageTaskListener, ActivityManager.RunningTaskInfo taskInfo)90         void onNoLongerSupportMultiWindow(StageTaskListener stageTaskListener,
91                 ActivityManager.RunningTaskInfo taskInfo);
92     }
93 
94     private final Context mContext;
95     private final StageListenerCallbacks mCallbacks;
96     private final SyncTransactionQueue mSyncQueue;
97     private final IconProvider mIconProvider;
98     private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
99 
100     /** Whether or not the root task has been created. */
101     boolean mHasRootTask = false;
102     /** Whether or not the root task is visible. */
103     boolean mVisible = false;
104     /** Whether or not the root task has any children or not. */
105     boolean mHasChildren = false;
106     protected ActivityManager.RunningTaskInfo mRootTaskInfo;
107     protected SurfaceControl mRootLeash;
108     protected SurfaceControl mDimLayer;
109     protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
110     private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>();
111     // TODO(b/204308910): Extracts SplitDecorManager related code to common package.
112     private SplitDecorManager mSplitDecorManager;
113 
StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, IconProvider iconProvider, Optional<WindowDecorViewModel> windowDecorViewModel, int id)114     StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
115             StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
116             IconProvider iconProvider,
117             Optional<WindowDecorViewModel> windowDecorViewModel, int id) {
118         mContext = context;
119         mCallbacks = callbacks;
120         mSyncQueue = syncQueue;
121         mIconProvider = iconProvider;
122         mWindowDecorViewModel = windowDecorViewModel;
123         taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
124         mId = id;
125     }
126 
getChildCount()127     int getChildCount() {
128         return mChildrenTaskInfo.size();
129     }
130 
containsTask(int taskId)131     boolean containsTask(int taskId) {
132         return mChildrenTaskInfo.contains(taskId);
133     }
134 
containsToken(WindowContainerToken token)135     boolean containsToken(WindowContainerToken token) {
136         return contains(t -> t.token.equals(token));
137     }
138 
containsContainer(IBinder binder)139     boolean containsContainer(IBinder binder) {
140         return contains(t -> t.token.asBinder() == binder);
141     }
142 
143     /**
144      * Returns the top visible child task's id.
145      */
getTopVisibleChildTaskId()146     int getTopVisibleChildTaskId() {
147         // TODO(b/378601156): This doesn't get the top task (translucent tasks are also
148         //  visible-requested)
149         final ActivityManager.RunningTaskInfo taskInfo = getChildTaskInfo(t -> t.isVisible
150                 && t.isVisibleRequested);
151         return taskInfo != null ? taskInfo.taskId : INVALID_TASK_ID;
152     }
153 
154     /**
155      * Returns the top activity uid for the top child task.
156      */
getTopChildTaskUid()157     int getTopChildTaskUid() {
158         // TODO(b/378601156): This doesn't get the top task
159         final ActivityManager.RunningTaskInfo taskInfo =
160                 getChildTaskInfo(t -> t.topActivityInfo != null);
161         return taskInfo != null ? taskInfo.topActivityInfo.applicationInfo.uid : 0;
162     }
163 
164     /** @return {@code true} if this listener contains the currently focused task. */
isFocused()165     boolean isFocused() {
166         return contains(t -> t.isFocused);
167     }
168 
169     @StageType
getId()170     int getId() {
171         return mId;
172     }
173 
contains(Predicate<ActivityManager.RunningTaskInfo> predicate)174     private boolean contains(Predicate<ActivityManager.RunningTaskInfo> predicate) {
175         if (mRootTaskInfo != null && predicate.test(mRootTaskInfo)) {
176             return true;
177         }
178 
179         return getChildTaskInfo(predicate) != null;
180     }
181 
getRootLeash()182     public SurfaceControl getRootLeash() {
183         return mRootLeash;
184     }
185 
getRunningTaskInfo()186     public ActivityManager.RunningTaskInfo getRunningTaskInfo() {
187         return mRootTaskInfo;
188     }
189 
getDecorManager()190     public SplitDecorManager getDecorManager() {
191         return mSplitDecorManager;
192     }
193 
194     @Nullable
getChildTaskInfo( Predicate<ActivityManager.RunningTaskInfo> predicate)195     private ActivityManager.RunningTaskInfo getChildTaskInfo(
196             Predicate<ActivityManager.RunningTaskInfo> predicate) {
197         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
198             final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
199             if (predicate.test(taskInfo)) {
200                 return taskInfo;
201             }
202         }
203         return null;
204     }
205 
206     @Override
207     @CallSuper
onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash)208     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
209         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: taskId=%d taskParent=%d rootTask=%d "
210                         + "stageId=%s taskActivity=%s",
211                 taskInfo.taskId, taskInfo.parentTaskId,
212                 mRootTaskInfo != null ? mRootTaskInfo.taskId : -1,
213                 stageTypeToString(mId), taskInfo.baseActivity);
214         if (mRootTaskInfo == null) {
215             mRootLeash = leash;
216             mRootTaskInfo = taskInfo;
217             mSplitDecorManager = new SplitDecorManager(
218                     mRootTaskInfo.configuration,
219                     mIconProvider);
220             mHasRootTask = true;
221             mCallbacks.onRootTaskAppeared();
222             if (mVisible != mRootTaskInfo.isVisible) {
223                 mVisible = mRootTaskInfo.isVisible;
224                 mCallbacks.onStageVisibilityChanged(this);
225             }
226             mSyncQueue.runInSync(t -> mDimLayer =
227                     SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer"));
228         } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
229             final int taskId = taskInfo.taskId;
230             mChildrenLeashes.put(taskId, leash);
231             mChildrenTaskInfo.put(taskId, taskInfo);
232             mCallbacks.onChildTaskStatusChanged(this, taskId, true /* present */,
233                     taskInfo.isVisible && taskInfo.isVisibleRequested);
234         } else {
235             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
236                     + "\n mRootTaskInfo: " + mRootTaskInfo);
237         }
238     }
239 
240     @Override
241     @CallSuper
onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo)242     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
243         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: taskId=%d taskAct=%s "
244                         + "stageId=%s",
245                 taskInfo.taskId, taskInfo.baseActivity, stageTypeToString(mId));
246         mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo));
247         if (mRootTaskInfo.taskId == taskInfo.taskId) {
248             mRootTaskInfo = taskInfo;
249         } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
250             if (!taskInfo.supportsMultiWindow
251                     || !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
252                     || !ArrayUtils.contains(CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
253                     taskInfo.getWindowingMode())) {
254                 ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
255                         "onTaskInfoChanged: task=%d no longer supports multiwindow",
256                         taskInfo.taskId);
257                 // Leave split screen if the task no longer supports multi window or have
258                 // uncontrolled task.
259                 mCallbacks.onNoLongerSupportMultiWindow(this, taskInfo);
260                 return;
261             }
262             mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
263             mCallbacks.onChildTaskStatusChanged(this, taskInfo.taskId, true /* present */,
264                     taskInfo.isVisible && taskInfo.isVisibleRequested);
265         } else {
266             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
267                     + "\n mRootTaskInfo: " + mRootTaskInfo);
268         }
269     }
270 
271     @Override
272     @CallSuper
onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)273     public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
274         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%d stageId=%s",
275                 taskInfo.taskId, stageTypeToString(mId));
276         final int taskId = taskInfo.taskId;
277         mWindowDecorViewModel.ifPresent(vm -> vm.onTaskVanished(taskInfo));
278         if (mRootTaskInfo.taskId == taskId) {
279             mHasRootTask = false;
280             mVisible = false;
281             mHasChildren = false;
282             mCallbacks.onRootTaskVanished();
283             mRootTaskInfo = null;
284             mRootLeash = null;
285             mSyncQueue.runInSync(t -> {
286                 t.remove(mDimLayer);
287                 mSplitDecorManager.release(t);
288             });
289         } else if (mChildrenTaskInfo.contains(taskId)) {
290             mChildrenTaskInfo.remove(taskId);
291             mChildrenLeashes.remove(taskId);
292             mCallbacks.onChildTaskStatusChanged(this, taskId, false /* present */,
293                     taskInfo.isVisible);
294         } else {
295             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
296                     + "\n mRootTaskInfo: " + mRootTaskInfo);
297         }
298     }
299 
300     @Override
attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b)301     public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
302         b.setParent(findTaskSurface(taskId));
303     }
304 
305     @Override
reparentChildSurfaceToTask(int taskId, SurfaceControl sc, SurfaceControl.Transaction t)306     public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
307             SurfaceControl.Transaction t) {
308         t.reparent(sc, findTaskSurface(taskId));
309     }
310 
findTaskSurface(int taskId)311     private SurfaceControl findTaskSurface(int taskId) {
312         if (mRootTaskInfo.taskId == taskId) {
313             return mRootLeash;
314         } else if (mChildrenLeashes.contains(taskId)) {
315             return mChildrenLeashes.get(taskId);
316         } else {
317             throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
318         }
319     }
320 
isRootTaskId(int taskId)321     boolean isRootTaskId(int taskId) {
322         return mRootTaskInfo != null && mRootTaskInfo.taskId == taskId;
323     }
324 
onResizing(Rect newBounds, Rect sideBounds, Rect displayBounds, SurfaceControl.Transaction t, int offsetX, int offsetY, boolean immediately)325     void onResizing(Rect newBounds, Rect sideBounds, Rect displayBounds,
326             SurfaceControl.Transaction t, int offsetX, int offsetY, boolean immediately) {
327         if (mSplitDecorManager != null && mRootTaskInfo != null) {
328             mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, displayBounds, t,
329                     offsetX, offsetY, immediately);
330         }
331     }
332 
onResized(SurfaceControl.Transaction t)333     void onResized(SurfaceControl.Transaction t) {
334         if (mSplitDecorManager != null) {
335             mSplitDecorManager.onResized(t, null);
336         }
337     }
338 
screenshotIfNeeded(SurfaceControl.Transaction t)339     void screenshotIfNeeded(SurfaceControl.Transaction t) {
340         if (mSplitDecorManager != null) {
341             mSplitDecorManager.screenshotIfNeeded(t);
342         }
343     }
344 
fadeOutDecor(Runnable finishedCallback)345     void fadeOutDecor(Runnable finishedCallback) {
346         if (mSplitDecorManager != null) {
347             mSplitDecorManager.fadeOutDecor(finishedCallback, false /* addDelay */);
348         } else {
349             finishedCallback.run();
350         }
351     }
352 
getSplitDecorManager()353     SplitDecorManager getSplitDecorManager() {
354         return mSplitDecorManager;
355     }
356 
addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct)357     void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
358         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "addTask: task=%d", task.taskId);
359         // Clear overridden bounds and windowing mode to make sure the child task can inherit
360         // windowing mode and bounds from split root.
361         wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
362                 .setBounds(task.token, null);
363 
364         wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/);
365     }
366 
reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct)367     void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) {
368         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "reorderChild: task=%d onTop=%b", taskId, onTop);
369         if (!containsTask(taskId)) {
370             return;
371         }
372         wct.reorder(mChildrenTaskInfo.get(taskId).token, onTop /* onTop */);
373     }
374 
doForAllChildTasks(Consumer<Integer> consumer)375     void doForAllChildTasks(Consumer<Integer> consumer) {
376         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
377             final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
378             consumer.accept(taskInfo.taskId);
379         }
380     }
381 
doForAllChildTaskInfos(Consumer<ActivityManager.RunningTaskInfo> consumer)382     void doForAllChildTaskInfos(Consumer<ActivityManager.RunningTaskInfo> consumer) {
383         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
384             final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
385             consumer.accept(taskInfo);
386         }
387     }
388 
389     /** Collects all the current child tasks and prepares transaction to evict them to display. */
evictAllChildren(WindowContainerTransaction wct)390     void evictAllChildren(WindowContainerTransaction wct) {
391         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
392             final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
393             evictChild(wct, taskInfo, "all");
394         }
395     }
396 
evictOtherChildren(WindowContainerTransaction wct, int taskId)397     void evictOtherChildren(WindowContainerTransaction wct, int taskId) {
398         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
399             final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
400             if (taskId == taskInfo.taskId) continue;
401             evictChild(wct, taskInfo, "other");
402         }
403     }
404 
evictNonOpeningChildren(RemoteAnimationTarget[] apps, WindowContainerTransaction wct)405     void evictNonOpeningChildren(RemoteAnimationTarget[] apps, WindowContainerTransaction wct) {
406         final SparseArray<ActivityManager.RunningTaskInfo> toBeEvict = mChildrenTaskInfo.clone();
407         for (int i = 0; i < apps.length; i++) {
408             if (apps[i].mode == MODE_OPENING) {
409                 toBeEvict.remove(apps[i].taskId);
410             }
411         }
412         for (int i = toBeEvict.size() - 1; i >= 0; i--) {
413             final ActivityManager.RunningTaskInfo taskInfo = toBeEvict.valueAt(i);
414             evictChild(wct, taskInfo, "non-opening");
415         }
416     }
417 
evictInvisibleChildren(WindowContainerTransaction wct)418     void evictInvisibleChildren(WindowContainerTransaction wct) {
419         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
420             final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
421             if (!taskInfo.isVisible) {
422                 evictChild(wct, taskInfo, "invisible");
423             }
424         }
425     }
426 
evictChild(WindowContainerTransaction wct, int taskId, String reason)427     void evictChild(WindowContainerTransaction wct, int taskId, String reason) {
428         final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.get(taskId);
429         if (taskInfo != null) {
430             evictChild(wct, taskInfo, reason);
431         }
432     }
433 
evictChild(@onNull WindowContainerTransaction wct, @NonNull TaskInfo taskInfo, @NonNull String reason)434     private void evictChild(@NonNull WindowContainerTransaction wct, @NonNull TaskInfo taskInfo,
435             @NonNull String reason) {
436         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict child: task=%d reason=%s", taskInfo.taskId,
437                 reason);
438         // We are reparenting the task, but not removing the task from mChildrenTaskInfo, so to
439         // prevent this task from being considered as a top task for the roots, we need to override
440         // the visibility of the soon-to-be-hidden task
441         taskInfo.isVisible = false;
442         taskInfo.isVisibleRequested = false;
443         wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
444     }
445 
reparentTopTask(WindowContainerTransaction wct)446     void reparentTopTask(WindowContainerTransaction wct) {
447         wct.reparentTasks(null /* currentParent */, mRootTaskInfo.token,
448                 CONTROLLED_WINDOWING_MODES, CONTROLLED_ACTIVITY_TYPES,
449                 true /* onTop */, true /* reparentTopOnly */);
450     }
451 
resetBounds(WindowContainerTransaction wct)452     void resetBounds(WindowContainerTransaction wct) {
453         wct.setBounds(mRootTaskInfo.token, null);
454         wct.setAppBounds(mRootTaskInfo.token, null);
455         wct.setSmallestScreenWidthDp(mRootTaskInfo.token, SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
456     }
457 
onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener, @StageType int stage)458     void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
459             @StageType int stage) {
460         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
461             int taskId = mChildrenTaskInfo.keyAt(i);
462             listener.onTaskStageChanged(taskId, stage,
463                     mChildrenTaskInfo.get(taskId).isVisible);
464         }
465     }
466 
467     // ---------
468     // Previously only used in MainStage
isActive()469     boolean isActive() {
470         return mIsActive;
471     }
472 
activate(WindowContainerTransaction wct, boolean includingTopTask)473     void activate(WindowContainerTransaction wct, boolean includingTopTask) {
474         if (mIsActive && !enableFlexibleSplit()) return;
475         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: includingTopTask=%b stage=%s",
476                 includingTopTask, stageTypeToString(mId));
477 
478         if (includingTopTask) {
479             reparentTopTask(wct);
480         }
481 
482         if (enableFlexibleSplit()) {
483             return;
484         }
485         mIsActive = true;
486     }
487 
deactivate(WindowContainerTransaction wct)488     void deactivate(WindowContainerTransaction wct) {
489         deactivate(wct, false /* toTop */);
490     }
491 
deactivate(WindowContainerTransaction wct, boolean reparentTasksToTop)492     void deactivate(WindowContainerTransaction wct, boolean reparentTasksToTop) {
493         if (!mIsActive && !enableFlexibleSplit()) return;
494         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: reparentTasksToTop=%b "
495                         + "rootTaskInfo=%s stage=%s",
496                 reparentTasksToTop, mRootTaskInfo, stageTypeToString(mId));
497         if (!enableFlexibleSplit()) {
498             mIsActive = false;
499         }
500 
501         if (mRootTaskInfo == null) return;
502         final WindowContainerToken rootToken = mRootTaskInfo.token;
503         wct.reparentTasks(
504                 rootToken,
505                 null /* newParent */,
506                 null /* windowingModes */,
507                 null /* activityTypes */,
508                 reparentTasksToTop);
509     }
510 
511     // --------
512     // Previously only used in SideStage. With flexible split this is called for all stages
removeAllTasks(WindowContainerTransaction wct, boolean toTop)513     boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
514         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b "
515                         + " stageI=%s",
516                 mChildrenTaskInfo.size(), toTop, stageTypeToString(mId));
517         if (mChildrenTaskInfo.size() == 0) return false;
518         wct.reparentTasks(
519                 mRootTaskInfo.token,
520                 null /* newParent */,
521                 null /* windowingModes */,
522                 null /* activityTypes */,
523                 toTop);
524         return true;
525     }
526 
removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct)527     boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
528         final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
529         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId,
530                 task != null);
531         if (task == null) return false;
532         wct.reparent(task.token, newParent, false /* onTop */);
533         return true;
534     }
535 
536     @Override
toString()537     public String toString() {
538         return "mId: " + stageTypeToString(mId)
539                 + " mVisible: " + mVisible
540                 + " mActive: " + mIsActive
541                 + " mHasRootTask: " + mHasRootTask
542                 + " childSize: " + mChildrenTaskInfo.size();
543     }
544 
545     @Override
546     @CallSuper
dump(@onNull PrintWriter pw, String prefix)547     public void dump(@NonNull PrintWriter pw, String prefix) {
548         final String innerPrefix = prefix + "  ";
549         final String childPrefix = innerPrefix + "  ";
550         if (mChildrenTaskInfo.size() > 0) {
551             pw.println(prefix + "Children list:");
552             for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
553                 final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
554                 pw.println(childPrefix + "Task#" + i + " taskID=" + taskInfo.taskId
555                         + " baseActivity=" + taskInfo.baseActivity);
556             }
557         }
558         pw.println(prefix + "mHasRootTask=" + mHasRootTask);
559         pw.println(prefix + "mVisible=" + mVisible);
560         pw.println(prefix + "mHasChildren=" + mHasChildren);
561     }
562 }
563