• 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.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.ExecutorUtils.executeRemoteCallWithTaskPermission;
28 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
29 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
30 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
31 import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
32 import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
33 import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage;
34 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
35 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
36 import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
37 
38 import android.annotation.NonNull;
39 import android.annotation.Nullable;
40 import android.app.ActivityManager;
41 import android.app.ActivityOptions;
42 import android.app.ActivityTaskManager;
43 import android.app.PendingIntent;
44 import android.app.TaskInfo;
45 import android.content.Context;
46 import android.content.Intent;
47 import android.content.pm.ShortcutInfo;
48 import android.graphics.Rect;
49 import android.os.Bundle;
50 import android.os.RemoteException;
51 import android.os.UserHandle;
52 import android.util.ArrayMap;
53 import android.util.Slog;
54 import android.view.IRemoteAnimationFinishedCallback;
55 import android.view.IRemoteAnimationRunner;
56 import android.view.RemoteAnimationAdapter;
57 import android.view.RemoteAnimationTarget;
58 import android.view.SurfaceControl;
59 import android.view.SurfaceSession;
60 import android.view.WindowManager;
61 import android.widget.Toast;
62 import android.window.RemoteTransition;
63 import android.window.WindowContainerTransaction;
64 
65 import androidx.annotation.BinderThread;
66 import androidx.annotation.IntDef;
67 
68 import com.android.internal.annotations.VisibleForTesting;
69 import com.android.internal.logging.InstanceId;
70 import com.android.internal.protolog.common.ProtoLog;
71 import com.android.launcher3.icons.IconProvider;
72 import com.android.wm.shell.R;
73 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
74 import com.android.wm.shell.ShellTaskOrganizer;
75 import com.android.wm.shell.common.DisplayController;
76 import com.android.wm.shell.common.DisplayImeController;
77 import com.android.wm.shell.common.DisplayInsetsController;
78 import com.android.wm.shell.common.ExternalInterfaceBinder;
79 import com.android.wm.shell.common.RemoteCallable;
80 import com.android.wm.shell.common.ShellExecutor;
81 import com.android.wm.shell.common.SingleInstanceRemoteListener;
82 import com.android.wm.shell.common.SyncTransactionQueue;
83 import com.android.wm.shell.common.TransactionPool;
84 import com.android.wm.shell.common.annotations.ExternalThread;
85 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
86 import com.android.wm.shell.common.split.SplitScreenUtils;
87 import com.android.wm.shell.draganddrop.DragAndDropController;
88 import com.android.wm.shell.draganddrop.DragAndDropPolicy;
89 import com.android.wm.shell.protolog.ShellProtoLogGroup;
90 import com.android.wm.shell.recents.RecentTasksController;
91 import com.android.wm.shell.sysui.KeyguardChangeListener;
92 import com.android.wm.shell.sysui.ShellCommandHandler;
93 import com.android.wm.shell.sysui.ShellController;
94 import com.android.wm.shell.sysui.ShellInit;
95 import com.android.wm.shell.transition.Transitions;
96 
97 import java.io.PrintWriter;
98 import java.lang.annotation.Retention;
99 import java.lang.annotation.RetentionPolicy;
100 import java.util.Optional;
101 import java.util.concurrent.Executor;
102 
103 /**
104  * Class manages split-screen multitasking mode and implements the main interface
105  * {@link SplitScreen}.
106  *
107  * @see StageCoordinator
108  */
109 // TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen.
110 public class SplitScreenController implements DragAndDropPolicy.Starter,
111         RemoteCallable<SplitScreenController>, KeyguardChangeListener {
112     private static final String TAG = SplitScreenController.class.getSimpleName();
113 
114     public static final int EXIT_REASON_UNKNOWN = 0;
115     public static final int EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW = 1;
116     public static final int EXIT_REASON_APP_FINISHED = 2;
117     public static final int EXIT_REASON_DEVICE_FOLDED = 3;
118     public static final int EXIT_REASON_DRAG_DIVIDER = 4;
119     public static final int EXIT_REASON_RETURN_HOME = 5;
120     public static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
121     public static final int EXIT_REASON_SCREEN_LOCKED = 7;
122     public static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
123     public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
124     public static final int EXIT_REASON_RECREATE_SPLIT = 10;
125     public static final int EXIT_REASON_FULLSCREEN_SHORTCUT = 11;
126     @IntDef(value = {
127             EXIT_REASON_UNKNOWN,
128             EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
129             EXIT_REASON_APP_FINISHED,
130             EXIT_REASON_DEVICE_FOLDED,
131             EXIT_REASON_DRAG_DIVIDER,
132             EXIT_REASON_RETURN_HOME,
133             EXIT_REASON_ROOT_TASK_VANISHED,
134             EXIT_REASON_SCREEN_LOCKED,
135             EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP,
136             EXIT_REASON_CHILD_TASK_ENTER_PIP,
137             EXIT_REASON_RECREATE_SPLIT,
138             EXIT_REASON_FULLSCREEN_SHORTCUT,
139     })
140     @Retention(RetentionPolicy.SOURCE)
141     @interface ExitReason{}
142 
143     public static final int ENTER_REASON_UNKNOWN = 0;
144     public static final int ENTER_REASON_MULTI_INSTANCE = 1;
145     public static final int ENTER_REASON_DRAG = 2;
146     public static final int ENTER_REASON_LAUNCHER = 3;
147     /** Acts as a mapping to the actual EnterReasons as defined in the logging proto */
148     @IntDef(value = {
149             ENTER_REASON_MULTI_INSTANCE,
150             ENTER_REASON_DRAG,
151             ENTER_REASON_LAUNCHER,
152             ENTER_REASON_UNKNOWN
153     })
154     public @interface SplitEnterReason {
155     }
156 
157     private final ShellCommandHandler mShellCommandHandler;
158     private final ShellController mShellController;
159     private final ShellTaskOrganizer mTaskOrganizer;
160     private final SyncTransactionQueue mSyncQueue;
161     private final Context mContext;
162     private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
163     private final ShellExecutor mMainExecutor;
164     private final SplitScreenImpl mImpl = new SplitScreenImpl();
165     private final DisplayController mDisplayController;
166     private final DisplayImeController mDisplayImeController;
167     private final DisplayInsetsController mDisplayInsetsController;
168     private final DragAndDropController mDragAndDropController;
169     private final Transitions mTransitions;
170     private final TransactionPool mTransactionPool;
171     private final IconProvider mIconProvider;
172     private final Optional<RecentTasksController> mRecentTasksOptional;
173     private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
174     private final String[] mAppsSupportMultiInstances;
175 
176     @VisibleForTesting
177     StageCoordinator mStageCoordinator;
178 
179     // Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
180     // outside the bounds of the roots by being reparented into a higher level fullscreen container
181     private SurfaceControl mGoingToRecentsTasksLayer;
182     private SurfaceControl mStartingSplitTasksLayer;
183 
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, ShellExecutor mainExecutor)184     public SplitScreenController(Context context,
185             ShellInit shellInit,
186             ShellCommandHandler shellCommandHandler,
187             ShellController shellController,
188             ShellTaskOrganizer shellTaskOrganizer,
189             SyncTransactionQueue syncQueue,
190             RootTaskDisplayAreaOrganizer rootTDAOrganizer,
191             DisplayController displayController,
192             DisplayImeController displayImeController,
193             DisplayInsetsController displayInsetsController,
194             DragAndDropController dragAndDropController,
195             Transitions transitions,
196             TransactionPool transactionPool,
197             IconProvider iconProvider,
198             Optional<RecentTasksController> recentTasks,
199             ShellExecutor mainExecutor) {
200         mShellCommandHandler = shellCommandHandler;
201         mShellController = shellController;
202         mTaskOrganizer = shellTaskOrganizer;
203         mSyncQueue = syncQueue;
204         mContext = context;
205         mRootTDAOrganizer = rootTDAOrganizer;
206         mMainExecutor = mainExecutor;
207         mDisplayController = displayController;
208         mDisplayImeController = displayImeController;
209         mDisplayInsetsController = displayInsetsController;
210         mDragAndDropController = dragAndDropController;
211         mTransitions = transitions;
212         mTransactionPool = transactionPool;
213         mIconProvider = iconProvider;
214         mRecentTasksOptional = recentTasks;
215         mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
216         // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
217         //                    override for this controller from the base module
218         if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
219             shellInit.addInitCallback(this::onInit, this);
220         }
221 
222         // TODO(255224696): Remove the config once having a way for client apps to opt-in
223         //                  multi-instances split.
224         mAppsSupportMultiInstances = mContext.getResources()
225                 .getStringArray(R.array.config_appsSupportMultiInstancesSplit);
226     }
227 
228     @VisibleForTesting
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, RecentTasksController recentTasks, ShellExecutor mainExecutor, StageCoordinator stageCoordinator)229     SplitScreenController(Context context,
230             ShellInit shellInit,
231             ShellCommandHandler shellCommandHandler,
232             ShellController shellController,
233             ShellTaskOrganizer shellTaskOrganizer,
234             SyncTransactionQueue syncQueue,
235             RootTaskDisplayAreaOrganizer rootTDAOrganizer,
236             DisplayController displayController,
237             DisplayImeController displayImeController,
238             DisplayInsetsController displayInsetsController,
239             DragAndDropController dragAndDropController,
240             Transitions transitions,
241             TransactionPool transactionPool,
242             IconProvider iconProvider,
243             RecentTasksController recentTasks,
244             ShellExecutor mainExecutor,
245             StageCoordinator stageCoordinator) {
246         mShellCommandHandler = shellCommandHandler;
247         mShellController = shellController;
248         mTaskOrganizer = shellTaskOrganizer;
249         mSyncQueue = syncQueue;
250         mContext = context;
251         mRootTDAOrganizer = rootTDAOrganizer;
252         mMainExecutor = mainExecutor;
253         mDisplayController = displayController;
254         mDisplayImeController = displayImeController;
255         mDisplayInsetsController = displayInsetsController;
256         mDragAndDropController = dragAndDropController;
257         mTransitions = transitions;
258         mTransactionPool = transactionPool;
259         mIconProvider = iconProvider;
260         mRecentTasksOptional = Optional.of(recentTasks);
261         mStageCoordinator = stageCoordinator;
262         mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
263         shellInit.addInitCallback(this::onInit, this);
264         mAppsSupportMultiInstances = mContext.getResources()
265                 .getStringArray(R.array.config_appsSupportMultiInstancesSplit);
266     }
267 
asSplitScreen()268     public SplitScreen asSplitScreen() {
269         return mImpl;
270     }
271 
createExternalInterface()272     private ExternalInterfaceBinder createExternalInterface() {
273         return new ISplitScreenImpl(this);
274     }
275 
276     /**
277      * This will be called after ShellTaskOrganizer has initialized/registered because of the
278      * dependency order.
279      */
280     @VisibleForTesting
onInit()281     void onInit() {
282         mShellCommandHandler.addDumpCallback(this::dump, this);
283         mShellCommandHandler.addCommandCallback("splitscreen", mSplitScreenShellCommandHandler,
284                 this);
285         mShellController.addKeyguardChangeListener(this);
286         mShellController.addExternalInterface(KEY_EXTRA_SHELL_SPLIT_SCREEN,
287                 this::createExternalInterface, this);
288         if (mStageCoordinator == null) {
289             // TODO: Multi-display
290             mStageCoordinator = createStageCoordinator();
291         }
292         mDragAndDropController.setSplitScreenController(this);
293     }
294 
createStageCoordinator()295     protected StageCoordinator createStageCoordinator() {
296         return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
297                 mTaskOrganizer, mDisplayController, mDisplayImeController,
298                 mDisplayInsetsController, mTransitions, mTransactionPool,
299                 mIconProvider, mMainExecutor, mRecentTasksOptional);
300     }
301 
302     @Override
getContext()303     public Context getContext() {
304         return mContext;
305     }
306 
307     @Override
getRemoteCallExecutor()308     public ShellExecutor getRemoteCallExecutor() {
309         return mMainExecutor;
310     }
311 
isSplitScreenVisible()312     public boolean isSplitScreenVisible() {
313         return mStageCoordinator.isSplitScreenVisible();
314     }
315 
getTransitionHandler()316     public StageCoordinator getTransitionHandler() {
317         return mStageCoordinator;
318     }
319 
320     @Nullable
getTaskInfo(@plitPosition int splitPosition)321     public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
322         if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
323             return null;
324         }
325 
326         final int taskId = mStageCoordinator.getTaskId(splitPosition);
327         return mTaskOrganizer.getRunningTaskInfo(taskId);
328     }
329 
330     /** Check task is under split or not by taskId. */
isTaskInSplitScreen(int taskId)331     public boolean isTaskInSplitScreen(int taskId) {
332         return mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
333     }
334 
335     /** Check split is foreground and task is under split or not by taskId. */
isTaskInSplitScreenForeground(int taskId)336     public boolean isTaskInSplitScreenForeground(int taskId) {
337         return isTaskInSplitScreen(taskId) && isSplitScreenVisible();
338     }
339 
getSplitPosition(int taskId)340     public @SplitPosition int getSplitPosition(int taskId) {
341         return mStageCoordinator.getSplitPosition(taskId);
342     }
343 
moveToSideStage(int taskId, @SplitPosition int sideStagePosition)344     public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
345         return moveToStage(taskId, sideStagePosition, new WindowContainerTransaction());
346     }
347 
348     /**
349      * Update surfaces of the split screen layout based on the current state
350      * @param transaction to write the updates to
351      */
updateSplitScreenSurfaces(SurfaceControl.Transaction transaction)352     public void updateSplitScreenSurfaces(SurfaceControl.Transaction transaction) {
353         mStageCoordinator.updateSurfaces(transaction);
354     }
355 
moveToStage(int taskId, @SplitPosition int stagePosition, WindowContainerTransaction wct)356     private boolean moveToStage(int taskId, @SplitPosition int stagePosition,
357             WindowContainerTransaction wct) {
358         final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
359         if (task == null) {
360             throw new IllegalArgumentException("Unknown taskId" + taskId);
361         }
362         return mStageCoordinator.moveToStage(task, stagePosition, wct);
363     }
364 
removeFromSideStage(int taskId)365     public boolean removeFromSideStage(int taskId) {
366         return mStageCoordinator.removeFromSideStage(taskId);
367     }
368 
setSideStagePosition(@plitPosition int sideStagePosition)369     public void setSideStagePosition(@SplitPosition int sideStagePosition) {
370         mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
371     }
372 
enterSplitScreen(int taskId, boolean leftOrTop)373     public void enterSplitScreen(int taskId, boolean leftOrTop) {
374         enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
375     }
376 
prepareEnterSplitScreen(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo taskInfo, int startPosition)377     public void prepareEnterSplitScreen(WindowContainerTransaction wct,
378             ActivityManager.RunningTaskInfo taskInfo, int startPosition) {
379         mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition);
380     }
381 
finishEnterSplitScreen(SurfaceControl.Transaction t)382     public void finishEnterSplitScreen(SurfaceControl.Transaction t) {
383         mStageCoordinator.finishEnterSplitScreen(t);
384     }
385 
enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct)386     public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
387         final int stagePosition =
388                 leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
389         moveToStage(taskId, stagePosition, wct);
390     }
391 
exitSplitScreen(int toTopTaskId, @ExitReason int exitReason)392     public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
393         mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
394     }
395 
396     @Override
onKeyguardVisibilityChanged(boolean visible, boolean occluded, boolean animatingDismiss)397     public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
398             boolean animatingDismiss) {
399         mStageCoordinator.onKeyguardVisibilityChanged(visible);
400     }
401 
onFinishedWakingUp()402     public void onFinishedWakingUp() {
403         mStageCoordinator.onFinishedWakingUp();
404     }
405 
exitSplitScreenOnHide(boolean exitSplitScreenOnHide)406     public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
407         mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
408     }
409 
getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds)410     public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
411         mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
412     }
413 
registerSplitScreenListener(SplitScreen.SplitScreenListener listener)414     public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
415         mStageCoordinator.registerSplitScreenListener(listener);
416     }
417 
unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener)418     public void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
419         mStageCoordinator.unregisterSplitScreenListener(listener);
420     }
421 
goToFullscreenFromSplit()422     public void goToFullscreenFromSplit() {
423         mStageCoordinator.goToFullscreenFromSplit();
424     }
425 
426     /** Move the specified task to fullscreen, regardless of focus state. */
moveTaskToFullscreen(int taskId)427     public void moveTaskToFullscreen(int taskId) {
428         mStageCoordinator.moveTaskToFullscreen(taskId);
429     }
430 
isLaunchToSplit(TaskInfo taskInfo)431     public boolean isLaunchToSplit(TaskInfo taskInfo) {
432         return mStageCoordinator.isLaunchToSplit(taskInfo);
433     }
434 
getActivateSplitPosition(TaskInfo taskInfo)435     public int getActivateSplitPosition(TaskInfo taskInfo) {
436         return mStageCoordinator.getActivateSplitPosition(taskInfo);
437     }
438 
startTask(int taskId, @SplitPosition int position, @Nullable Bundle options)439     public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
440         final int[] result = new int[1];
441         IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
442             @Override
443             public void onAnimationStart(@WindowManager.TransitionOldType int transit,
444                     RemoteAnimationTarget[] apps,
445                     RemoteAnimationTarget[] wallpapers,
446                     RemoteAnimationTarget[] nonApps,
447                     final IRemoteAnimationFinishedCallback finishedCallback) {
448                 try {
449                     finishedCallback.onAnimationFinished();
450                 } catch (RemoteException e) {
451                     Slog.e(TAG, "Failed to invoke onAnimationFinished", e);
452                 }
453                 if (result[0] == START_SUCCESS || result[0] == START_TASK_TO_FRONT) {
454                     final WindowContainerTransaction evictWct = new WindowContainerTransaction();
455                     mStageCoordinator.prepareEvictNonOpeningChildTasks(position, apps, evictWct);
456                     mSyncQueue.queue(evictWct);
457                 }
458             }
459             @Override
460             public void onAnimationCancelled(boolean isKeyguardOccluded) {
461                 final WindowContainerTransaction evictWct = new WindowContainerTransaction();
462                 mStageCoordinator.prepareEvictInvisibleChildTasks(evictWct);
463                 mSyncQueue.queue(evictWct);
464             }
465         };
466         options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
467                 null /* wct */);
468         RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
469                 0 /* duration */, 0 /* statusBarTransitionDelay */);
470         ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
471         activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
472 
473         try {
474             result[0] = ActivityTaskManager.getService().startActivityFromRecents(taskId,
475                     activityOptions.toBundle());
476         } catch (RemoteException e) {
477             Slog.e(TAG, "Failed to launch task", e);
478         }
479     }
480 
481     /**
482      * See {@link #startShortcut(String, String, int, Bundle, UserHandle)}
483      * @param instanceId to be used by {@link SplitscreenEventLogger}
484      */
startShortcut(String packageName, String shortcutId, @SplitPosition int position, @Nullable Bundle options, UserHandle user, @NonNull InstanceId instanceId)485     public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
486             @Nullable Bundle options, UserHandle user, @NonNull InstanceId instanceId) {
487         mStageCoordinator.onRequestToSplit(instanceId, ENTER_REASON_LAUNCHER);
488         startShortcut(packageName, shortcutId, position, options, user);
489     }
490 
491     @Override
startShortcut(String packageName, String shortcutId, @SplitPosition int position, @Nullable Bundle options, UserHandle user)492     public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
493             @Nullable Bundle options, UserHandle user) {
494         if (options == null) options = new Bundle();
495         final ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
496 
497         if (samePackage(packageName, getPackageName(reverseSplitPosition(position)))) {
498             if (supportMultiInstancesSplit(packageName)) {
499                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
500                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
501             } else if (isSplitScreenVisible()) {
502                 mStageCoordinator.switchSplitPosition("startShortcut");
503                 return;
504             } else {
505                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
506                         "Cancel entering split as not supporting multi-instances");
507                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
508                         Toast.LENGTH_SHORT).show();
509                 return;
510             }
511         }
512 
513         mStageCoordinator.startShortcut(packageName, shortcutId, position,
514                 activityOptions.toBundle(), user);
515     }
516 
startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId)517     void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
518             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
519             @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
520             InstanceId instanceId) {
521         if (options1 == null) options1 = new Bundle();
522         final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
523 
524         final String packageName1 = shortcutInfo.getPackage();
525         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
526         if (samePackage(packageName1, packageName2)) {
527             if (supportMultiInstancesSplit(shortcutInfo.getPackage())) {
528                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
529                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
530             } else {
531                 taskId = INVALID_TASK_ID;
532                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
533                         "Cancel entering split as not supporting multi-instances");
534                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
535                         Toast.LENGTH_SHORT).show();
536             }
537         }
538 
539         mStageCoordinator.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
540                 activityOptions.toBundle(), taskId, options2, splitPosition, splitRatio, adapter,
541                 instanceId);
542     }
543 
544     /**
545      * See {@link #startIntent(PendingIntent, Intent, int, Bundle)}
546      * @param instanceId to be used by {@link SplitscreenEventLogger}
547      */
startIntent(PendingIntent intent, @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options, @NonNull InstanceId instanceId)548     public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
549             @SplitPosition int position, @Nullable Bundle options, @NonNull InstanceId instanceId) {
550         mStageCoordinator.onRequestToSplit(instanceId, ENTER_REASON_LAUNCHER);
551         startIntent(intent, fillInIntent, position, options);
552     }
553 
startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId)554     private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
555             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
556             @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
557             InstanceId instanceId) {
558         Intent fillInIntent = null;
559         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
560         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
561         if (samePackage(packageName1, packageName2)) {
562             if (supportMultiInstancesSplit(packageName1)) {
563                 fillInIntent = new Intent();
564                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
565                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
566             } else {
567                 taskId = INVALID_TASK_ID;
568                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
569                         "Cancel entering split as not supporting multi-instances");
570                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
571                         Toast.LENGTH_SHORT).show();
572             }
573         }
574         mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
575                 options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId);
576     }
577 
startIntentAndTask(PendingIntent pendingIntent, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)578     private void startIntentAndTask(PendingIntent pendingIntent, @Nullable Bundle options1,
579             int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
580             float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
581         Intent fillInIntent = null;
582         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
583         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
584         if (samePackage(packageName1, packageName2)) {
585             if (supportMultiInstancesSplit(packageName1)) {
586                 fillInIntent = new Intent();
587                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
588                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
589             } else {
590                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
591                         "Cancel entering split as not supporting multi-instances");
592                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
593                         Toast.LENGTH_SHORT).show();
594             }
595         }
596         mStageCoordinator.startIntentAndTask(pendingIntent, fillInIntent, options1, taskId,
597                 options2, splitPosition, splitRatio, remoteTransition, instanceId);
598     }
599 
startIntentsWithLegacyTransition(PendingIntent pendingIntent1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId)600     private void startIntentsWithLegacyTransition(PendingIntent pendingIntent1,
601             @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
602             PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2,
603             @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
604             RemoteAnimationAdapter adapter, InstanceId instanceId) {
605         Intent fillInIntent1 = null;
606         Intent fillInIntent2 = null;
607         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
608         final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
609         if (samePackage(packageName1, packageName2)) {
610             if (supportMultiInstancesSplit(packageName1)) {
611                 fillInIntent1 = new Intent();
612                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
613                 fillInIntent2 = new Intent();
614                 fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
615                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
616             } else {
617                 pendingIntent2 = null;
618                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
619                         "Cancel entering split as not supporting multi-instances");
620                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
621                         Toast.LENGTH_SHORT).show();
622             }
623         }
624         mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1,
625                 shortcutInfo1, options1, pendingIntent2, fillInIntent2, shortcutInfo2, options2,
626                 splitPosition, splitRatio, adapter, instanceId);
627     }
628 
629     @Override
startIntent(PendingIntent intent, @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options)630     public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
631             @SplitPosition int position, @Nullable Bundle options) {
632         // Flag this as a no-user-action launch to prevent sending user leaving event to the current
633         // top activity since it's going to be put into another side of the split. This prevents the
634         // current top activity from going into pip mode due to user leaving event.
635         if (fillInIntent == null) fillInIntent = new Intent();
636         fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
637 
638         final String packageName1 = SplitScreenUtils.getPackageName(intent);
639         final String packageName2 = getPackageName(reverseSplitPosition(position));
640         if (SplitScreenUtils.samePackage(packageName1, packageName2)) {
641             if (supportMultiInstancesSplit(packageName1)) {
642                 // To prevent accumulating large number of instances in the background, reuse task
643                 // in the background with priority.
644                 final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
645                         .map(recentTasks -> recentTasks.findTaskInBackground(
646                                 intent.getIntent().getComponent()))
647                         .orElse(null);
648                 if (taskInfo != null) {
649                     startTask(taskInfo.taskId, position, options);
650                     ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
651                             "Start task in background");
652                     return;
653                 }
654 
655                 // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of
656                 // the split and there is no reusable background task.
657                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
658                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
659             } else if (isSplitScreenVisible()) {
660                 mStageCoordinator.switchSplitPosition("startIntent");
661                 return;
662             } else {
663                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
664                         "Cancel entering split as not supporting multi-instances");
665                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
666                         Toast.LENGTH_SHORT).show();
667                 return;
668             }
669         }
670 
671         mStageCoordinator.startIntent(intent, fillInIntent, position, options);
672     }
673 
674     /** Retrieve package name of a specific split position if split screen is activated, otherwise
675      *  returns the package name of the top running task. */
676     @Nullable
getPackageName(@plitPosition int position)677     private String getPackageName(@SplitPosition int position) {
678         ActivityManager.RunningTaskInfo taskInfo;
679         if (isSplitScreenVisible()) {
680             taskInfo = getTaskInfo(position);
681         } else {
682             taskInfo = mRecentTasksOptional
683                     .map(recentTasks -> recentTasks.getTopRunningTask())
684                     .orElse(null);
685             if (!isValidToSplit(taskInfo)) {
686                 return null;
687             }
688         }
689 
690         return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : null;
691     }
692 
693     @VisibleForTesting
supportMultiInstancesSplit(String packageName)694     boolean supportMultiInstancesSplit(String packageName) {
695         if (packageName != null) {
696             for (int i = 0; i < mAppsSupportMultiInstances.length; i++) {
697                 if (mAppsSupportMultiInstances[i].equals(packageName)) {
698                     return true;
699                 }
700             }
701         }
702 
703         return false;
704     }
705 
onGoingToRecentsLegacy(RemoteAnimationTarget[] apps)706     RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
707         if (ENABLE_SHELL_TRANSITIONS) return null;
708 
709         if (isSplitScreenVisible()) {
710             // Evict child tasks except the top visible one under split root to ensure it could be
711             // launched as full screen when switching to it on recents.
712             final WindowContainerTransaction wct = new WindowContainerTransaction();
713             mStageCoordinator.prepareEvictInvisibleChildTasks(wct);
714             mSyncQueue.queue(wct);
715         } else {
716             return null;
717         }
718 
719         SurfaceControl.Transaction t = mTransactionPool.acquire();
720         if (mGoingToRecentsTasksLayer != null) {
721             t.remove(mGoingToRecentsTasksLayer);
722         }
723         mGoingToRecentsTasksLayer = reparentSplitTasksForAnimation(apps, t,
724                 "SplitScreenController#onGoingToRecentsLegacy" /* callsite */);
725         t.apply();
726         mTransactionPool.release(t);
727 
728         return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
729     }
730 
onStartingSplitLegacy(RemoteAnimationTarget[] apps)731     RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
732         if (ENABLE_SHELL_TRANSITIONS) return null;
733 
734         int openingApps = 0;
735         for (int i = 0; i < apps.length; ++i) {
736             if (apps[i].mode == MODE_OPENING) openingApps++;
737         }
738         if (openingApps < 2) {
739             // Not having enough apps to enter split screen
740             return null;
741         }
742 
743         SurfaceControl.Transaction t = mTransactionPool.acquire();
744         if (mStartingSplitTasksLayer != null) {
745             t.remove(mStartingSplitTasksLayer);
746         }
747         mStartingSplitTasksLayer = reparentSplitTasksForAnimation(apps, t,
748                 "SplitScreenController#onStartingSplitLegacy" /* callsite */);
749         t.apply();
750         mTransactionPool.release(t);
751 
752         try {
753             return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
754         } finally {
755             for (RemoteAnimationTarget appTarget : apps) {
756                 if (appTarget.leash != null) {
757                     appTarget.leash.release();
758                 }
759             }
760         }
761     }
762 
reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps, SurfaceControl.Transaction t, String callsite)763     private SurfaceControl reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
764             SurfaceControl.Transaction t, String callsite) {
765         final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
766                 .setContainerLayer()
767                 .setName("RecentsAnimationSplitTasks")
768                 .setHidden(false)
769                 .setCallsite(callsite);
770         mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
771         final SurfaceControl splitTasksLayer = builder.build();
772 
773         for (int i = 0; i < apps.length; ++i) {
774             final RemoteAnimationTarget appTarget = apps[i];
775             t.reparent(appTarget.leash, splitTasksLayer);
776             t.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
777                     appTarget.screenSpaceBounds.top);
778         }
779         return splitTasksLayer;
780     }
781     /**
782      * Drop callback when splitscreen is entered.
783      */
onDroppedToSplit(@plitPosition int position, InstanceId dragSessionId)784     public void onDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
785         mStageCoordinator.onDroppedToSplit(position, dragSessionId);
786     }
787 
788     /**
789      * Return the {@param exitReason} as a string.
790      */
exitReasonToString(int exitReason)791     public static String exitReasonToString(int exitReason) {
792         switch (exitReason) {
793             case EXIT_REASON_UNKNOWN:
794                 return "UNKNOWN_EXIT";
795             case EXIT_REASON_DRAG_DIVIDER:
796                 return "DRAG_DIVIDER";
797             case EXIT_REASON_RETURN_HOME:
798                 return "RETURN_HOME";
799             case EXIT_REASON_SCREEN_LOCKED:
800                 return "SCREEN_LOCKED";
801             case EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP:
802                 return "SCREEN_LOCKED_SHOW_ON_TOP";
803             case EXIT_REASON_DEVICE_FOLDED:
804                 return "DEVICE_FOLDED";
805             case EXIT_REASON_ROOT_TASK_VANISHED:
806                 return "ROOT_TASK_VANISHED";
807             case EXIT_REASON_APP_FINISHED:
808                 return "APP_FINISHED";
809             case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
810                 return "APP_DOES_NOT_SUPPORT_MULTIWINDOW";
811             case EXIT_REASON_CHILD_TASK_ENTER_PIP:
812                 return "CHILD_TASK_ENTER_PIP";
813             case EXIT_REASON_RECREATE_SPLIT:
814                 return "RECREATE_SPLIT";
815             default:
816                 return "unknown reason, reason int = " + exitReason;
817         }
818     }
819 
dump(@onNull PrintWriter pw, String prefix)820     public void dump(@NonNull PrintWriter pw, String prefix) {
821         pw.println(prefix + TAG);
822         if (mStageCoordinator != null) {
823             mStageCoordinator.dump(pw, prefix);
824         }
825     }
826 
827     /**
828      * The interface for calls from outside the Shell, within the host process.
829      */
830     @ExternalThread
831     private class SplitScreenImpl implements SplitScreen {
832         private final ArrayMap<SplitScreenListener, Executor> mExecutors = new ArrayMap<>();
833         private final SplitScreen.SplitScreenListener mListener = new SplitScreenListener() {
834             @Override
835             public void onStagePositionChanged(int stage, int position) {
836                 for (int i = 0; i < mExecutors.size(); i++) {
837                     final int index = i;
838                     mExecutors.valueAt(index).execute(() -> {
839                         mExecutors.keyAt(index).onStagePositionChanged(stage, position);
840                     });
841                 }
842             }
843 
844             @Override
845             public void onTaskStageChanged(int taskId, int stage, boolean visible) {
846                 for (int i = 0; i < mExecutors.size(); i++) {
847                     final int index = i;
848                     mExecutors.valueAt(index).execute(() -> {
849                         mExecutors.keyAt(index).onTaskStageChanged(taskId, stage, visible);
850                     });
851                 }
852             }
853 
854             @Override
855             public void onSplitBoundsChanged(Rect rootBounds, Rect mainBounds, Rect sideBounds) {
856                 for (int i = 0; i < mExecutors.size(); i++) {
857                     final int index = i;
858                     mExecutors.valueAt(index).execute(() -> {
859                         mExecutors.keyAt(index).onSplitBoundsChanged(rootBounds, mainBounds,
860                                 sideBounds);
861                     });
862                 }
863             }
864 
865             @Override
866             public void onSplitVisibilityChanged(boolean visible) {
867                 for (int i = 0; i < mExecutors.size(); i++) {
868                     final int index = i;
869                     mExecutors.valueAt(index).execute(() -> {
870                         mExecutors.keyAt(index).onSplitVisibilityChanged(visible);
871                     });
872                 }
873             }
874         };
875 
876         @Override
registerSplitScreenListener(SplitScreenListener listener, Executor executor)877         public void registerSplitScreenListener(SplitScreenListener listener, Executor executor) {
878             if (mExecutors.containsKey(listener)) return;
879 
880             mMainExecutor.execute(() -> {
881                 if (mExecutors.size() == 0) {
882                     SplitScreenController.this.registerSplitScreenListener(mListener);
883                 }
884 
885                 mExecutors.put(listener, executor);
886             });
887 
888             executor.execute(() -> {
889                 mStageCoordinator.sendStatusToListener(listener);
890             });
891         }
892 
893         @Override
unregisterSplitScreenListener(SplitScreenListener listener)894         public void unregisterSplitScreenListener(SplitScreenListener listener) {
895             mMainExecutor.execute(() -> {
896                 mExecutors.remove(listener);
897 
898                 if (mExecutors.size() == 0) {
899                     SplitScreenController.this.unregisterSplitScreenListener(mListener);
900                 }
901             });
902         }
903 
904         @Override
onFinishedWakingUp()905         public void onFinishedWakingUp() {
906             mMainExecutor.execute(SplitScreenController.this::onFinishedWakingUp);
907         }
908 
909         @Override
goToFullscreenFromSplit()910         public void goToFullscreenFromSplit() {
911             mMainExecutor.execute(SplitScreenController.this::goToFullscreenFromSplit);
912         }
913     }
914 
915     /**
916      * The interface for calls from outside the host process.
917      */
918     @BinderThread
919     private static class ISplitScreenImpl extends ISplitScreen.Stub
920             implements ExternalInterfaceBinder {
921         private SplitScreenController mController;
922         private final SingleInstanceRemoteListener<SplitScreenController,
923                 ISplitScreenListener> mListener;
924         private final SplitScreen.SplitScreenListener mSplitScreenListener =
925                 new SplitScreen.SplitScreenListener() {
926                     @Override
927                     public void onStagePositionChanged(int stage, int position) {
928                         mListener.call(l -> l.onStagePositionChanged(stage, position));
929                     }
930 
931                     @Override
932                     public void onTaskStageChanged(int taskId, int stage, boolean visible) {
933                         mListener.call(l -> l.onTaskStageChanged(taskId, stage, visible));
934                     }
935                 };
936 
ISplitScreenImpl(SplitScreenController controller)937         public ISplitScreenImpl(SplitScreenController controller) {
938             mController = controller;
939             mListener = new SingleInstanceRemoteListener<>(controller,
940                     c -> c.registerSplitScreenListener(mSplitScreenListener),
941                     c -> c.unregisterSplitScreenListener(mSplitScreenListener));
942         }
943 
944         /**
945          * Invalidates this instance, preventing future calls from updating the controller.
946          */
947         @Override
invalidate()948         public void invalidate() {
949             mController = null;
950             // Unregister the listener to ensure any registered binder death recipients are unlinked
951             mListener.unregister();
952         }
953 
954         @Override
registerSplitScreenListener(ISplitScreenListener listener)955         public void registerSplitScreenListener(ISplitScreenListener listener) {
956             executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener",
957                     (controller) -> mListener.register(listener));
958         }
959 
960         @Override
unregisterSplitScreenListener(ISplitScreenListener listener)961         public void unregisterSplitScreenListener(ISplitScreenListener listener) {
962             executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener",
963                     (controller) -> mListener.unregister());
964         }
965 
966         @Override
exitSplitScreen(int toTopTaskId)967         public void exitSplitScreen(int toTopTaskId) {
968             executeRemoteCallWithTaskPermission(mController, "exitSplitScreen",
969                     (controller) -> controller.exitSplitScreen(toTopTaskId, EXIT_REASON_UNKNOWN));
970         }
971 
972         @Override
exitSplitScreenOnHide(boolean exitSplitScreenOnHide)973         public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
974             executeRemoteCallWithTaskPermission(mController, "exitSplitScreenOnHide",
975                     (controller) -> controller.exitSplitScreenOnHide(exitSplitScreenOnHide));
976         }
977 
978         @Override
removeFromSideStage(int taskId)979         public void removeFromSideStage(int taskId) {
980             executeRemoteCallWithTaskPermission(mController, "removeFromSideStage",
981                     (controller) -> controller.removeFromSideStage(taskId));
982         }
983 
984         @Override
startTask(int taskId, int position, @Nullable Bundle options)985         public void startTask(int taskId, int position, @Nullable Bundle options) {
986             executeRemoteCallWithTaskPermission(mController, "startTask",
987                     (controller) -> controller.startTask(taskId, position, options));
988         }
989 
990         @Override
startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId)991         public void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1,
992                 int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition,
993                 float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
994             executeRemoteCallWithTaskPermission(mController, "startTasks",
995                     (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
996                             taskId1, options1, taskId2, options2, splitPosition,
997                             splitRatio, adapter, instanceId));
998         }
999 
1000         @Override
startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Bundle options1, int taskId, Bundle options2, int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId)1001         public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
1002                 Bundle options1, int taskId, Bundle options2, int splitPosition, float splitRatio,
1003                 RemoteAnimationAdapter adapter, InstanceId instanceId) {
1004             executeRemoteCallWithTaskPermission(mController,
1005                     "startIntentAndTaskWithLegacyTransition", (controller) ->
1006                             controller.startIntentAndTaskWithLegacyTransition(pendingIntent,
1007                                     options1, taskId, options2, splitPosition, splitRatio, adapter,
1008                                     instanceId));
1009         }
1010 
1011         @Override
startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId)1012         public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
1013                 @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
1014                 @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
1015                 InstanceId instanceId) {
1016             executeRemoteCallWithTaskPermission(mController,
1017                     "startShortcutAndTaskWithLegacyTransition", (controller) ->
1018                             controller.startShortcutAndTaskWithLegacyTransition(
1019                                     shortcutInfo, options1, taskId, options2, splitPosition,
1020                                     splitRatio, adapter, instanceId));
1021         }
1022 
1023         @Override
startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)1024         public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
1025                 @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
1026                 @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
1027             executeRemoteCallWithTaskPermission(mController, "startTasks",
1028                     (controller) -> controller.mStageCoordinator.startTasks(taskId1, options1,
1029                             taskId2, options2, splitPosition, splitRatio, remoteTransition,
1030                             instanceId));
1031         }
1032 
1033         @Override
startIntentAndTask(PendingIntent pendingIntent, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)1034         public void startIntentAndTask(PendingIntent pendingIntent, @Nullable Bundle options1,
1035                 int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
1036                 float splitRatio, @Nullable RemoteTransition remoteTransition,
1037                 InstanceId instanceId) {
1038             executeRemoteCallWithTaskPermission(mController, "startIntentAndTask",
1039                     (controller) -> controller.startIntentAndTask(pendingIntent, options1, taskId,
1040                             options2, splitPosition, splitRatio, remoteTransition, instanceId));
1041         }
1042 
1043         @Override
startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)1044         public void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
1045                 int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
1046                 float splitRatio, @Nullable RemoteTransition remoteTransition,
1047                 InstanceId instanceId) {
1048             executeRemoteCallWithTaskPermission(mController, "startShortcutAndTask",
1049                     (controller) -> controller.mStageCoordinator.startShortcutAndTask(shortcutInfo,
1050                             options1, taskId, options2, splitPosition, splitRatio, remoteTransition,
1051                             instanceId));
1052         }
1053 
1054         @Override
startIntentsWithLegacyTransition(PendingIntent pendingIntent1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId)1055         public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1,
1056                 @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
1057                 PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2,
1058                 @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
1059                 RemoteAnimationAdapter adapter, InstanceId instanceId) {
1060             executeRemoteCallWithTaskPermission(mController, "startIntentsWithLegacyTransition",
1061                     (controller) ->
1062                         controller.startIntentsWithLegacyTransition(pendingIntent1, shortcutInfo1,
1063                                 options1, pendingIntent2, shortcutInfo2, options2, splitPosition,
1064                                 splitRatio, adapter, instanceId)
1065                     );
1066         }
1067 
1068         @Override
startIntents(PendingIntent pendingIntent1, @Nullable Bundle options1, PendingIntent pendingIntent2, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId)1069         public void startIntents(PendingIntent pendingIntent1, @Nullable Bundle options1,
1070                 PendingIntent pendingIntent2, @Nullable Bundle options2,
1071                 @SplitPosition int splitPosition, float splitRatio,
1072                 @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
1073             // TODO(b/259368992): To be implemented.
1074         }
1075 
1076         @Override
startShortcut(String packageName, String shortcutId, int position, @Nullable Bundle options, UserHandle user, InstanceId instanceId)1077         public void startShortcut(String packageName, String shortcutId, int position,
1078                 @Nullable Bundle options, UserHandle user, InstanceId instanceId) {
1079             executeRemoteCallWithTaskPermission(mController, "startShortcut",
1080                     (controller) -> controller.startShortcut(packageName, shortcutId, position,
1081                             options, user, instanceId));
1082         }
1083 
1084         @Override
startIntent(PendingIntent intent, Intent fillInIntent, int position, @Nullable Bundle options, InstanceId instanceId)1085         public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
1086                 @Nullable Bundle options, InstanceId instanceId) {
1087             executeRemoteCallWithTaskPermission(mController, "startIntent",
1088                     (controller) -> controller.startIntent(intent, fillInIntent, position, options,
1089                             instanceId));
1090         }
1091 
1092         @Override
onGoingToRecentsLegacy(RemoteAnimationTarget[] apps)1093         public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
1094             final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null};
1095             executeRemoteCallWithTaskPermission(mController, "onGoingToRecentsLegacy",
1096                     (controller) -> out[0] = controller.onGoingToRecentsLegacy(apps),
1097                     true /* blocking */);
1098             return out[0];
1099         }
1100 
1101         @Override
onStartingSplitLegacy(RemoteAnimationTarget[] apps)1102         public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
1103             final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null};
1104             executeRemoteCallWithTaskPermission(mController, "onStartingSplitLegacy",
1105                     (controller) -> out[0] = controller.onStartingSplitLegacy(apps),
1106                     true /* blocking */);
1107             return out[0];
1108         }
1109     }
1110 }
1111