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