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.ActivityManager.START_SUCCESS;
20 import static android.app.ActivityManager.START_TASK_TO_FRONT;
21 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
22 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
23 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
24 import static android.view.Display.DEFAULT_DISPLAY;
25 import static android.view.RemoteAnimationTarget.MODE_OPENING;
26 
27 import static com.android.wm.shell.common.MultiInstanceHelper.getComponent;
28 import static com.android.wm.shell.common.MultiInstanceHelper.getShortcutComponent;
29 import static com.android.wm.shell.common.MultiInstanceHelper.samePackage;
30 import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
31 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
32 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
33 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
34 import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
35 import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
36 import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
37 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
38 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
39 import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
40 
41 import android.app.ActivityManager;
42 import android.app.ActivityOptions;
43 import android.app.ActivityTaskManager;
44 import android.app.PendingIntent;
45 import android.app.TaskInfo;
46 import android.content.ComponentName;
47 import android.content.Context;
48 import android.content.Intent;
49 import android.content.pm.LauncherApps;
50 import android.content.pm.ShortcutInfo;
51 import android.graphics.Rect;
52 import android.os.Bundle;
53 import android.os.RemoteException;
54 import android.os.UserHandle;
55 import android.util.ArrayMap;
56 import android.util.Log;
57 import android.util.Slog;
58 import android.view.IRemoteAnimationFinishedCallback;
59 import android.view.IRemoteAnimationRunner;
60 import android.view.RemoteAnimationAdapter;
61 import android.view.RemoteAnimationTarget;
62 import android.view.SurfaceControl;
63 import android.view.SurfaceSession;
64 import android.view.WindowManager;
65 import android.widget.Toast;
66 import android.window.RemoteTransition;
67 import android.window.WindowContainerTransaction;
68 
69 import androidx.annotation.BinderThread;
70 import androidx.annotation.IntDef;
71 import androidx.annotation.NonNull;
72 import androidx.annotation.Nullable;
73 
74 import com.android.internal.annotations.VisibleForTesting;
75 import com.android.internal.logging.InstanceId;
76 import com.android.internal.protolog.common.ProtoLog;
77 import com.android.launcher3.icons.IconProvider;
78 import com.android.wm.shell.R;
79 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
80 import com.android.wm.shell.ShellTaskOrganizer;
81 import com.android.wm.shell.common.DisplayController;
82 import com.android.wm.shell.common.DisplayImeController;
83 import com.android.wm.shell.common.DisplayInsetsController;
84 import com.android.wm.shell.common.ExternalInterfaceBinder;
85 import com.android.wm.shell.common.LaunchAdjacentController;
86 import com.android.wm.shell.common.MultiInstanceHelper;
87 import com.android.wm.shell.common.RemoteCallable;
88 import com.android.wm.shell.common.ShellExecutor;
89 import com.android.wm.shell.common.SingleInstanceRemoteListener;
90 import com.android.wm.shell.common.SyncTransactionQueue;
91 import com.android.wm.shell.common.TransactionPool;
92 import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
93 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
94 import com.android.wm.shell.common.split.SplitScreenUtils;
95 import com.android.wm.shell.desktopmode.DesktopTasksController;
96 import com.android.wm.shell.draganddrop.DragAndDropController;
97 import com.android.wm.shell.draganddrop.DragAndDropPolicy;
98 import com.android.wm.shell.protolog.ShellProtoLogGroup;
99 import com.android.wm.shell.recents.RecentTasksController;
100 import com.android.wm.shell.shared.annotations.ExternalThread;
101 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
102 import com.android.wm.shell.sysui.KeyguardChangeListener;
103 import com.android.wm.shell.sysui.ShellCommandHandler;
104 import com.android.wm.shell.sysui.ShellController;
105 import com.android.wm.shell.sysui.ShellInit;
106 import com.android.wm.shell.transition.Transitions;
107 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
108 
109 import java.io.PrintWriter;
110 import java.lang.annotation.Retention;
111 import java.lang.annotation.RetentionPolicy;
112 import java.util.Optional;
113 import java.util.concurrent.Executor;
114 import java.util.concurrent.atomic.AtomicBoolean;
115 
116 /**
117  * Class manages split-screen multitasking mode and implements the main interface
118  * {@link SplitScreen}.
119  *
120  * @see StageCoordinator
121  */
122 // TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen.
123 public class SplitScreenController implements DragAndDropPolicy.Starter,
124         RemoteCallable<SplitScreenController>, KeyguardChangeListener {
125     private static final String TAG = SplitScreenController.class.getSimpleName();
126 
127     public static final int EXIT_REASON_UNKNOWN = 0;
128     public static final int EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW = 1;
129     public static final int EXIT_REASON_APP_FINISHED = 2;
130     public static final int EXIT_REASON_DEVICE_FOLDED = 3;
131     public static final int EXIT_REASON_DRAG_DIVIDER = 4;
132     public static final int EXIT_REASON_RETURN_HOME = 5;
133     public static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
134     public static final int EXIT_REASON_SCREEN_LOCKED = 7;
135     public static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
136     public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
137     public static final int EXIT_REASON_RECREATE_SPLIT = 10;
138     public static final int EXIT_REASON_FULLSCREEN_SHORTCUT = 11;
139     public static final int EXIT_REASON_DESKTOP_MODE = 12;
140     public static final int EXIT_REASON_FULLSCREEN_REQUEST = 13;
141     @IntDef(value = {
142             EXIT_REASON_UNKNOWN,
143             EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
144             EXIT_REASON_APP_FINISHED,
145             EXIT_REASON_DEVICE_FOLDED,
146             EXIT_REASON_DRAG_DIVIDER,
147             EXIT_REASON_RETURN_HOME,
148             EXIT_REASON_ROOT_TASK_VANISHED,
149             EXIT_REASON_SCREEN_LOCKED,
150             EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP,
151             EXIT_REASON_CHILD_TASK_ENTER_PIP,
152             EXIT_REASON_RECREATE_SPLIT,
153             EXIT_REASON_FULLSCREEN_SHORTCUT,
154             EXIT_REASON_DESKTOP_MODE,
155             EXIT_REASON_FULLSCREEN_REQUEST
156     })
157     @Retention(RetentionPolicy.SOURCE)
158     @interface ExitReason{}
159 
160     public static final int ENTER_REASON_UNKNOWN = 0;
161     public static final int ENTER_REASON_MULTI_INSTANCE = 1;
162     public static final int ENTER_REASON_DRAG = 2;
163     public static final int ENTER_REASON_LAUNCHER = 3;
164     /** Acts as a mapping to the actual EnterReasons as defined in the logging proto */
165     @IntDef(value = {
166             ENTER_REASON_MULTI_INSTANCE,
167             ENTER_REASON_DRAG,
168             ENTER_REASON_LAUNCHER,
169             ENTER_REASON_UNKNOWN
170     })
171     public @interface SplitEnterReason {
172     }
173 
174     private final ShellCommandHandler mShellCommandHandler;
175     private final ShellController mShellController;
176     private final ShellTaskOrganizer mTaskOrganizer;
177     private final SyncTransactionQueue mSyncQueue;
178     private final Context mContext;
179     private final LauncherApps mLauncherApps;
180     private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
181     private final ShellExecutor mMainExecutor;
182     private final SplitScreenImpl mImpl = new SplitScreenImpl();
183     private final DisplayController mDisplayController;
184     private final DisplayImeController mDisplayImeController;
185     private final DisplayInsetsController mDisplayInsetsController;
186     private final DragAndDropController mDragAndDropController;
187     private final Transitions mTransitions;
188     private final TransactionPool mTransactionPool;
189     private final IconProvider mIconProvider;
190     private final Optional<RecentTasksController> mRecentTasksOptional;
191     private final LaunchAdjacentController mLaunchAdjacentController;
192     private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
193     private final Optional<DesktopTasksController> mDesktopTasksController;
194     private final MultiInstanceHelper mMultiInstanceHelpher;
195     private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
196 
197     @VisibleForTesting
198     StageCoordinator mStageCoordinator;
199 
200     // Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
201     // outside the bounds of the roots by being reparented into a higher level fullscreen container
202     private SurfaceControl mGoingToRecentsTasksLayer;
203     private SurfaceControl mStartingSplitTasksLayer;
204 
205     /**
206      * @param stageCoordinator if null, a stage coordinator will be created when this controller is
207      *                         initialized. Can be non-null for testing purposes.
208      */
SplitScreenController(Context context, ShellInit shellInit, ShellCommandHandler shellCommandHandler, ShellController shellController, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTDAOrganizer, DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, DragAndDropController dragAndDropController, Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider, Optional<RecentTasksController> recentTasks, LaunchAdjacentController launchAdjacentController, Optional<WindowDecorViewModel> windowDecorViewModel, Optional<DesktopTasksController> desktopTasksController, @Nullable StageCoordinator stageCoordinator, MultiInstanceHelper multiInstanceHelper, ShellExecutor mainExecutor)209     public SplitScreenController(Context context,
210             ShellInit shellInit,
211             ShellCommandHandler shellCommandHandler,
212             ShellController shellController,
213             ShellTaskOrganizer shellTaskOrganizer,
214             SyncTransactionQueue syncQueue,
215             RootTaskDisplayAreaOrganizer rootTDAOrganizer,
216             DisplayController displayController,
217             DisplayImeController displayImeController,
218             DisplayInsetsController displayInsetsController,
219             DragAndDropController dragAndDropController,
220             Transitions transitions,
221             TransactionPool transactionPool,
222             IconProvider iconProvider,
223             Optional<RecentTasksController> recentTasks,
224             LaunchAdjacentController launchAdjacentController,
225             Optional<WindowDecorViewModel> windowDecorViewModel,
226             Optional<DesktopTasksController> desktopTasksController,
227             @Nullable StageCoordinator stageCoordinator,
228             MultiInstanceHelper multiInstanceHelper,
229             ShellExecutor mainExecutor) {
230         mShellCommandHandler = shellCommandHandler;
231         mShellController = shellController;
232         mTaskOrganizer = shellTaskOrganizer;
233         mSyncQueue = syncQueue;
234         mContext = context;
235         mLauncherApps = context.getSystemService(LauncherApps.class);
236         mRootTDAOrganizer = rootTDAOrganizer;
237         mMainExecutor = mainExecutor;
238         mDisplayController = displayController;
239         mDisplayImeController = displayImeController;
240         mDisplayInsetsController = displayInsetsController;
241         mDragAndDropController = dragAndDropController;
242         mTransitions = transitions;
243         mTransactionPool = transactionPool;
244         mIconProvider = iconProvider;
245         mRecentTasksOptional = recentTasks;
246         mLaunchAdjacentController = launchAdjacentController;
247         mWindowDecorViewModel = windowDecorViewModel;
248         mDesktopTasksController = desktopTasksController;
249         mStageCoordinator = stageCoordinator;
250         mMultiInstanceHelpher = multiInstanceHelper;
251         mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
252         // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
253         //                    override for this controller from the base module
254         if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
255             shellInit.addInitCallback(this::onInit, this);
256         }
257     }
258 
asSplitScreen()259     public SplitScreen asSplitScreen() {
260         return mImpl;
261     }
262 
createExternalInterface()263     private ExternalInterfaceBinder createExternalInterface() {
264         return new ISplitScreenImpl(this);
265     }
266 
267     /**
268      * This will be called after ShellTaskOrganizer has initialized/registered because of the
269      * dependency order.
270      */
271     @VisibleForTesting
onInit()272     void onInit() {
273         mShellCommandHandler.addDumpCallback(this::dump, this);
274         mShellCommandHandler.addCommandCallback("splitscreen", mSplitScreenShellCommandHandler,
275                 this);
276         mShellController.addKeyguardChangeListener(this);
277         mShellController.addExternalInterface(KEY_EXTRA_SHELL_SPLIT_SCREEN,
278                 this::createExternalInterface, this);
279         if (mStageCoordinator == null) {
280             // TODO: Multi-display
281             mStageCoordinator = createStageCoordinator();
282         }
283         if (mDragAndDropController != null) {
284             mDragAndDropController.setSplitScreenController(this);
285         }
286         mWindowDecorViewModel.ifPresent(viewModel -> viewModel.setSplitScreenController(this));
287         mDesktopTasksController.ifPresent(controller -> controller.setSplitScreenController(this));
288     }
289 
createStageCoordinator()290     protected StageCoordinator createStageCoordinator() {
291         return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
292                 mTaskOrganizer, mDisplayController, mDisplayImeController,
293                 mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider,
294                 mMainExecutor, mRecentTasksOptional, mLaunchAdjacentController,
295                 mWindowDecorViewModel);
296     }
297 
298     @Override
getContext()299     public Context getContext() {
300         return mContext;
301     }
302 
303     @Override
getRemoteCallExecutor()304     public ShellExecutor getRemoteCallExecutor() {
305         return mMainExecutor;
306     }
307 
isSplitScreenVisible()308     public boolean isSplitScreenVisible() {
309         return mStageCoordinator.isSplitScreenVisible();
310     }
311 
getTransitionHandler()312     public StageCoordinator getTransitionHandler() {
313         return mStageCoordinator;
314     }
315 
316     @Nullable
getTaskInfo(@plitPosition int splitPosition)317     public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
318         if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
319             return null;
320         }
321 
322         final int taskId = mStageCoordinator.getTaskId(splitPosition);
323         return mTaskOrganizer.getRunningTaskInfo(taskId);
324     }
325 
326     /** Check task is under split or not by taskId. */
isTaskInSplitScreen(int taskId)327     public boolean isTaskInSplitScreen(int taskId) {
328         return mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
329     }
330 
331     /** Get the split stage of task is under it. */
getStageOfTask(int taskId)332     public @StageType int getStageOfTask(int taskId) {
333         return mStageCoordinator.getStageOfTask(taskId);
334     }
335 
336     /**
337      * @return {@code true} if we should create a left-right split, {@code false} if we should
338      * create a top-bottom split.
339      */
isLeftRightSplit()340     public boolean isLeftRightSplit() {
341         return mStageCoordinator.isLeftRightSplit();
342     }
343 
344     /** Check split is foreground and task is under split or not by taskId. */
isTaskInSplitScreenForeground(int taskId)345     public boolean isTaskInSplitScreenForeground(int taskId) {
346         return isTaskInSplitScreen(taskId) && isSplitScreenVisible();
347     }
348 
349     /** Check whether the task is the single-top root or the root of one of the stages. */
isTaskRootOrStageRoot(int taskId)350     public boolean isTaskRootOrStageRoot(int taskId) {
351         return mStageCoordinator.isRootOrStageRoot(taskId);
352     }
353 
getSplitPosition(int taskId)354     public @SplitPosition int getSplitPosition(int taskId) {
355         return mStageCoordinator.getSplitPosition(taskId);
356     }
357 
moveToSideStage(int taskId, @SplitPosition int sideStagePosition)358     public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
359         return moveToStage(taskId, sideStagePosition, new WindowContainerTransaction());
360     }
361 
362     /**
363      * Update surfaces of the split screen layout based on the current state
364      * @param transaction to write the updates to
365      */
updateSplitScreenSurfaces(SurfaceControl.Transaction transaction)366     public void updateSplitScreenSurfaces(SurfaceControl.Transaction transaction) {
367         mStageCoordinator.updateSurfaces(transaction);
368     }
369 
moveToStage(int taskId, @SplitPosition int stagePosition, WindowContainerTransaction wct)370     private boolean moveToStage(int taskId, @SplitPosition int stagePosition,
371             WindowContainerTransaction wct) {
372         final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
373         if (task == null) {
374             throw new IllegalArgumentException("Unknown taskId" + taskId);
375         }
376         if (isTaskInSplitScreen(taskId)) {
377             throw new IllegalArgumentException("taskId is in split" + taskId);
378         }
379         return mStageCoordinator.moveToStage(task, stagePosition, wct);
380     }
381 
removeFromSideStage(int taskId)382     public boolean removeFromSideStage(int taskId) {
383         return mStageCoordinator.removeFromSideStage(taskId);
384     }
385 
setSideStagePosition(@plitPosition int sideStagePosition)386     public void setSideStagePosition(@SplitPosition int sideStagePosition) {
387         mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
388     }
389 
390     /**
391      * Doing necessary window transaction for other transition handler need to enter split in
392      * transition.
393      */
prepareEnterSplitScreen(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo taskInfo, int startPosition)394     public void prepareEnterSplitScreen(WindowContainerTransaction wct,
395             ActivityManager.RunningTaskInfo taskInfo, int startPosition) {
396         mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition,
397                 false /* resizeAnim */);
398     }
399 
400     /**
401      * Doing necessary surface transaction for other transition handler need to enter split in
402      * transition when finished.
403      */
finishEnterSplitScreen(SurfaceControl.Transaction finishT)404     public void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
405         mStageCoordinator.finishEnterSplitScreen(finishT);
406     }
407 
408     /**
409      * Performs previous child eviction and such to prepare for the pip task expending into one of
410      * the split stages
411      *
412      * @param taskInfo TaskInfo of the pip task
413      */
onPipExpandToSplit(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo taskInfo)414     public void onPipExpandToSplit(WindowContainerTransaction wct,
415             ActivityManager.RunningTaskInfo taskInfo) {
416         mStageCoordinator.onPipExpandToSplit(wct, taskInfo);
417     }
418 
419     /**
420      * Doing necessary window transaction for other transition handler need to exit split in
421      * transition.
422      */
prepareExitSplitScreen(WindowContainerTransaction wct, @StageType int stageToTop, @ExitReason int reason)423     public void prepareExitSplitScreen(WindowContainerTransaction wct,
424             @StageType int stageToTop, @ExitReason int reason) {
425         mStageCoordinator.prepareExitSplitScreen(stageToTop, wct);
426         mStageCoordinator.clearSplitPairedInRecents(reason);
427     }
428 
enterSplitScreen(int taskId, boolean leftOrTop)429     public void enterSplitScreen(int taskId, boolean leftOrTop) {
430         enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
431     }
432 
enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct)433     public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
434         final int stagePosition =
435                 leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
436         moveToStage(taskId, stagePosition, wct);
437     }
438 
exitSplitScreen(int toTopTaskId, @ExitReason int exitReason)439     public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
440         if (ENABLE_SHELL_TRANSITIONS) {
441             mStageCoordinator.dismissSplitScreen(toTopTaskId, exitReason);
442         } else {
443             mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
444         }
445     }
446 
447     @Override
onKeyguardVisibilityChanged(boolean visible, boolean occluded, boolean animatingDismiss)448     public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
449             boolean animatingDismiss) {
450         mStageCoordinator.onKeyguardVisibilityChanged(visible);
451     }
452 
onFinishedWakingUp()453     public void onFinishedWakingUp() {
454         mStageCoordinator.onFinishedWakingUp();
455     }
456 
exitSplitScreenOnHide(boolean exitSplitScreenOnHide)457     public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
458         mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
459     }
460 
getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds)461     public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
462         mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
463     }
464 
registerSplitScreenListener(SplitScreen.SplitScreenListener listener)465     public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
466         mStageCoordinator.registerSplitScreenListener(listener);
467     }
468 
unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener)469     public void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
470         mStageCoordinator.unregisterSplitScreenListener(listener);
471     }
472 
473     /** Register a split select listener */
registerSplitSelectListener(SplitScreen.SplitSelectListener listener)474     public void registerSplitSelectListener(SplitScreen.SplitSelectListener listener) {
475         mStageCoordinator.registerSplitSelectListener(listener);
476     }
477 
478     /** Unregister a split select listener */
unregisterSplitSelectListener(SplitScreen.SplitSelectListener listener)479     public void unregisterSplitSelectListener(SplitScreen.SplitSelectListener listener) {
480         mStageCoordinator.unregisterSplitSelectListener(listener);
481     }
482 
goToFullscreenFromSplit()483     public void goToFullscreenFromSplit() {
484         if (mStageCoordinator.isSplitActive()) {
485             mStageCoordinator.goToFullscreenFromSplit();
486         }
487     }
488 
setSplitscreenFocus(boolean leftOrTop)489     public void setSplitscreenFocus(boolean leftOrTop) {
490         if (mStageCoordinator.isSplitActive()) {
491             mStageCoordinator.grantFocusToPosition(leftOrTop);
492         }
493     }
494 
495     /** Move the specified task to fullscreen, regardless of focus state. */
moveTaskToFullscreen(int taskId, int exitReason)496     public void moveTaskToFullscreen(int taskId, int exitReason) {
497         mStageCoordinator.moveTaskToFullscreen(taskId, exitReason);
498     }
499 
isLaunchToSplit(TaskInfo taskInfo)500     public boolean isLaunchToSplit(TaskInfo taskInfo) {
501         return mStageCoordinator.isLaunchToSplit(taskInfo);
502     }
503 
getActivateSplitPosition(TaskInfo taskInfo)504     public int getActivateSplitPosition(TaskInfo taskInfo) {
505         return mStageCoordinator.getActivateSplitPosition(taskInfo);
506     }
507 
508     /** Start two tasks in parallel as a splitscreen pair. */
startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)509     public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
510             @Nullable Bundle options2, @SplitPosition int splitPosition,
511             @PersistentSnapPosition int snapPosition,
512             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
513         mStageCoordinator.startTasks(taskId1, options1, taskId2, options2, splitPosition,
514                 snapPosition, remoteTransition, instanceId);
515     }
516 
517     /**
518      * Move a task to split select
519      * @param taskInfo the task being moved to split select
520      * @param wct transaction to apply if this is a valid request
521      * @param splitPosition the split position this task should move to
522      * @param taskBounds current freeform bounds of the task entering split
523      */
requestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo, WindowContainerTransaction wct, int splitPosition, Rect taskBounds)524     public void requestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
525             WindowContainerTransaction wct, int splitPosition, Rect taskBounds) {
526         mStageCoordinator.requestEnterSplitSelect(taskInfo, wct, splitPosition, taskBounds);
527     }
528 
startTask(int taskId, @SplitPosition int position, @Nullable Bundle options)529     public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
530         final int[] result = new int[1];
531         IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
532             @Override
533             public void onAnimationStart(@WindowManager.TransitionOldType int transit,
534                     RemoteAnimationTarget[] apps,
535                     RemoteAnimationTarget[] wallpapers,
536                     RemoteAnimationTarget[] nonApps,
537                     final IRemoteAnimationFinishedCallback finishedCallback) {
538                 try {
539                     finishedCallback.onAnimationFinished();
540                 } catch (RemoteException e) {
541                     Slog.e(TAG, "Failed to invoke onAnimationFinished", e);
542                 }
543                 if (result[0] == START_SUCCESS || result[0] == START_TASK_TO_FRONT) {
544                     final WindowContainerTransaction evictWct = new WindowContainerTransaction();
545                     mStageCoordinator.prepareEvictNonOpeningChildTasks(position, apps, evictWct);
546                     mSyncQueue.queue(evictWct);
547                 }
548             }
549             @Override
550             public void onAnimationCancelled() {
551                 final WindowContainerTransaction evictWct = new WindowContainerTransaction();
552                 mStageCoordinator.prepareEvictInvisibleChildTasks(evictWct);
553                 mSyncQueue.queue(evictWct);
554             }
555         };
556         options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
557                 null /* wct */);
558         RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
559                 0 /* duration */, 0 /* statusBarTransitionDelay */);
560         ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
561         activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
562 
563         try {
564             result[0] = ActivityTaskManager.getService().startActivityFromRecents(taskId,
565                     activityOptions.toBundle());
566         } catch (RemoteException e) {
567             Slog.e(TAG, "Failed to launch task", e);
568         }
569     }
570 
571     /**
572      * See {@link #startShortcut(String, String, int, Bundle, UserHandle)}
573      * @param instanceId to be used by {@link SplitscreenEventLogger}
574      */
startShortcut(String packageName, String shortcutId, @SplitPosition int position, @Nullable Bundle options, UserHandle user, @NonNull InstanceId instanceId)575     public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
576             @Nullable Bundle options, UserHandle user, @NonNull InstanceId instanceId) {
577         mStageCoordinator.onRequestToSplit(instanceId, ENTER_REASON_LAUNCHER);
578         startShortcut(packageName, shortcutId, position, options, user);
579     }
580 
581     @Override
startShortcut(String packageName, String shortcutId, @SplitPosition int position, @Nullable Bundle options, UserHandle user)582     public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
583             @Nullable Bundle options, UserHandle user) {
584         if (options == null) options = new Bundle();
585         final ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
586 
587         if (samePackage(packageName, getPackageName(reverseSplitPosition(position)),
588                 user.getIdentifier(), getUserId(reverseSplitPosition(position)))) {
589             if (mMultiInstanceHelpher.supportsMultiInstanceSplit(
590                     getShortcutComponent(packageName, shortcutId, user, mLauncherApps))) {
591                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
592                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
593             } else if (isSplitScreenVisible()) {
594                 mStageCoordinator.switchSplitPosition("startShortcut");
595                 return;
596             } else {
597                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
598                         "Cancel entering split as not supporting multi-instances");
599                 Log.w(TAG, splitFailureMessage("startShortcut",
600                         "app package " + packageName + " does not support multi-instance"));
601                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
602                         Toast.LENGTH_SHORT).show();
603                 return;
604             }
605         }
606 
607         mStageCoordinator.startShortcut(packageName, shortcutId, position,
608                 activityOptions.toBundle(), user);
609     }
610 
startShortcutAndTaskWithLegacyTransition(@onNull ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId)611     void startShortcutAndTaskWithLegacyTransition(@NonNull ShortcutInfo shortcutInfo,
612             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
613             @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
614             RemoteAnimationAdapter adapter, InstanceId instanceId) {
615         if (options1 == null) options1 = new Bundle();
616         final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
617 
618         final String packageName1 = shortcutInfo.getPackage();
619         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
620         final int userId1 = shortcutInfo.getUserId();
621         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
622         if (samePackage(packageName1, packageName2, userId1, userId2)) {
623             if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
624                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
625                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
626             } else {
627                 taskId = INVALID_TASK_ID;
628                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
629                         "Cancel entering split as not supporting multi-instances");
630                 Log.w(TAG, splitFailureMessage("startShortcutAndTaskWithLegacyTransition",
631                         "app package " + packageName1 + " does not support multi-instance"));
632                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
633                         Toast.LENGTH_SHORT).show();
634             }
635         }
636 
637         mStageCoordinator.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
638                 activityOptions.toBundle(), taskId, options2, splitPosition, snapPosition, adapter,
639                 instanceId);
640     }
641 
startShortcutAndTask(@onNull ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)642     void startShortcutAndTask(@NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options1,
643             int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
644             @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
645             InstanceId instanceId) {
646         if (options1 == null) options1 = new Bundle();
647         final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
648         final String packageName1 = shortcutInfo.getPackage();
649         // NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
650         //       recents that hasn't launched and is not being organized
651         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
652         final int userId1 = shortcutInfo.getUserId();
653         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
654         if (samePackage(packageName1, packageName2, userId1, userId2)) {
655             if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
656                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
657                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
658             } else {
659                 if (mRecentTasksOptional.isPresent()) {
660                     mRecentTasksOptional.get().removeSplitPair(taskId);
661                 }
662                 taskId = INVALID_TASK_ID;
663                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
664                         "Cancel entering split as not supporting multi-instances");
665                 Log.w(TAG, splitFailureMessage("startShortcutAndTask",
666                         "app package " + packageName1 + " does not support multi-instance"));
667                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
668                         Toast.LENGTH_SHORT).show();
669             }
670         }
671         mStageCoordinator.startShortcutAndTask(shortcutInfo, activityOptions.toBundle(), taskId,
672                 options2, splitPosition, snapPosition, remoteTransition, instanceId);
673     }
674 
675     /**
676      * See {@link #startIntent(PendingIntent, int, Intent, int, Bundle)}
677      * @param instanceId to be used by {@link SplitscreenEventLogger}
678      */
startIntent(PendingIntent intent, int userId, @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options, @NonNull InstanceId instanceId)679     public void startIntent(PendingIntent intent, int userId, @Nullable Intent fillInIntent,
680             @SplitPosition int position, @Nullable Bundle options, @NonNull InstanceId instanceId) {
681         mStageCoordinator.onRequestToSplit(instanceId, ENTER_REASON_LAUNCHER);
682         startIntent(intent, userId, fillInIntent, position, options);
683     }
684 
startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId)685     private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
686             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
687             @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
688             RemoteAnimationAdapter adapter, InstanceId instanceId) {
689         Intent fillInIntent = null;
690         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
691         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
692         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
693         if (samePackage(packageName1, packageName2, userId1, userId2)) {
694             if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent))) {
695                 fillInIntent = new Intent();
696                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
697                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
698             } else {
699                 taskId = INVALID_TASK_ID;
700                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
701                         "Cancel entering split as not supporting multi-instances");
702                 Log.w(TAG, splitFailureMessage("startIntentAndTaskWithLegacyTransition",
703                         "app package " + packageName1 + " does not support multi-instance"));
704                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
705                         Toast.LENGTH_SHORT).show();
706             }
707         }
708         mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
709                 options1, taskId, options2, splitPosition, snapPosition, adapter, instanceId);
710     }
711 
startIntentAndTask(PendingIntent pendingIntent, int userId1, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)712     private void startIntentAndTask(PendingIntent pendingIntent, int userId1,
713             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
714             @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
715             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
716         Intent fillInIntent = null;
717         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
718         // NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
719         //       recents that hasn't launched and is not being organized
720         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
721         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
722         boolean setSecondIntentMultipleTask = false;
723         if (samePackage(packageName1, packageName2, userId1, userId2)) {
724             if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent))) {
725                 setSecondIntentMultipleTask = true;
726                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
727             } else {
728                 if (mRecentTasksOptional.isPresent()) {
729                     mRecentTasksOptional.get().removeSplitPair(taskId);
730                 }
731                 taskId = INVALID_TASK_ID;
732                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
733                         "Cancel entering split as not supporting multi-instances");
734                 Log.w(TAG, splitFailureMessage("startIntentAndTask",
735                         "app package " + packageName1 + " does not support multi-instance"));
736                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
737                         Toast.LENGTH_SHORT).show();
738             }
739         }
740         if (options2 != null) {
741             Intent widgetIntent = options2.getParcelable(KEY_EXTRA_WIDGET_INTENT, Intent.class);
742             fillInIntent = resolveWidgetFillinIntent(widgetIntent, setSecondIntentMultipleTask);
743         }
744         mStageCoordinator.startIntentAndTask(pendingIntent, fillInIntent, options1, taskId,
745                 options2, splitPosition, snapPosition, remoteTransition, instanceId);
746     }
747 
startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId)748     private void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
749             @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
750             PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
751             @Nullable Bundle options2, @SplitPosition int splitPosition,
752             @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
753             InstanceId instanceId) {
754         Intent fillInIntent1 = null;
755         Intent fillInIntent2 = null;
756         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
757         final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
758         if (samePackage(packageName1, packageName2, userId1, userId2)) {
759             if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
760                 fillInIntent1 = new Intent();
761                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
762                 fillInIntent2 = new Intent();
763                 fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
764                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
765             } else {
766                 pendingIntent2 = null;
767                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
768                         "Cancel entering split as not supporting multi-instances");
769                 Log.w(TAG, splitFailureMessage("startIntentsWithLegacyTransition",
770                         "app package " + packageName1 + " does not support multi-instance"));
771                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
772                         Toast.LENGTH_SHORT).show();
773             }
774         }
775         mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1,
776                 shortcutInfo1, options1, pendingIntent2, fillInIntent2, shortcutInfo2, options2,
777                 splitPosition, snapPosition, adapter, instanceId);
778     }
779 
startIntents(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)780     private void startIntents(PendingIntent pendingIntent1, int userId1,
781             @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
782             PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
783             @Nullable Bundle options2, @SplitPosition int splitPosition,
784             @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
785             InstanceId instanceId) {
786         Intent fillInIntent1 = null;
787         Intent fillInIntent2 = null;
788         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
789         final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
790         final ActivityOptions activityOptions1 = options1 != null
791                 ? ActivityOptions.fromBundle(options1) : ActivityOptions.makeBasic();
792         final ActivityOptions activityOptions2 = options2 != null
793                 ? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
794         boolean setSecondIntentMultipleTask = false;
795         if (samePackage(packageName1, packageName2, userId1, userId2)) {
796             if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
797                 fillInIntent1 = new Intent();
798                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
799                 setSecondIntentMultipleTask = true;
800 
801                 if (shortcutInfo1 != null) {
802                     activityOptions1.setApplyMultipleTaskFlagForShortcut(true);
803                 }
804                 if (shortcutInfo2 != null) {
805                     activityOptions2.setApplyMultipleTaskFlagForShortcut(true);
806                 }
807                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
808             } else {
809                 pendingIntent2 = null;
810                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
811                         "Cancel entering split as not supporting multi-instances");
812                 Log.w(TAG, splitFailureMessage("startIntents",
813                         "app package " + packageName1 + " does not support multi-instance"));
814                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
815                         Toast.LENGTH_SHORT).show();
816             }
817         }
818         if (options2 != null) {
819             Intent widgetIntent = options2.getParcelable(KEY_EXTRA_WIDGET_INTENT, Intent.class);
820             fillInIntent2 = resolveWidgetFillinIntent(widgetIntent, setSecondIntentMultipleTask);
821         }
822         mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1,
823                 activityOptions1.toBundle(), pendingIntent2, fillInIntent2, shortcutInfo2,
824                 activityOptions2.toBundle(), splitPosition, snapPosition, remoteTransition,
825                 instanceId);
826     }
827 
828     @Override
startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options)829     public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent,
830             @SplitPosition int position, @Nullable Bundle options) {
831         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
832                 "startIntent(): intent=%s user=%d fillInIntent=%s position=%d", intent, userId1,
833                 fillInIntent, position);
834         // Flag this as a no-user-action launch to prevent sending user leaving event to the current
835         // top activity since it's going to be put into another side of the split. This prevents the
836         // current top activity from going into pip mode due to user leaving event.
837         if (fillInIntent == null) fillInIntent = new Intent();
838         fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
839 
840         final String packageName1 = SplitScreenUtils.getPackageName(intent);
841         final String packageName2 = getPackageName(reverseSplitPosition(position));
842         final int userId2 = getUserId(reverseSplitPosition(position));
843         final ComponentName component = intent.getIntent().getComponent();
844 
845         // To prevent accumulating large number of instances in the background, reuse task
846         // in the background. If we don't explicitly reuse, new may be created even if the app
847         // isn't multi-instance because WM won't automatically remove/reuse the previous instance
848         final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
849                 .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1))
850                 .orElse(null);
851         if (taskInfo != null) {
852             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
853                     "Found suitable background task=%s", taskInfo);
854             if (ENABLE_SHELL_TRANSITIONS) {
855                 mStageCoordinator.startTask(taskInfo.taskId, position, options);
856             } else {
857                 startTask(taskInfo.taskId, position, options);
858             }
859             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Start task in background");
860             return;
861         }
862         if (samePackage(packageName1, packageName2, userId1, userId2)) {
863             if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(intent))) {
864                 // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of
865                 // the split and there is no reusable background task.
866                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
867                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
868             } else if (isSplitScreenVisible()) {
869                 mStageCoordinator.switchSplitPosition("startIntent");
870                 return;
871             } else {
872                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
873                         "Cancel entering split as not supporting multi-instances");
874                 Log.w(TAG, splitFailureMessage("startIntent",
875                         "app package " + packageName1 + " does not support multi-instance"));
876                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
877                         Toast.LENGTH_SHORT).show();
878                 return;
879             }
880         }
881 
882         mStageCoordinator.startIntent(intent, fillInIntent, position, options);
883     }
884 
885     /** Retrieve package name of a specific split position if split screen is activated, otherwise
886      *  returns the package name of the top running task. */
887     @Nullable
getPackageName(@plitPosition int position)888     private String getPackageName(@SplitPosition int position) {
889         ActivityManager.RunningTaskInfo taskInfo;
890         if (isSplitScreenVisible()) {
891             taskInfo = getTaskInfo(position);
892         } else {
893             taskInfo = mRecentTasksOptional
894                     .map(recentTasks -> recentTasks.getTopRunningTask())
895                     .orElse(null);
896             if (!isValidToSplit(taskInfo)) {
897                 return null;
898             }
899         }
900 
901         return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : null;
902     }
903 
904     /** Retrieve user id of a specific split position if split screen is activated, otherwise
905      *  returns the user id of the top running task. */
getUserId(@plitPosition int position)906     private int getUserId(@SplitPosition int position) {
907         ActivityManager.RunningTaskInfo taskInfo;
908         if (isSplitScreenVisible()) {
909             taskInfo = getTaskInfo(position);
910         } else {
911             taskInfo = mRecentTasksOptional
912                     .map(recentTasks -> recentTasks.getTopRunningTask())
913                     .orElse(null);
914             if (!isValidToSplit(taskInfo)) {
915                 return -1;
916             }
917         }
918 
919         return taskInfo != null ? taskInfo.userId : -1;
920     }
921 
922     /**
923      * Determines whether the widgetIntent needs to be modified if multiple tasks of its
924      * corresponding package/app are supported. There are 4 possible paths:
925      *  <li> We select a widget for second app which is the same as the first app </li>
926      *  <li> We select a widget for second app which is different from the first app </li>
927      *  <li> No widgets involved, we select a second app that is the same as first app </li>
928      *  <li> No widgets involved, we select a second app that is different from the first app
929      *       (returns null) </li>
930      *
931      * @return an {@link Intent} with the appropriate {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}
932      *         added on or not depending on {@param launchMultipleTasks}.
933      */
934     @Nullable
resolveWidgetFillinIntent(@ullable Intent widgetIntent, boolean launchMultipleTasks)935     private Intent resolveWidgetFillinIntent(@Nullable Intent widgetIntent,
936             boolean launchMultipleTasks) {
937         Intent fillInIntent2 = null;
938         if (launchMultipleTasks && widgetIntent != null) {
939             fillInIntent2 = widgetIntent;
940             fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
941         } else if (widgetIntent != null) {
942             fillInIntent2 = widgetIntent;
943         } else if (launchMultipleTasks) {
944             fillInIntent2 = new Intent();
945             fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
946         }
947         return fillInIntent2;
948     }
949 
onGoingToRecentsLegacy(RemoteAnimationTarget[] apps)950     RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
951         if (ENABLE_SHELL_TRANSITIONS) return null;
952 
953         if (isSplitScreenVisible()) {
954             // Evict child tasks except the top visible one under split root to ensure it could be
955             // launched as full screen when switching to it on recents.
956             final WindowContainerTransaction wct = new WindowContainerTransaction();
957             mStageCoordinator.prepareEvictInvisibleChildTasks(wct);
958             mSyncQueue.queue(wct);
959         } else {
960             return null;
961         }
962 
963         SurfaceControl.Transaction t = mTransactionPool.acquire();
964         if (mGoingToRecentsTasksLayer != null) {
965             t.remove(mGoingToRecentsTasksLayer);
966         }
967         mGoingToRecentsTasksLayer = reparentSplitTasksForAnimation(apps, t,
968                 "SplitScreenController#onGoingToRecentsLegacy" /* callsite */);
969         t.apply();
970         mTransactionPool.release(t);
971 
972         return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
973     }
974 
onStartingSplitLegacy(RemoteAnimationTarget[] apps)975     RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
976         if (ENABLE_SHELL_TRANSITIONS) return null;
977 
978         int openingApps = 0;
979         for (int i = 0; i < apps.length; ++i) {
980             if (apps[i].mode == MODE_OPENING) openingApps++;
981         }
982         if (openingApps < 2) {
983             // Not having enough apps to enter split screen
984             return null;
985         }
986 
987         SurfaceControl.Transaction t = mTransactionPool.acquire();
988         if (mStartingSplitTasksLayer != null) {
989             t.remove(mStartingSplitTasksLayer);
990         }
991         mStartingSplitTasksLayer = reparentSplitTasksForAnimation(apps, t,
992                 "SplitScreenController#onStartingSplitLegacy" /* callsite */);
993         t.apply();
994         mTransactionPool.release(t);
995 
996         try {
997             return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
998         } finally {
999             for (RemoteAnimationTarget appTarget : apps) {
1000                 if (appTarget.leash != null) {
1001                     appTarget.leash.release();
1002                 }
1003             }
1004         }
1005     }
1006 
reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps, SurfaceControl.Transaction t, String callsite)1007     private SurfaceControl reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
1008             SurfaceControl.Transaction t, String callsite) {
1009         final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
1010                 .setContainerLayer()
1011                 .setName("RecentsAnimationSplitTasks")
1012                 .setHidden(false)
1013                 .setCallsite(callsite);
1014         mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
1015         final SurfaceControl splitTasksLayer = builder.build();
1016 
1017         for (int i = 0; i < apps.length; ++i) {
1018             final RemoteAnimationTarget appTarget = apps[i];
1019             t.reparent(appTarget.leash, splitTasksLayer);
1020             t.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
1021                     appTarget.screenSpaceBounds.top);
1022         }
1023         return splitTasksLayer;
1024     }
1025     /**
1026      * Drop callback when splitscreen is entered.
1027      */
onDroppedToSplit(@plitPosition int position, InstanceId dragSessionId)1028     public void onDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
1029         mStageCoordinator.onDroppedToSplit(position, dragSessionId);
1030     }
1031 
switchSplitPosition(String reason)1032     void switchSplitPosition(String reason) {
1033         if (isSplitScreenVisible()) {
1034             mStageCoordinator.switchSplitPosition(reason);
1035         }
1036     }
1037 
1038     /**
1039      * Return the {@param exitReason} as a string.
1040      */
exitReasonToString(int exitReason)1041     public static String exitReasonToString(int exitReason) {
1042         switch (exitReason) {
1043             case EXIT_REASON_UNKNOWN:
1044                 return "UNKNOWN_EXIT";
1045             case EXIT_REASON_DRAG_DIVIDER:
1046                 return "DRAG_DIVIDER";
1047             case EXIT_REASON_RETURN_HOME:
1048                 return "RETURN_HOME";
1049             case EXIT_REASON_SCREEN_LOCKED:
1050                 return "SCREEN_LOCKED";
1051             case EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP:
1052                 return "SCREEN_LOCKED_SHOW_ON_TOP";
1053             case EXIT_REASON_DEVICE_FOLDED:
1054                 return "DEVICE_FOLDED";
1055             case EXIT_REASON_ROOT_TASK_VANISHED:
1056                 return "ROOT_TASK_VANISHED";
1057             case EXIT_REASON_APP_FINISHED:
1058                 return "APP_FINISHED";
1059             case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
1060                 return "APP_DOES_NOT_SUPPORT_MULTIWINDOW";
1061             case EXIT_REASON_CHILD_TASK_ENTER_PIP:
1062                 return "CHILD_TASK_ENTER_PIP";
1063             case EXIT_REASON_RECREATE_SPLIT:
1064                 return "RECREATE_SPLIT";
1065             case EXIT_REASON_DESKTOP_MODE:
1066                 return "DESKTOP_MODE";
1067             case EXIT_REASON_FULLSCREEN_REQUEST:
1068                 return "FULLSCREEN_REQUEST";
1069             default:
1070                 return "unknown reason, reason int = " + exitReason;
1071         }
1072     }
1073 
dump(@onNull PrintWriter pw, String prefix)1074     public void dump(@NonNull PrintWriter pw, String prefix) {
1075         pw.println(prefix + TAG);
1076         if (mStageCoordinator != null) {
1077             mStageCoordinator.dump(pw, prefix);
1078         }
1079     }
1080 
1081     /**
1082      * The interface for calls from outside the Shell, within the host process.
1083      */
1084     @ExternalThread
1085     private class SplitScreenImpl implements SplitScreen {
1086         private final ArrayMap<SplitScreenListener, Executor> mExecutors = new ArrayMap<>();
1087         private final SplitScreen.SplitScreenListener mListener = new SplitScreenListener() {
1088             @Override
1089             public void onStagePositionChanged(int stage, int position) {
1090                 for (int i = 0; i < mExecutors.size(); i++) {
1091                     final int index = i;
1092                     mExecutors.valueAt(index).execute(() -> {
1093                         mExecutors.keyAt(index).onStagePositionChanged(stage, position);
1094                     });
1095                 }
1096             }
1097 
1098             @Override
1099             public void onTaskStageChanged(int taskId, int stage, boolean visible) {
1100                 for (int i = 0; i < mExecutors.size(); i++) {
1101                     final int index = i;
1102                     mExecutors.valueAt(index).execute(() -> {
1103                         mExecutors.keyAt(index).onTaskStageChanged(taskId, stage, visible);
1104                     });
1105                 }
1106             }
1107 
1108             @Override
1109             public void onSplitBoundsChanged(Rect rootBounds, Rect mainBounds, Rect sideBounds) {
1110                 for (int i = 0; i < mExecutors.size(); i++) {
1111                     final int index = i;
1112                     mExecutors.valueAt(index).execute(() -> {
1113                         mExecutors.keyAt(index).onSplitBoundsChanged(rootBounds, mainBounds,
1114                                 sideBounds);
1115                     });
1116                 }
1117             }
1118 
1119             @Override
1120             public void onSplitVisibilityChanged(boolean visible) {
1121                 for (int i = 0; i < mExecutors.size(); i++) {
1122                     final int index = i;
1123                     mExecutors.valueAt(index).execute(() -> {
1124                         mExecutors.keyAt(index).onSplitVisibilityChanged(visible);
1125                     });
1126                 }
1127             }
1128         };
1129 
1130         @Override
startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, int splitPosition, int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)1131         public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
1132                 @Nullable Bundle options2, int splitPosition, int snapPosition,
1133                 @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
1134             mMainExecutor.execute(() -> SplitScreenController.this.startTasks(
1135                     taskId1, options1, taskId2, options2, splitPosition, snapPosition,
1136                     remoteTransition, instanceId));
1137         }
1138 
1139         @Override
registerSplitScreenListener(SplitScreenListener listener, Executor executor)1140         public void registerSplitScreenListener(SplitScreenListener listener, Executor executor) {
1141             if (mExecutors.containsKey(listener)) return;
1142 
1143             mMainExecutor.execute(() -> {
1144                 if (mExecutors.size() == 0) {
1145                     SplitScreenController.this.registerSplitScreenListener(mListener);
1146                 }
1147 
1148                 mExecutors.put(listener, executor);
1149             });
1150 
1151             executor.execute(() -> {
1152                 mStageCoordinator.sendStatusToListener(listener);
1153             });
1154         }
1155 
1156         @Override
unregisterSplitScreenListener(SplitScreenListener listener)1157         public void unregisterSplitScreenListener(SplitScreenListener listener) {
1158             mMainExecutor.execute(() -> {
1159                 mExecutors.remove(listener);
1160 
1161                 if (mExecutors.size() == 0) {
1162                     SplitScreenController.this.unregisterSplitScreenListener(mListener);
1163                 }
1164             });
1165         }
1166 
1167         @Override
registerSplitAnimationListener(@onNull SplitInvocationListener listener, @NonNull Executor executor)1168         public void registerSplitAnimationListener(@NonNull SplitInvocationListener listener,
1169                 @NonNull Executor executor) {
1170             mStageCoordinator.registerSplitAnimationListener(listener, executor);
1171         }
1172 
1173         @Override
onFinishedWakingUp()1174         public void onFinishedWakingUp() {
1175             mMainExecutor.execute(SplitScreenController.this::onFinishedWakingUp);
1176         }
1177 
1178         @Override
goToFullscreenFromSplit()1179         public void goToFullscreenFromSplit() {
1180             mMainExecutor.execute(SplitScreenController.this::goToFullscreenFromSplit);
1181         }
1182 
1183         @Override
setSplitscreenFocus(boolean leftOrTop)1184         public void setSplitscreenFocus(boolean leftOrTop) {
1185             mMainExecutor.execute(
1186                     () -> SplitScreenController.this.setSplitscreenFocus(leftOrTop));
1187         }
1188     }
1189 
1190     /**
1191      * The interface for calls from outside the host process.
1192      */
1193     @BinderThread
1194     private static class ISplitScreenImpl extends ISplitScreen.Stub
1195             implements ExternalInterfaceBinder {
1196         private SplitScreenController mController;
1197         private final SingleInstanceRemoteListener<SplitScreenController,
1198                 ISplitScreenListener> mListener;
1199         private final SingleInstanceRemoteListener<SplitScreenController,
1200                 ISplitSelectListener> mSelectListener;
1201         private final SplitScreen.SplitScreenListener mSplitScreenListener =
1202                 new SplitScreen.SplitScreenListener() {
1203                     @Override
1204                     public void onStagePositionChanged(int stage, int position) {
1205                         mListener.call(l -> l.onStagePositionChanged(stage, position));
1206                     }
1207 
1208                     @Override
1209                     public void onTaskStageChanged(int taskId, int stage, boolean visible) {
1210                         mListener.call(l -> l.onTaskStageChanged(taskId, stage, visible));
1211                     }
1212                 };
1213 
1214         private final SplitScreen.SplitSelectListener mSplitSelectListener =
1215                 new SplitScreen.SplitSelectListener() {
1216                     @Override
1217                     public boolean onRequestEnterSplitSelect(
1218                             ActivityManager.RunningTaskInfo taskInfo, int splitPosition,
1219                             Rect taskBounds) {
1220                         AtomicBoolean result = new AtomicBoolean(false);
1221                         mSelectListener.call(l -> result.set(l.onRequestSplitSelect(taskInfo,
1222                                 splitPosition, taskBounds)));
1223                         return result.get();
1224                     }
1225                 };
1226 
ISplitScreenImpl(SplitScreenController controller)1227         public ISplitScreenImpl(SplitScreenController controller) {
1228             mController = controller;
1229             mListener = new SingleInstanceRemoteListener<>(controller,
1230                     c -> c.registerSplitScreenListener(mSplitScreenListener),
1231                     c -> c.unregisterSplitScreenListener(mSplitScreenListener));
1232             mSelectListener = new SingleInstanceRemoteListener<>(controller,
1233                     c -> c.registerSplitSelectListener(mSplitSelectListener),
1234                     c -> c.unregisterSplitSelectListener(mSplitSelectListener));
1235         }
1236 
1237         /**
1238          * Invalidates this instance, preventing future calls from updating the controller.
1239          */
1240         @Override
invalidate()1241         public void invalidate() {
1242             mController = null;
1243             // Unregister the listeners to ensure any binder death recipients are unlinked
1244             mListener.unregister();
1245             mSelectListener.unregister();
1246         }
1247 
1248         @Override
registerSplitScreenListener(ISplitScreenListener listener)1249         public void registerSplitScreenListener(ISplitScreenListener listener) {
1250             executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener",
1251                     (controller) -> mListener.register(listener));
1252         }
1253 
1254         @Override
unregisterSplitScreenListener(ISplitScreenListener listener)1255         public void unregisterSplitScreenListener(ISplitScreenListener listener) {
1256             executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener",
1257                     (controller) -> mListener.unregister());
1258         }
1259 
1260         @Override
registerSplitSelectListener(ISplitSelectListener listener)1261         public void registerSplitSelectListener(ISplitSelectListener listener) {
1262             executeRemoteCallWithTaskPermission(mController, "registerSplitSelectListener",
1263                     (controller) -> mSelectListener.register(listener));
1264         }
1265 
1266         @Override
unregisterSplitSelectListener(ISplitSelectListener listener)1267         public void unregisterSplitSelectListener(ISplitSelectListener listener) {
1268             executeRemoteCallWithTaskPermission(mController, "unregisterSplitSelectListener",
1269                     (controller) -> mSelectListener.unregister());
1270         }
1271 
1272         @Override
exitSplitScreen(int toTopTaskId)1273         public void exitSplitScreen(int toTopTaskId) {
1274             executeRemoteCallWithTaskPermission(mController, "exitSplitScreen",
1275                     (controller) -> controller.exitSplitScreen(toTopTaskId, EXIT_REASON_UNKNOWN));
1276         }
1277 
1278         @Override
exitSplitScreenOnHide(boolean exitSplitScreenOnHide)1279         public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
1280             executeRemoteCallWithTaskPermission(mController, "exitSplitScreenOnHide",
1281                     (controller) -> controller.exitSplitScreenOnHide(exitSplitScreenOnHide));
1282         }
1283 
1284         @Override
removeFromSideStage(int taskId)1285         public void removeFromSideStage(int taskId) {
1286             executeRemoteCallWithTaskPermission(mController, "removeFromSideStage",
1287                     (controller) -> controller.removeFromSideStage(taskId));
1288         }
1289 
1290         @Override
startTask(int taskId, int position, @Nullable Bundle options)1291         public void startTask(int taskId, int position, @Nullable Bundle options) {
1292             executeRemoteCallWithTaskPermission(mController, "startTask",
1293                     (controller) -> controller.startTask(taskId, position, options));
1294         }
1295 
1296         @Override
startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId)1297         public void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1,
1298                 int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition,
1299                 @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
1300                 InstanceId instanceId) {
1301             executeRemoteCallWithTaskPermission(mController, "startTasks",
1302                     (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
1303                             taskId1, options1, taskId2, options2, splitPosition, snapPosition,
1304                             adapter, instanceId));
1305         }
1306 
1307         @Override
startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1, Bundle options1, int taskId, Bundle options2, int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId)1308         public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
1309                 Bundle options1, int taskId, Bundle options2, int splitPosition,
1310                 @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
1311                 InstanceId instanceId) {
1312             executeRemoteCallWithTaskPermission(mController,
1313                     "startIntentAndTaskWithLegacyTransition", (controller) ->
1314                             controller.startIntentAndTaskWithLegacyTransition(pendingIntent,
1315                                     userId1, options1, taskId, options2, splitPosition,
1316                                     snapPosition, adapter, instanceId));
1317         }
1318 
1319         @Override
startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId)1320         public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
1321                 @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
1322                 @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
1323                 RemoteAnimationAdapter adapter, InstanceId instanceId) {
1324             executeRemoteCallWithTaskPermission(mController,
1325                     "startShortcutAndTaskWithLegacyTransition", (controller) ->
1326                             controller.startShortcutAndTaskWithLegacyTransition(
1327                                     shortcutInfo, options1, taskId, options2, splitPosition,
1328                                     snapPosition, adapter, instanceId));
1329         }
1330 
1331         @Override
startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)1332         public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
1333                 @Nullable Bundle options2, @SplitPosition int splitPosition,
1334                 @PersistentSnapPosition int snapPosition,
1335                 @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
1336             executeRemoteCallWithTaskPermission(mController, "startTasks",
1337                     (controller) -> controller.mStageCoordinator.startTasks(taskId1, options1,
1338                             taskId2, options2, splitPosition, snapPosition, remoteTransition,
1339                             instanceId));
1340         }
1341 
1342         @Override
startIntentAndTask(PendingIntent pendingIntent, int userId1, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)1343         public void startIntentAndTask(PendingIntent pendingIntent, int userId1,
1344                 @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
1345                 @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
1346                 @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
1347             executeRemoteCallWithTaskPermission(mController, "startIntentAndTask",
1348                     (controller) -> controller.startIntentAndTask(pendingIntent, userId1, options1,
1349                             taskId, options2, splitPosition, snapPosition, remoteTransition,
1350                             instanceId));
1351         }
1352 
1353         @Override
startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)1354         public void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
1355                 int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
1356                 @PersistentSnapPosition int snapPosition,
1357                 @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
1358             executeRemoteCallWithTaskPermission(mController, "startShortcutAndTask",
1359                     (controller) -> controller.startShortcutAndTask(shortcutInfo, options1, taskId,
1360                             options2, splitPosition, snapPosition, remoteTransition, instanceId));
1361         }
1362 
1363         @Override
startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId)1364         public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
1365                 @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
1366                 PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
1367                 @Nullable Bundle options2, @SplitPosition int splitPosition,
1368                 @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
1369                 InstanceId instanceId) {
1370             executeRemoteCallWithTaskPermission(mController, "startIntentsWithLegacyTransition",
1371                     (controller) ->
1372                         controller.startIntentsWithLegacyTransition(pendingIntent1, userId1,
1373                                 shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2,
1374                                 options2, splitPosition, snapPosition, adapter, instanceId)
1375                     );
1376         }
1377 
1378         @Override
startIntents(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)1379         public void startIntents(PendingIntent pendingIntent1, int userId1,
1380                 @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
1381                 PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
1382                 @Nullable Bundle options2, @SplitPosition int splitPosition,
1383                 @PersistentSnapPosition int snapPosition,
1384                 @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
1385             executeRemoteCallWithTaskPermission(mController, "startIntents",
1386                     (controller) ->
1387                             controller.startIntents(pendingIntent1, userId1, shortcutInfo1,
1388                                     options1, pendingIntent2, userId2, shortcutInfo2, options2,
1389                                     splitPosition, snapPosition, remoteTransition, instanceId)
1390             );
1391         }
1392 
1393         @Override
startShortcut(String packageName, String shortcutId, int position, @Nullable Bundle options, UserHandle user, InstanceId instanceId)1394         public void startShortcut(String packageName, String shortcutId, int position,
1395                 @Nullable Bundle options, UserHandle user, InstanceId instanceId) {
1396             executeRemoteCallWithTaskPermission(mController, "startShortcut",
1397                     (controller) -> controller.startShortcut(packageName, shortcutId, position,
1398                             options, user, instanceId));
1399         }
1400 
1401         @Override
startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position, @Nullable Bundle options, InstanceId instanceId)1402         public void startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position,
1403                 @Nullable Bundle options, InstanceId instanceId) {
1404             executeRemoteCallWithTaskPermission(mController, "startIntent",
1405                     (controller) -> controller.startIntent(intent, userId, fillInIntent, position,
1406                             options, instanceId));
1407         }
1408 
1409         @Override
onGoingToRecentsLegacy(RemoteAnimationTarget[] apps)1410         public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
1411             final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null};
1412             executeRemoteCallWithTaskPermission(mController, "onGoingToRecentsLegacy",
1413                     (controller) -> out[0] = controller.onGoingToRecentsLegacy(apps),
1414                     true /* blocking */);
1415             return out[0];
1416         }
1417 
1418         @Override
onStartingSplitLegacy(RemoteAnimationTarget[] apps)1419         public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
1420             final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null};
1421             executeRemoteCallWithTaskPermission(mController, "onStartingSplitLegacy",
1422                     (controller) -> out[0] = controller.onStartingSplitLegacy(apps),
1423                     true /* blocking */);
1424             return out[0];
1425         }
1426 
1427         @Override
switchSplitPosition()1428         public void switchSplitPosition() {
1429             executeRemoteCallWithTaskPermission(mController, "switchSplitPosition",
1430                     (controller) -> controller.switchSplitPosition("remoteCall"));
1431         }
1432     }
1433 }
1434