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.server.wm; 18 19 import static android.Manifest.permission.START_TASKS_FROM_RECENTS; 20 import static android.app.ActivityManager.isStartResultSuccessful; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 23 import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS; 24 import static android.view.Display.DEFAULT_DISPLAY; 25 import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS; 26 import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE; 27 import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; 28 import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT; 29 import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE; 30 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK; 31 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; 32 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK; 33 import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; 34 import static android.window.TaskFragmentOperation.OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT; 35 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; 36 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS; 37 import static android.window.TaskFragmentOperation.OP_TYPE_SET_COMPANION_TASK_FRAGMENT; 38 import static android.window.TaskFragmentOperation.OP_TYPE_SET_DECOR_SURFACE_BOOSTED; 39 import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK; 40 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION; 41 import static android.window.TaskFragmentOperation.OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH; 42 import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED; 43 import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS; 44 import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT; 45 import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN; 46 import static android.window.WindowContainerTransaction.Change.CHANGE_FOCUSABLE; 47 import static android.window.WindowContainerTransaction.Change.CHANGE_FORCE_TRANSLUCENT; 48 import static android.window.WindowContainerTransaction.Change.CHANGE_HIDDEN; 49 import static android.window.WindowContainerTransaction.Change.CHANGE_RELATIVE_BOUNDS; 50 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER; 51 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION; 52 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; 53 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS; 54 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY; 55 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK; 56 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK; 57 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT; 58 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER; 59 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK; 60 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; 61 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; 62 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER; 63 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS; 64 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP; 65 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT; 66 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; 67 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH; 68 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT; 69 70 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; 71 import static com.android.server.wm.ActivityRecord.State.PAUSING; 72 import static com.android.server.wm.ActivityRecord.State.RESUMED; 73 import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission; 74 import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; 75 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK; 76 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; 77 import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK; 78 import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_TASK_FRAGMENT; 79 import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED; 80 import static com.android.server.wm.TaskFragment.FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG; 81 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; 82 import static com.android.server.wm.WindowContainer.POSITION_TOP; 83 84 import android.annotation.NonNull; 85 import android.annotation.Nullable; 86 import android.app.ActivityManager; 87 import android.app.ActivityOptions; 88 import android.app.WindowConfiguration; 89 import android.content.ActivityNotFoundException; 90 import android.content.Intent; 91 import android.content.pm.ActivityInfo; 92 import android.content.res.Configuration; 93 import android.graphics.Point; 94 import android.graphics.Rect; 95 import android.os.Binder; 96 import android.os.Bundle; 97 import android.os.Handler; 98 import android.os.IBinder; 99 import android.os.Looper; 100 import android.os.Parcel; 101 import android.os.RemoteException; 102 import android.util.AndroidRuntimeException; 103 import android.util.ArrayMap; 104 import android.util.ArraySet; 105 import android.util.Slog; 106 import android.view.RemoteAnimationAdapter; 107 import android.view.SurfaceControl; 108 import android.view.WindowManager; 109 import android.window.IDisplayAreaOrganizerController; 110 import android.window.ITaskFragmentOrganizer; 111 import android.window.ITaskFragmentOrganizerController; 112 import android.window.ITaskOrganizerController; 113 import android.window.ITransitionMetricsReporter; 114 import android.window.ITransitionPlayer; 115 import android.window.IWindowContainerTransactionCallback; 116 import android.window.IWindowOrganizerController; 117 import android.window.RemoteTransition; 118 import android.window.TaskFragmentAnimationParams; 119 import android.window.TaskFragmentCreationParams; 120 import android.window.TaskFragmentOperation; 121 import android.window.TaskFragmentOrganizerToken; 122 import android.window.WindowContainerToken; 123 import android.window.WindowContainerTransaction; 124 125 import com.android.internal.annotations.VisibleForTesting; 126 import com.android.internal.protolog.ProtoLogGroup; 127 import com.android.internal.protolog.common.ProtoLog; 128 import com.android.internal.util.ArrayUtils; 129 import com.android.server.LocalServices; 130 import com.android.server.pm.LauncherAppsService.LauncherAppsServiceInternal; 131 import com.android.window.flags.Flags; 132 133 import java.util.ArrayList; 134 import java.util.HashMap; 135 import java.util.Iterator; 136 import java.util.List; 137 import java.util.Map; 138 import java.util.Objects; 139 import java.util.function.IntSupplier; 140 141 /** 142 * Server side implementation for the interface for organizing windows 143 * @see android.window.WindowOrganizer 144 */ 145 class WindowOrganizerController extends IWindowOrganizerController.Stub 146 implements BLASTSyncEngine.TransactionReadyListener { 147 148 private static final String TAG = "WindowOrganizerController"; 149 150 private static final int TRANSACT_EFFECTS_NONE = 0; 151 /** Flag indicating that an applied transaction may have effected lifecycle */ 152 private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1; 153 private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1; 154 155 /** 156 * Masks specifying which configurations task-organizers can control. Incoming transactions 157 * will be filtered to only include these. 158 */ 159 static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION 160 | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE 161 | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_DENSITY; 162 static final int CONTROLLABLE_WINDOW_CONFIGS = WINDOW_CONFIG_BOUNDS 163 | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; 164 165 private final ActivityTaskManagerService mService; 166 private final WindowManagerGlobalLock mGlobalLock; 167 168 private final HashMap<Integer, IWindowContainerTransactionCallback> 169 mTransactionCallbacksByPendingSyncId = new HashMap(); 170 171 final TaskOrganizerController mTaskOrganizerController; 172 final DisplayAreaOrganizerController mDisplayAreaOrganizerController; 173 final TaskFragmentOrganizerController mTaskFragmentOrganizerController; 174 175 final TransitionController mTransitionController; 176 177 /** 178 * A Map which manages the relationship between 179 * {@link TaskFragmentCreationParams#getFragmentToken()} and {@link TaskFragment} 180 */ 181 @VisibleForTesting 182 final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>(); 183 184 private final Rect mTmpBounds0 = new Rect(); 185 private final Rect mTmpBounds1 = new Rect(); 186 WindowOrganizerController(ActivityTaskManagerService atm)187 WindowOrganizerController(ActivityTaskManagerService atm) { 188 mService = atm; 189 mGlobalLock = atm.mGlobalLock; 190 mTaskOrganizerController = new TaskOrganizerController(mService); 191 mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService); 192 mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm, this); 193 mTransitionController = new TransitionController(atm); 194 } 195 getTransitionController()196 TransitionController getTransitionController() { 197 return mTransitionController; 198 } 199 200 @Override onTransact(int code, Parcel data, Parcel reply, int flags)201 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 202 throws RemoteException { 203 try { 204 return super.onTransact(code, data, reply, flags); 205 } catch (RuntimeException e) { 206 throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e); 207 } 208 } 209 210 @Override applyTransaction(WindowContainerTransaction t)211 public void applyTransaction(WindowContainerTransaction t) { 212 if (t == null) { 213 throw new IllegalArgumentException("Null transaction passed to applyTransaction"); 214 } 215 enforceTaskPermission("applyTransaction()"); 216 final CallerInfo caller = new CallerInfo(); 217 final long ident = Binder.clearCallingIdentity(); 218 try { 219 synchronized (mGlobalLock) { 220 applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller); 221 } 222 } finally { 223 Binder.restoreCallingIdentity(ident); 224 } 225 } 226 227 @Override applySyncTransaction(WindowContainerTransaction t, IWindowContainerTransactionCallback callback)228 public int applySyncTransaction(WindowContainerTransaction t, 229 IWindowContainerTransactionCallback callback) { 230 if (t == null) { 231 throw new IllegalArgumentException("Null transaction passed to applySyncTransaction"); 232 } 233 enforceTaskPermission("applySyncTransaction()"); 234 final CallerInfo caller = new CallerInfo(); 235 final long ident = Binder.clearCallingIdentity(); 236 try { 237 synchronized (mGlobalLock) { 238 if (callback == null) { 239 applyTransaction(t, -1 /* syncId*/, null /*transition*/, caller); 240 return -1; 241 } 242 243 /** 244 * If callback is non-null we are looking to synchronize this transaction by 245 * collecting all the results in to a SurfaceFlinger transaction and then delivering 246 * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the 247 * details of the operation. But at a high level we create a sync operation with a 248 * given ID and an associated callback. Then we notify each WindowContainer in this 249 * WindowContainer transaction that it is participating in a sync operation with 250 * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady" 251 * which means that we have added everything to the set. At any point after this, 252 * all the WindowContainers will eventually finish applying their changes and notify 253 * the BLASTSyncEngine which will deliver the Transaction to the callback. 254 */ 255 final BLASTSyncEngine.SyncGroup syncGroup = prepareSyncWithOrganizer(callback); 256 final int syncId = syncGroup.mSyncId; 257 if (mTransitionController.isShellTransitionsEnabled()) { 258 mTransitionController.startLegacySyncOrQueue(syncGroup, (deferred) -> { 259 applyTransaction(t, syncId, null /* transition */, caller, deferred); 260 setSyncReady(syncId); 261 }); 262 } else { 263 if (!mService.mWindowManager.mSyncEngine.hasActiveSync()) { 264 mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup); 265 applyTransaction(t, syncId, null /*transition*/, caller); 266 setSyncReady(syncId); 267 } else { 268 // Because the BLAST engine only supports one sync at a time, queue the 269 // transaction. 270 mService.mWindowManager.mSyncEngine.queueSyncSet( 271 () -> mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup), 272 () -> { 273 applyTransaction(t, syncId, null /*transition*/, caller); 274 setSyncReady(syncId); 275 }); 276 } 277 } 278 return syncId; 279 } 280 } finally { 281 Binder.restoreCallingIdentity(ident); 282 } 283 } 284 285 @Override startNewTransition(int type, @Nullable WindowContainerTransaction t)286 public IBinder startNewTransition(int type, @Nullable WindowContainerTransaction t) { 287 return startTransition(type, null /* transitionToken */, t); 288 } 289 290 @Override startTransition(@onNull IBinder transitionToken, @Nullable WindowContainerTransaction t)291 public void startTransition(@NonNull IBinder transitionToken, 292 @Nullable WindowContainerTransaction t) { 293 startTransition(-1 /* unused type */, transitionToken, t); 294 } 295 startTransition(@indowManager.TransitionType int type, @Nullable IBinder transitionToken, @Nullable WindowContainerTransaction t)296 private IBinder startTransition(@WindowManager.TransitionType int type, 297 @Nullable IBinder transitionToken, @Nullable WindowContainerTransaction t) { 298 enforceTaskPermission("startTransition()"); 299 final CallerInfo caller = new CallerInfo(); 300 final long ident = Binder.clearCallingIdentity(); 301 try { 302 synchronized (mGlobalLock) { 303 Transition transition = Transition.fromBinder(transitionToken); 304 if (mTransitionController.getTransitionPlayer() == null && transition == null) { 305 Slog.w(TAG, "Using shell transitions API for legacy transitions."); 306 if (t == null) { 307 throw new IllegalArgumentException("Can't use legacy transitions in" 308 + " compatibility mode with no WCT."); 309 } 310 applyTransaction(t, -1 /* syncId */, null, caller); 311 return null; 312 } 313 final WindowContainerTransaction wct = 314 t != null ? t : new WindowContainerTransaction(); 315 if (transition == null) { 316 if (type < 0) { 317 throw new IllegalArgumentException("Can't create transition with no type"); 318 } 319 // This is a direct call from shell, so the entire transition lifecycle is 320 // contained in the provided transaction if provided. Thus, we can setReady 321 // immediately after apply. 322 final Transition.ReadyCondition wctApplied = 323 new Transition.ReadyCondition("start WCT applied"); 324 final boolean needsSetReady = t != null; 325 final Transition nextTransition = new Transition(type, 0 /* flags */, 326 mTransitionController, mService.mWindowManager.mSyncEngine); 327 nextTransition.mReadyTracker.add(wctApplied); 328 nextTransition.calcParallelCollectType(wct); 329 mTransitionController.startCollectOrQueue(nextTransition, 330 (deferred) -> { 331 nextTransition.start(); 332 nextTransition.mLogger.mStartWCT = wct; 333 applyTransaction(wct, -1 /* syncId */, nextTransition, caller, 334 deferred); 335 wctApplied.meet(); 336 if (needsSetReady) { 337 setAllReadyIfNeeded(nextTransition, wct); 338 } 339 }); 340 return nextTransition.getToken(); 341 } 342 // Currently, application of wct can span multiple looper loops (ie. 343 // waitAsyncStart), so add a condition to ensure that it finishes applying. 344 final Transition.ReadyCondition wctApplied; 345 if (t != null) { 346 wctApplied = new Transition.ReadyCondition("start WCT applied"); 347 transition.mReadyTracker.add(wctApplied); 348 } else { 349 wctApplied = null; 350 } 351 // The transition already started collecting before sending a request to shell, 352 // so just start here. 353 if (!transition.isCollecting() && !transition.isForcePlaying()) { 354 Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably" 355 + " means Shell took too long to respond to a request. WM State may be" 356 + " incorrect now, please file a bug"); 357 applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller); 358 if (wctApplied != null) { 359 wctApplied.meet(); 360 } 361 return transition.getToken(); 362 } 363 transition.mLogger.mStartWCT = wct; 364 if (transition.shouldApplyOnDisplayThread()) { 365 mService.mH.post(() -> { 366 synchronized (mService.mGlobalLock) { 367 transition.start(); 368 applyTransaction(wct, -1 /* syncId */, transition, caller); 369 if (wctApplied != null) { 370 wctApplied.meet(); 371 } 372 } 373 }); 374 } else { 375 transition.start(); 376 applyTransaction(wct, -1 /* syncId */, transition, caller); 377 if (wctApplied != null) { 378 wctApplied.meet(); 379 } 380 } 381 // Since the transition is already provided, it means WMCore is determining the 382 // "readiness lifecycle" outside the provided transaction, so don't set ready here. 383 return transition.getToken(); 384 } 385 } finally { 386 Binder.restoreCallingIdentity(ident); 387 } 388 } 389 hasActivityLaunch(@onNull WindowContainerTransaction wct)390 private static boolean hasActivityLaunch(@NonNull WindowContainerTransaction wct) { 391 for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { 392 if (wct.getHierarchyOps().get(i).getType() == HIERARCHY_OP_TYPE_LAUNCH_TASK) { 393 return true; 394 } 395 } 396 return false; 397 } 398 isCreatedTaskFragmentReady(@onNull WindowContainerTransaction wct)399 private boolean isCreatedTaskFragmentReady(@NonNull WindowContainerTransaction wct) { 400 for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { 401 final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i); 402 if (op.getType() != HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION 403 || op.getTaskFragmentOperation().getOpType() 404 != OP_TYPE_CREATE_TASK_FRAGMENT) { 405 continue; 406 } 407 final IBinder tfToken = op.getTaskFragmentOperation() 408 .getTaskFragmentCreationParams().getFragmentToken(); 409 final TaskFragment taskFragment = getTaskFragment(tfToken); 410 if (taskFragment != null && !taskFragment.isReadyToTransit()) { 411 return false; 412 } 413 } 414 return true; 415 } 416 setAllReadyIfNeeded(@onNull Transition transition, @NonNull WindowContainerTransaction wct)417 private void setAllReadyIfNeeded(@NonNull Transition transition, 418 @NonNull WindowContainerTransaction wct) { 419 // TODO(b/294925498): Remove this once we have accurate ready tracking. 420 if (hasActivityLaunch(wct) && !mService.mRootWindowContainer 421 .allPausedActivitiesComplete()) { 422 // WCT is launching an activity, so we need to wait for its 423 // lifecycle events. 424 return; 425 } 426 if (!isCreatedTaskFragmentReady(wct)) { 427 // When the organizer intercepts a #startActivity, it will create an empty TaskFragment 428 // for that specific incoming starting activity. We don't want to set all ready here, 429 // because we requires that #startActivity to be included in this transition, and NOT be 430 // in its own transition. 431 // TODO(b/232042367): explicitly ensure the #startActivity and this transaction are in 432 // the same transition instead of relying on this possible racing condition. 433 return; 434 } 435 436 transition.setAllReady(); 437 } 438 439 @Override startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter, @NonNull IWindowContainerTransactionCallback callback, @NonNull WindowContainerTransaction t)440 public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter, 441 @NonNull IWindowContainerTransactionCallback callback, 442 @NonNull WindowContainerTransaction t) { 443 enforceTaskPermission("startLegacyTransition()"); 444 final CallerInfo caller = new CallerInfo(); 445 final long ident = Binder.clearCallingIdentity(); 446 int syncId; 447 try { 448 synchronized (mGlobalLock) { 449 if (type < 0) { 450 throw new IllegalArgumentException("Can't create transition with no type"); 451 } 452 if (mTransitionController.getTransitionPlayer() != null) { 453 throw new IllegalArgumentException("Can't use legacy transitions in" 454 + " when shell transitions are enabled."); 455 } 456 final DisplayContent dc = 457 mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY); 458 if (dc.mAppTransition.isTransitionSet()) { 459 // a transition already exists, so the callback probably won't be called. 460 return -1; 461 } 462 adapter.setCallingPidUid(caller.mPid, caller.mUid); 463 dc.prepareAppTransition(type); 464 dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */, 465 false /* isActivityEmbedding */); 466 syncId = startSyncWithOrganizer(callback); 467 applyTransaction(t, syncId, null /* transition */, caller); 468 setSyncReady(syncId); 469 } 470 } finally { 471 Binder.restoreCallingIdentity(ident); 472 } 473 return syncId; 474 } 475 476 @Override finishTransition(@onNull IBinder transitionToken, @Nullable WindowContainerTransaction t)477 public void finishTransition(@NonNull IBinder transitionToken, 478 @Nullable WindowContainerTransaction t) { 479 enforceTaskPermission("finishTransition()"); 480 final CallerInfo caller = new CallerInfo(); 481 final long ident = Binder.clearCallingIdentity(); 482 try { 483 synchronized (mGlobalLock) { 484 final Transition transition = Transition.fromBinder(transitionToken); 485 // apply the incoming transaction before finish in case it alters the visibility 486 // of the participants. 487 if (t != null) { 488 // Set the finishing transition before applyTransaction so the visibility 489 // changes of the transition participants will only set visible-requested 490 // and still let finishTransition handle the participants. 491 mTransitionController.mFinishingTransition = transition; 492 applyTransaction(t, -1 /* syncId */, null /*transition*/, caller, transition); 493 } 494 mTransitionController.finishTransition(transition); 495 mTransitionController.mFinishingTransition = null; 496 } 497 } finally { 498 Binder.restoreCallingIdentity(ident); 499 } 500 } 501 502 /** 503 * Applies the {@link WindowContainerTransaction} as a request from 504 * {@link android.window.TaskFragmentOrganizer}. 505 * 506 * @param wct {@link WindowContainerTransaction} to apply. 507 * @param type {@link WindowManager.TransitionType} if it needs to start a new transition. 508 * @param shouldApplyIndependently If {@code true}, the {@code wct} will request a new 509 * transition, which will be queued until the sync engine is 510 * free if there is any other active sync. If {@code false}, 511 * the {@code wct} will be directly applied to the active sync. 512 * @param remoteTransition {@link RemoteTransition} to apply for the transaction. Only available 513 * for system organizers. 514 */ applyTaskFragmentTransactionLocked(@onNull WindowContainerTransaction wct, @WindowManager.TransitionType int type, boolean shouldApplyIndependently, @Nullable RemoteTransition remoteTransition)515 void applyTaskFragmentTransactionLocked(@NonNull WindowContainerTransaction wct, 516 @WindowManager.TransitionType int type, boolean shouldApplyIndependently, 517 @Nullable RemoteTransition remoteTransition) { 518 enforceTaskFragmentOrganizerPermission("applyTaskFragmentTransaction()", 519 Objects.requireNonNull(wct.getTaskFragmentOrganizer()), 520 Objects.requireNonNull(wct)); 521 if (remoteTransition != null && !mTaskFragmentOrganizerController.isSystemOrganizer( 522 wct.getTaskFragmentOrganizer().asBinder())) { 523 throw new SecurityException( 524 "Only a system organizer is allowed to use remote transition!"); 525 } 526 final CallerInfo caller = new CallerInfo(); 527 final long ident = Binder.clearCallingIdentity(); 528 try { 529 if (mTransitionController.getTransitionPlayer() == null) { 530 // No need to worry about transition when Shell transition is not enabled. 531 applyTransaction(wct, -1 /* syncId */, null /* transition */, caller); 532 return; 533 } 534 535 if (mService.mWindowManager.mSyncEngine.hasActiveSync() 536 && !shouldApplyIndependently) { 537 // Although there is an active sync, we want to apply the transaction now. 538 // TODO(b/232042367) Redesign the organizer update on activity callback so that we 539 // we will know about the transition explicitly. 540 final Transition transition = mTransitionController.getCollectingTransition(); 541 if (transition == null) { 542 // This should rarely happen, and we should try to avoid using 543 // {@link #applySyncTransaction} with Shell transition. 544 // We still want to apply and merge the transaction to the active sync 545 // because {@code shouldApplyIndependently} is {@code false}. 546 ProtoLog.w(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, 547 "TaskFragmentTransaction changes are not collected in transition" 548 + " because there is an ongoing sync for" 549 + " applySyncTransaction()."); 550 } 551 applyTransaction(wct, -1 /* syncId */, transition, caller); 552 return; 553 } 554 555 final Transition transition = new Transition(type, 0 /* flags */, 556 mTransitionController, mService.mWindowManager.mSyncEngine); 557 TransitionController.OnStartCollect doApply = (deferred) -> { 558 if (deferred && !mTaskFragmentOrganizerController.isValidTransaction(wct)) { 559 transition.abort(); 560 return; 561 } 562 if (applyTransaction(wct, -1 /* syncId */, transition, caller, deferred) 563 == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) { 564 transition.abort(); 565 return; 566 } 567 mTransitionController.requestStartTransition(transition, null /* startTask */, 568 remoteTransition, null /* displayChange */); 569 setAllReadyIfNeeded(transition, wct); 570 }; 571 mTransitionController.startCollectOrQueue(transition, doApply); 572 } finally { 573 Binder.restoreCallingIdentity(ident); 574 } 575 } 576 applyTransaction(@onNull WindowContainerTransaction t, int syncId, @Nullable Transition transition, @NonNull CallerInfo caller)577 private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId, 578 @Nullable Transition transition, @NonNull CallerInfo caller) { 579 return applyTransaction(t, syncId, transition, caller, null /* finishTransition */); 580 } 581 applyTransaction(@onNull WindowContainerTransaction t, int syncId, @Nullable Transition transition, @NonNull CallerInfo caller, boolean deferred)582 private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId, 583 @Nullable Transition transition, @NonNull CallerInfo caller, boolean deferred) { 584 if (deferred) { 585 try { 586 return applyTransaction(t, syncId, transition, caller); 587 } catch (RuntimeException e) { 588 // If the transaction is deferred, the caller could be from TransitionController 589 // #tryStartCollectFromQueue that executes on system's worker thread rather than 590 // binder thread. And the operation in the WCT may be outdated that violates the 591 // current state. So catch the exception to avoid crashing the system. 592 Slog.e(TAG, "Failed to execute deferred applyTransaction", e); 593 } 594 return TRANSACT_EFFECTS_NONE; 595 } 596 return applyTransaction(t, syncId, transition, caller); 597 } 598 599 /** 600 * @param syncId If non-null, this will be a sync-transaction. 601 * @param transition A transition to collect changes into. 602 * @param caller Info about the calling process. 603 * @param finishTransition The transition that is currently being finished. 604 * @return The effects of the window container transaction. 605 */ applyTransaction(@onNull WindowContainerTransaction t, int syncId, @Nullable Transition transition, @NonNull CallerInfo caller, @Nullable Transition finishTransition)606 private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId, 607 @Nullable Transition transition, @NonNull CallerInfo caller, 608 @Nullable Transition finishTransition) { 609 int effects = TRANSACT_EFFECTS_NONE; 610 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId); 611 mService.deferWindowLayout(); 612 mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */); 613 boolean deferTransitionReady = false; 614 if (transition != null && !t.isEmpty()) { 615 if (transition.isCollecting()) { 616 deferTransitionReady = true; 617 transition.deferTransitionReady(); 618 } else if (Flags.alwaysDeferTransitionWhenApplyWct()) { 619 Slog.w(TAG, "Transition is not collecting when applyTransaction." 620 + " transition=" + transition + " state=" + transition.getState()); 621 transition = null; 622 } 623 } 624 try { 625 final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>(); 626 if (transition != null) { 627 transition.applyDisplayChangeIfNeeded(haveConfigChanges); 628 if (!haveConfigChanges.isEmpty()) { 629 effects |= TRANSACT_EFFECTS_CLIENT_CONFIG; 630 } 631 } 632 final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); 633 final int hopSize = hops.size(); 634 Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries; 635 if (transition != null) { 636 // Mark any config-at-end containers before applying config changes so that 637 // the config changes don't dispatch to client. 638 entries = t.getChanges().entrySet().iterator(); 639 while (entries.hasNext()) { 640 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = 641 entries.next(); 642 if (!entry.getValue().getConfigAtTransitionEnd()) continue; 643 final WindowContainer wc = WindowContainer.fromBinder(entry.getKey()); 644 if (wc == null || !wc.isAttached()) continue; 645 transition.setConfigAtEnd(wc); 646 } 647 } 648 entries = t.getChanges().entrySet().iterator(); 649 while (entries.hasNext()) { 650 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next(); 651 final WindowContainer wc = WindowContainer.fromBinder(entry.getKey()); 652 if (wc == null || !wc.isAttached()) { 653 Slog.e(TAG, "Attempt to operate on detached container: " + wc); 654 continue; 655 } 656 // Make sure we add to the syncSet before performing 657 // operations so we don't end up splitting effects between the WM 658 // pending transaction and the BLASTSync transaction. 659 if (syncId >= 0) { 660 addToSyncSet(syncId, wc); 661 } 662 if (transition != null) transition.collect(wc); 663 664 if ((entry.getValue().getChangeMask() 665 & WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) { 666 // Disable entering pip (eg. when recents pretends to finish itself) 667 if (finishTransition != null) { 668 finishTransition.setCanPipOnFinish(false /* canPipOnFinish */); 669 } else if (transition != null) { 670 transition.setCanPipOnFinish(false /* canPipOnFinish */); 671 } 672 } 673 // A bit hacky, but we need to detect "remove PiP" so that we can "wrap" the 674 // setWindowingMode call in force-hidden. 675 boolean forceHiddenForPip = false; 676 if (wc.asTask() != null && wc.inPinnedWindowingMode() 677 && entry.getValue().getWindowingMode() != WINDOWING_MODE_PINNED) { 678 // We are going out of pip. Now search hierarchy ops to determine whether we 679 // are removing pip or expanding pip. 680 for (int i = 0; i < hopSize; ++i) { 681 final WindowContainerTransaction.HierarchyOp hop = hops.get(i); 682 if (hop.getType() != HIERARCHY_OP_TYPE_REORDER) continue; 683 final WindowContainer hopWc = WindowContainer.fromBinder( 684 hop.getContainer()); 685 if (!wc.equals(hopWc)) continue; 686 forceHiddenForPip = !hop.getToTop(); 687 } 688 } 689 if (forceHiddenForPip) { 690 wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */); 691 // When removing pip, make sure that onStop is sent to the app ahead of 692 // onPictureInPictureModeChanged. 693 // See also PinnedStackTests#testStopBeforeMultiWindowCallbacksOnDismiss 694 wc.asTask().ensureActivitiesVisible(null /* starting */); 695 wc.asTask().mTaskSupervisor.processStoppingAndFinishingActivities( 696 null /* launchedActivity */, false /* processPausingActivities */, 697 "force-stop-on-removing-pip"); 698 } 699 700 int containerEffect = applyWindowContainerChange(wc, entry.getValue(), 701 t.getErrorCallbackToken()); 702 effects |= containerEffect; 703 704 if (forceHiddenForPip) { 705 wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */); 706 } 707 708 // Lifecycle changes will trigger ensureConfig for everything. 709 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 710 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { 711 haveConfigChanges.add(wc); 712 } 713 } 714 // Hierarchy changes 715 if (hopSize > 0) { 716 final boolean isInLockTaskMode = mService.isInLockTaskMode(); 717 for (int i = 0; i < hopSize; ++i) { 718 effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition, 719 isInLockTaskMode, caller, t.getErrorCallbackToken(), 720 t.getTaskFragmentOrganizer(), finishTransition); 721 } 722 } 723 // Queue-up bounds-change transactions for tasks which are now organized. Do 724 // this after hierarchy ops so we have the final organized state. 725 entries = t.getChanges().entrySet().iterator(); 726 while (entries.hasNext()) { 727 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next(); 728 final WindowContainer wc = WindowContainer.fromBinder(entry.getKey()); 729 if (wc == null || !wc.isAttached()) { 730 Slog.e(TAG, "Attempt to operate on detached container: " + wc); 731 continue; 732 } 733 final Task task = wc.asTask(); 734 final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds(); 735 if (task == null || !task.isAttached() || surfaceBounds == null) { 736 continue; 737 } 738 if (!task.isOrganized()) { 739 final Task parent = task.getParent() != null ? task.getParent().asTask() : null; 740 // Also allow direct children of created-by-organizer tasks to be 741 // controlled. In the future, these will become organized anyways. 742 if (parent == null || !parent.mCreatedByOrganizer) { 743 throw new IllegalArgumentException( 744 "Can't manipulate non-organized task surface " + task); 745 } 746 } 747 final SurfaceControl.Transaction sft = new SurfaceControl.Transaction(); 748 final SurfaceControl sc = task.getSurfaceControl(); 749 sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top); 750 if (surfaceBounds.isEmpty()) { 751 sft.setWindowCrop(sc, null); 752 } else { 753 sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height()); 754 } 755 task.setMainWindowSizeChangeTransaction(sft); 756 } 757 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) { 758 mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */); 759 // Already calls ensureActivityConfig 760 mService.mRootWindowContainer.ensureActivitiesVisible(); 761 mService.mRootWindowContainer.resumeFocusedTasksTopActivities(); 762 } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { 763 for (int i = haveConfigChanges.size() - 1; i >= 0; --i) { 764 haveConfigChanges.valueAt(i).forAllActivities(r -> { 765 if (r.isVisibleRequested()) { 766 r.ensureActivityConfiguration(true /* ignoreVisibility */); 767 } 768 }); 769 } 770 } 771 772 if (effects != 0) { 773 mService.mWindowManager.mWindowPlacerLocked.requestTraversal(); 774 } 775 } finally { 776 if (deferTransitionReady) { 777 transition.continueTransitionReady(); 778 } 779 mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */); 780 mService.continueWindowLayout(); 781 } 782 return effects; 783 } 784 applyChanges(@onNull WindowContainer<?> container, @NonNull WindowContainerTransaction.Change change)785 private int applyChanges(@NonNull WindowContainer<?> container, 786 @NonNull WindowContainerTransaction.Change change) { 787 // The "client"-facing API should prevent bad changes; however, just in case, sanitize 788 // masks here. 789 final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS; 790 final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS; 791 int effects = TRANSACT_EFFECTS_NONE; 792 final int windowingMode = change.getWindowingMode(); 793 if (configMask != 0) { 794 if (windowingMode > -1 && windowingMode != container.getWindowingMode()) { 795 // Special handling for when we are setting a windowingMode in the same transaction. 796 // Setting the windowingMode is going to call onConfigurationChanged so we don't 797 // need it called right now. Additionally, some logic requires everything in the 798 // configuration to change at the same time (ie. surface-freezer requires bounds 799 // and mode to change at the same time). 800 final Configuration c = container.getRequestedOverrideConfiguration(); 801 c.setTo(change.getConfiguration(), configMask, windowMask); 802 } else { 803 final Configuration c = 804 new Configuration(container.getRequestedOverrideConfiguration()); 805 c.setTo(change.getConfiguration(), configMask, windowMask); 806 container.onRequestedOverrideConfigurationChanged(c); 807 } 808 effects |= TRANSACT_EFFECTS_CLIENT_CONFIG; 809 if (windowMask != 0 && container.isEmbedded()) { 810 // Changing bounds of the embedded TaskFragments may result in lifecycle changes. 811 effects |= TRANSACT_EFFECTS_LIFECYCLE; 812 } 813 } 814 if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) { 815 if (container.setFocusable(change.getFocusable())) { 816 effects |= TRANSACT_EFFECTS_LIFECYCLE; 817 } 818 } 819 820 if (windowingMode > -1) { 821 if (mService.isInLockTaskMode() 822 && WindowConfiguration.inMultiWindowMode(windowingMode)) { 823 Slog.w(TAG, "Dropping unsupported request to set multi-window windowing mode" 824 + " during locked task mode."); 825 return effects; 826 } 827 828 if (windowingMode == WINDOWING_MODE_PINNED) { 829 // Do not directly put the container into PINNED mode as it may not support it or 830 // the app may not want to enter it. Instead, send a signal to request PIP 831 // mode to the app if they wish to support it below in #applyTaskChanges. 832 return effects; 833 } 834 835 final int prevMode = container.getRequestedOverrideWindowingMode(); 836 container.setWindowingMode(windowingMode); 837 if (prevMode != container.getWindowingMode()) { 838 // The activity in the container may become focusable or non-focusable due to 839 // windowing modes changes (such as entering or leaving pinned windowing mode), 840 // so also apply the lifecycle effects to this transaction. 841 effects |= TRANSACT_EFFECTS_LIFECYCLE; 842 } 843 } 844 return effects; 845 } 846 applyTaskChanges(Task tr, WindowContainerTransaction.Change c)847 private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) { 848 final SurfaceControl.Transaction t = c.getBoundsChangeTransaction(); 849 // Check bounds change transaction at the beginning because it may pause updating window 850 // surface position. Then the following changes won't apply intermediate position. 851 if (t != null) { 852 tr.setMainWindowSizeChangeTransaction(t); 853 } 854 855 int effects = applyChanges(tr, c); 856 if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) { 857 if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) { 858 effects |= TRANSACT_EFFECTS_LIFECYCLE; 859 } 860 } 861 862 if ((c.getChangeMask() & CHANGE_FORCE_TRANSLUCENT) != 0) { 863 if (tr.setForceTranslucent(c.getForceTranslucent())) { 864 effects |= TRANSACT_EFFECTS_LIFECYCLE; 865 } 866 } 867 868 if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING) != 0) { 869 tr.setDragResizing(c.getDragResizing()); 870 } 871 872 final int childWindowingMode = c.getActivityWindowingMode(); 873 if (!ActivityTaskManagerService.isPip2ExperimentEnabled() 874 && tr.getWindowingMode() == WINDOWING_MODE_PINNED 875 && (childWindowingMode == WINDOWING_MODE_PINNED 876 || childWindowingMode == WINDOWING_MODE_UNDEFINED)) { 877 // If setActivityWindowingMode requested to match its pinned task's windowing mode, 878 // remove any inconsistency checking timeout callbacks for PiP. 879 Slog.d(TAG, "Task and activity windowing modes match, so remove any timeout " 880 + "abort PiP callbacks scheduled if needed; task_win_mode=" 881 + tr.getWindowingMode() + ", activity_win_mode=" + childWindowingMode); 882 mService.mRootWindowContainer.removeAllMaybeAbortPipEnterRunnable(); 883 } 884 if (childWindowingMode > -1) { 885 tr.forAllActivities(a -> { a.setWindowingMode(childWindowingMode); }); 886 } 887 888 Rect enterPipBounds = c.getEnterPipBounds(); 889 if (enterPipBounds != null) { 890 tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds); 891 } 892 893 if (c.getWindowingMode() == WINDOWING_MODE_PINNED 894 && !tr.inPinnedWindowingMode()) { 895 final ActivityRecord activity = tr.getTopNonFinishingActivity(); 896 if (activity != null) { 897 final boolean lastSupportsEnterPipOnTaskSwitch = 898 activity.supportsEnterPipOnTaskSwitch; 899 // Temporarily force enable enter PIP on task switch so that PIP is requested 900 // regardless of whether the activity is resumed or paused. 901 activity.supportsEnterPipOnTaskSwitch = true; 902 boolean canEnterPip = activity.checkEnterPictureInPictureState( 903 "applyTaskChanges", true /* beforeStopping */); 904 if (canEnterPip) { 905 canEnterPip = mService.mActivityClientController 906 .requestPictureInPictureMode(activity); 907 } 908 if (!canEnterPip) { 909 // Restore the flag to its previous state when the activity cannot enter PIP. 910 activity.supportsEnterPipOnTaskSwitch = lastSupportsEnterPipOnTaskSwitch; 911 } 912 } 913 } 914 915 return effects; 916 } 917 applyDisplayAreaChanges(DisplayArea displayArea, WindowContainerTransaction.Change c)918 private int applyDisplayAreaChanges(DisplayArea displayArea, 919 WindowContainerTransaction.Change c) { 920 final int[] effects = new int[1]; 921 effects[0] = applyChanges(displayArea, c); 922 923 if ((c.getChangeMask() 924 & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) { 925 if (displayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) { 926 effects[0] |= TRANSACT_EFFECTS_LIFECYCLE; 927 } 928 } 929 930 displayArea.forAllTasks(task -> { 931 Task tr = (Task) task; 932 if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) { 933 if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) { 934 effects[0] |= TRANSACT_EFFECTS_LIFECYCLE; 935 } 936 } 937 }); 938 939 return effects[0]; 940 } 941 applyTaskFragmentChanges(@onNull TaskFragment taskFragment, @NonNull WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken)942 private int applyTaskFragmentChanges(@NonNull TaskFragment taskFragment, 943 @NonNull WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) { 944 if (taskFragment.isEmbeddedTaskFragmentInPip()) { 945 // No override from organizer for embedded TaskFragment in a PIP Task. 946 return TRANSACT_EFFECTS_NONE; 947 } 948 949 int effects = TRANSACT_EFFECTS_NONE; 950 // When the TaskFragment is resized, we may want to create a change transition for it, for 951 // which we want to defer the surface update until we determine whether or not to start 952 // change transition. 953 mTmpBounds0.set(taskFragment.getBounds()); 954 mTmpBounds1.set(taskFragment.getRelativeEmbeddedBounds()); 955 taskFragment.deferOrganizedTaskFragmentSurfaceUpdate(); 956 final Rect relBounds = c.getRelativeBounds(); 957 if (relBounds != null) { 958 // Make sure the requested bounds satisfied the min dimensions requirement. 959 adjustTaskFragmentRelativeBoundsForMinDimensionsIfNeeded(taskFragment, relBounds, 960 errorCallbackToken); 961 962 // For embedded TaskFragment, the organizer set the bounds in parent coordinate to 963 // prevent flicker in case there is a racing condition between the parent bounds changed 964 // and the organizer request. 965 final Rect parentBounds = taskFragment.getParent().getBounds(); 966 // Convert relative bounds to screen space. 967 final Rect absBounds = taskFragment.translateRelativeBoundsToAbsoluteBounds(relBounds, 968 parentBounds); 969 c.getConfiguration().windowConfiguration.setBounds(absBounds); 970 taskFragment.setRelativeEmbeddedBounds(relBounds); 971 } 972 if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) { 973 if (taskFragment.setForceHidden( 974 FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG, c.getHidden())) { 975 effects |= TRANSACT_EFFECTS_LIFECYCLE; 976 } 977 } 978 if ((c.getChangeMask() & CHANGE_FORCE_TRANSLUCENT) != 0) { 979 if (taskFragment.setForceTranslucent(c.getForceTranslucent())) { 980 effects |= TRANSACT_EFFECTS_LIFECYCLE; 981 } 982 } 983 984 effects |= applyChanges(taskFragment, c); 985 986 if (taskFragment.shouldStartChangeTransition(mTmpBounds0, mTmpBounds1)) { 987 taskFragment.initializeChangeTransition(mTmpBounds0); 988 } 989 taskFragment.continueOrganizedTaskFragmentSurfaceUpdate(); 990 return effects; 991 } 992 993 /** 994 * Adjusts the requested relative bounds on {@link TaskFragment} to make sure it satisfies the 995 * activity min dimensions. 996 */ adjustTaskFragmentRelativeBoundsForMinDimensionsIfNeeded( @onNull TaskFragment taskFragment, @NonNull Rect inOutRelativeBounds, @Nullable IBinder errorCallbackToken)997 private void adjustTaskFragmentRelativeBoundsForMinDimensionsIfNeeded( 998 @NonNull TaskFragment taskFragment, @NonNull Rect inOutRelativeBounds, 999 @Nullable IBinder errorCallbackToken) { 1000 if (inOutRelativeBounds.isEmpty()) { 1001 return; 1002 } 1003 final Point minDimensions = taskFragment.calculateMinDimension(); 1004 if (inOutRelativeBounds.width() < minDimensions.x 1005 || inOutRelativeBounds.height() < minDimensions.y) { 1006 // Notify organizer about the request failure. 1007 final Throwable exception = new SecurityException("The requested relative bounds:" 1008 + inOutRelativeBounds + " does not satisfy minimum dimensions:" 1009 + minDimensions); 1010 sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(), 1011 errorCallbackToken, taskFragment, OP_TYPE_SET_RELATIVE_BOUNDS, exception); 1012 1013 // Reset to match parent bounds. 1014 inOutRelativeBounds.setEmpty(); 1015 } 1016 } 1017 applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects, int syncId, @Nullable Transition transition, boolean isInLockTaskMode, @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer, @Nullable Transition finishTransition)1018 private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects, 1019 int syncId, @Nullable Transition transition, boolean isInLockTaskMode, 1020 @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken, 1021 @Nullable ITaskFragmentOrganizer organizer, @Nullable Transition finishTransition) { 1022 final int type = hop.getType(); 1023 switch (type) { 1024 case HIERARCHY_OP_TYPE_REMOVE_TASK: { 1025 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); 1026 if (wc == null || wc.asTask() == null || !wc.isAttached()) { 1027 Slog.e(TAG, "Attempt to remove invalid task: " + wc); 1028 break; 1029 } 1030 final Task task = wc.asTask(); 1031 if (task.isVisibleRequested() || task.isVisible()) { 1032 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1033 } 1034 if (task.isLeafTask()) { 1035 mService.mTaskSupervisor 1036 .removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task" 1037 + "-through-hierarchyOp"); 1038 } else { 1039 mService.mTaskSupervisor.removeRootTask(task); 1040 } 1041 break; 1042 } 1043 case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { 1044 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); 1045 if (wc == null || !wc.isAttached()) { 1046 Slog.e(TAG, "Attempt to set launch root to a detached container: " + wc); 1047 break; 1048 } 1049 final Task task = wc.asTask(); 1050 if (task == null) { 1051 throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc); 1052 } else if (task.getTaskDisplayArea() == null) { 1053 throw new IllegalArgumentException("Cannot set a task without display area as " 1054 + "launch root: " + wc); 1055 } else { 1056 task.getDisplayArea().setLaunchRootTask(task, 1057 hop.getWindowingModes(), hop.getActivityTypes()); 1058 } 1059 break; 1060 } 1061 case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: { 1062 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); 1063 if (wc == null || !wc.isAttached()) { 1064 Slog.e(TAG, "Attempt to set launch adjacent to a detached container: " + wc); 1065 break; 1066 } 1067 final Task task = wc.asTask(); 1068 final boolean clearRoot = hop.getToTop(); 1069 if (task == null) { 1070 throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc); 1071 } else if (!task.mCreatedByOrganizer) { 1072 throw new UnsupportedOperationException( 1073 "Cannot set non-organized task as adjacent flag root: " + wc); 1074 } else if (task.getAdjacentTaskFragment() == null && !clearRoot) { 1075 throw new UnsupportedOperationException( 1076 "Cannot set non-adjacent task as adjacent flag root: " + wc); 1077 } 1078 1079 task.getDisplayArea().setLaunchAdjacentFlagRootTask(clearRoot ? null : task); 1080 break; 1081 } 1082 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: { 1083 effects |= setAdjacentRootsHierarchyOp(hop); 1084 break; 1085 } 1086 case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: { 1087 effects |= clearAdjacentRootsHierarchyOp(hop); 1088 break; 1089 } 1090 case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: { 1091 effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId, 1092 isInLockTaskMode); 1093 break; 1094 } 1095 case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: { 1096 final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer()); 1097 if (activity == null || activity.finishing) { 1098 break; 1099 } 1100 if (activity.isVisible() || activity.isVisibleRequested()) { 1101 // Prevent the transition from being executed too early if the activity is 1102 // visible. 1103 activity.finishIfPossible("finish-activity-op", false /* oomAdj */); 1104 } else { 1105 activity.destroyIfPossible("finish-activity-op"); 1106 } 1107 break; 1108 } 1109 case HIERARCHY_OP_TYPE_LAUNCH_TASK: { 1110 mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, 1111 "launchTask HierarchyOp"); 1112 final Bundle launchOpts = hop.getLaunchOptions(); 1113 final int taskId = launchOpts.getInt( 1114 WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); 1115 launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); 1116 final SafeActivityOptions safeOptions = 1117 SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid); 1118 waitAsyncStart(() -> mService.mTaskSupervisor.startActivityFromRecents( 1119 caller.mPid, caller.mUid, taskId, safeOptions)); 1120 break; 1121 } 1122 case HIERARCHY_OP_TYPE_REORDER: 1123 case HIERARCHY_OP_TYPE_REPARENT: { 1124 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); 1125 if (wc == null || !wc.isAttached()) { 1126 Slog.e(TAG, "Attempt to operate on detached container: " + wc); 1127 break; 1128 } 1129 // There is no use case to ask the reparent operation in lock-task mode now, so keep 1130 // skipping this operation as usual. 1131 if (isInLockTaskMode && type == HIERARCHY_OP_TYPE_REPARENT) { 1132 Slog.w(TAG, "Skip applying hierarchy operation " + hop 1133 + " while in lock task mode"); 1134 break; 1135 } 1136 if (isLockTaskModeViolation(wc.getParent(), wc.asTask(), isInLockTaskMode)) { 1137 break; 1138 } 1139 if (syncId >= 0) { 1140 addToSyncSet(syncId, wc); 1141 } 1142 if (transition != null) { 1143 transition.collect(wc); 1144 if (hop.isReparent()) { 1145 if (wc.getParent() != null) { 1146 // Collect the current parent. It's visibility may change as 1147 // a result of this reparenting. 1148 transition.collect(wc.getParent()); 1149 } 1150 if (hop.getNewParent() != null) { 1151 final WindowContainer parentWc = 1152 WindowContainer.fromBinder(hop.getNewParent()); 1153 if (parentWc == null) { 1154 Slog.e(TAG, "Can't resolve parent window from token"); 1155 break; 1156 } 1157 transition.collect(parentWc); 1158 } 1159 } 1160 } 1161 effects |= sanitizeAndApplyHierarchyOp(wc, hop); 1162 break; 1163 } 1164 case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION: { 1165 effects |= applyTaskFragmentOperation(hop, transition, isInLockTaskMode, caller, 1166 errorCallbackToken, organizer); 1167 break; 1168 } 1169 case HIERARCHY_OP_TYPE_PENDING_INTENT: { 1170 final Bundle launchOpts = hop.getLaunchOptions(); 1171 ActivityOptions activityOptions = launchOpts != null 1172 ? new ActivityOptions(launchOpts) : null; 1173 if (activityOptions != null && activityOptions.getTransientLaunch() 1174 && mService.isCallerRecents(hop.getPendingIntent().getCreatorUid())) { 1175 if (mService.getActivityStartController().startExistingRecentsIfPossible( 1176 hop.getActivityIntent(), activityOptions)) { 1177 // Start recents successfully. 1178 break; 1179 } 1180 } 1181 1182 String resolvedType = hop.getActivityIntent() != null 1183 ? hop.getActivityIntent().resolveTypeIfNeeded( 1184 mService.mContext.getContentResolver()) 1185 : null; 1186 1187 if (hop.getPendingIntent().isActivity()) { 1188 // Set the context display id as preferred for this activity launches, so that 1189 // it can land on caller's display. Or just brought the task to front at the 1190 // display where it was on since it has higher preference. 1191 if (activityOptions == null) { 1192 activityOptions = ActivityOptions.makeBasic(); 1193 } 1194 activityOptions.setCallerDisplayId(DEFAULT_DISPLAY); 1195 } 1196 final Bundle options = activityOptions != null ? activityOptions.toBundle() : null; 1197 int res = waitAsyncStart(() -> mService.mAmInternal.sendIntentSender( 1198 hop.getPendingIntent().getTarget(), 1199 hop.getPendingIntent().getWhitelistToken(), 0 /* code */, 1200 hop.getActivityIntent(), resolvedType, null /* finishReceiver */, 1201 null /* requiredPermission */, options)); 1202 if (ActivityManager.isStartResultSuccessful(res)) { 1203 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1204 } 1205 break; 1206 } 1207 default: { 1208 // The other operations may change task order so they are skipped while in lock 1209 // task mode. The above operations are still allowed because they don't move 1210 // tasks. And it may be necessary such as clearing launch root after entering 1211 // lock task mode. 1212 if (isInLockTaskMode) { 1213 Slog.w(TAG, "Skip applying hierarchy operation " + hop 1214 + " while in lock task mode"); 1215 return effects; 1216 } 1217 } 1218 } 1219 1220 switch (type) { 1221 case HIERARCHY_OP_TYPE_START_SHORTCUT: { 1222 final Bundle launchOpts = hop.getLaunchOptions(); 1223 final String callingPackage = launchOpts.getString( 1224 WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE); 1225 launchOpts.remove( 1226 WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE); 1227 1228 final LauncherAppsServiceInternal launcherApps = LocalServices.getService( 1229 LauncherAppsServiceInternal.class); 1230 1231 final boolean success = launcherApps.startShortcut(caller.mUid, caller.mPid, 1232 callingPackage, hop.getShortcutInfo().getPackage(), null /* featureId */, 1233 hop.getShortcutInfo().getId(), null /* sourceBounds */, launchOpts, 1234 hop.getShortcutInfo().getUserId()); 1235 if (success) { 1236 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1237 } 1238 break; 1239 } 1240 case HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK: { 1241 final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); 1242 Task pipTask = container.asTask(); 1243 if (pipTask == null) { 1244 break; 1245 } 1246 ActivityRecord pipActivity = pipTask.getActivity( 1247 (activity) -> activity.pictureInPictureArgs != null); 1248 1249 if (pipActivity.isState(RESUMED)) { 1250 // schedulePauseActivity() call uses this flag when entering PiP after Recents 1251 // swipe-up TO_FRONT transition. In this case the state of the activity is 1252 // RESUMED until ActivityRecord#makeActiveIfNeeded() makes it PAUSING followed 1253 // by the scheduling for PAUSE. See moveActivityToPinnedRootTask()'s call into 1254 // resumeFocusedTasksTopActivities(). 1255 pipActivity.mAutoEnteringPip = 1256 pipActivity.pictureInPictureArgs.isAutoEnterEnabled(); 1257 } 1258 Rect entryBounds = hop.getBounds(); 1259 mService.mRootWindowContainer.moveActivityToPinnedRootTask( 1260 pipActivity, null /* launchIntoPipHostActivity */, 1261 "moveActivityToPinnedRootTask", null /* transition */, entryBounds); 1262 1263 if (pipActivity.isState(PAUSING) && pipActivity.mPauseSchedulePendingForPip) { 1264 // Continue the pausing process. This must be done after moving PiP activity to 1265 // a potentially new pinned task (multi-activity case). This case is only 1266 // triggered if TaskFragment#startPausing() deems this an auto-enter case; 1267 // i.e. we enter this flow during button-nav auto-enter but not gesture-nav 1268 // auto-enter PiP for example. 1269 pipActivity.getTask().schedulePauseActivity( 1270 pipActivity, false /* userLeaving */, 1271 false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip"); 1272 } 1273 // Reset auto-entering PiP info since any internal state updates are finished. 1274 pipActivity.mAutoEnteringPip = false; 1275 1276 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1277 break; 1278 } 1279 case HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER: { 1280 if (finishTransition == null) break; 1281 final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); 1282 if (container == null) break; 1283 final Task thisTask = container.asActivityRecord() != null 1284 ? container.asActivityRecord().getTask() : container.asTask(); 1285 if (thisTask == null) break; 1286 final Task restoreAt = finishTransition.getTransientLaunchRestoreTarget(container); 1287 if (restoreAt == null) break; 1288 final TaskDisplayArea taskDisplayArea = thisTask.getTaskDisplayArea(); 1289 taskDisplayArea.moveRootTaskBehindRootTask(thisTask.getRootTask(), restoreAt); 1290 break; 1291 } 1292 case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: { 1293 final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); 1294 if (container == null) { 1295 Slog.e(TAG, "Attempt to add local insets source provider on unknown: " 1296 + container); 1297 break; 1298 } 1299 container.addLocalInsetsFrameProvider( 1300 hop.getInsetsFrameProvider(), hop.getInsetsFrameOwner()); 1301 break; 1302 } 1303 case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER: { 1304 final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); 1305 if (container == null) { 1306 Slog.e(TAG, "Attempt to remove local insets source provider from unknown: " 1307 + container); 1308 break; 1309 } 1310 container.removeLocalInsetsFrameProvider( 1311 hop.getInsetsFrameProvider(), hop.getInsetsFrameOwner()); 1312 break; 1313 } 1314 case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: { 1315 final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); 1316 if (container == null || !container.isAttached()) { 1317 Slog.e(TAG, "Attempt to operate on unknown or detached container: " 1318 + container); 1319 break; 1320 } 1321 if (container.asTask() == null && container.asDisplayArea() == null) { 1322 Slog.e(TAG, "Cannot set always-on-top on non-task or non-display area: " 1323 + container); 1324 break; 1325 } 1326 container.setAlwaysOnTop(hop.isAlwaysOnTop()); 1327 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1328 break; 1329 } 1330 case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH: { 1331 final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); 1332 final Task task = container != null ? container.asTask() : null; 1333 if (task == null || !task.isAttached()) { 1334 Slog.e(TAG, "Attempt to operate on unknown or detached container: " 1335 + container); 1336 break; 1337 } 1338 if (!task.mCreatedByOrganizer) { 1339 throw new UnsupportedOperationException( 1340 "Cannot set reparent leaf task flag on non-organized task : " + task); 1341 } 1342 if (!task.isRootTask()) { 1343 throw new UnsupportedOperationException( 1344 "Cannot set reparent leaf task flag on non-root task : " + task); 1345 } 1346 task.setReparentLeafTaskIfRelaunch(hop.isReparentLeafTaskIfRelaunch()); 1347 break; 1348 } 1349 } 1350 return effects; 1351 } 1352 1353 /** 1354 * Applies change set through {@link WindowContainerTransaction#addTaskFragmentOperation}. 1355 * @return an int to represent the transaction effects, such as {@link #TRANSACT_EFFECTS_NONE}, 1356 * {@link #TRANSACT_EFFECTS_LIFECYCLE} or {@link #TRANSACT_EFFECTS_CLIENT_CONFIG}. 1357 */ applyTaskFragmentOperation(@onNull WindowContainerTransaction.HierarchyOp hop, @Nullable Transition transition, boolean isInLockTaskMode, @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer)1358 private int applyTaskFragmentOperation(@NonNull WindowContainerTransaction.HierarchyOp hop, 1359 @Nullable Transition transition, boolean isInLockTaskMode, @NonNull CallerInfo caller, 1360 @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer) { 1361 if (!validateTaskFragmentOperation(hop, errorCallbackToken, organizer)) { 1362 return TRANSACT_EFFECTS_NONE; 1363 } 1364 final IBinder fragmentToken = hop.getContainer(); 1365 final TaskFragment taskFragment = mLaunchTaskFragments.get(fragmentToken); 1366 final TaskFragmentOperation operation = hop.getTaskFragmentOperation(); 1367 final int opType = operation.getOpType(); 1368 1369 int effects = TRANSACT_EFFECTS_NONE; 1370 switch (opType) { 1371 case OP_TYPE_CREATE_TASK_FRAGMENT: { 1372 final TaskFragmentCreationParams taskFragmentCreationParams = 1373 operation.getTaskFragmentCreationParams(); 1374 if (taskFragmentCreationParams == null) { 1375 final Throwable exception = new IllegalArgumentException( 1376 "TaskFragmentCreationParams must be non-null"); 1377 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1378 opType, exception); 1379 break; 1380 } 1381 createTaskFragment(taskFragmentCreationParams, errorCallbackToken, caller, 1382 transition); 1383 break; 1384 } 1385 case OP_TYPE_DELETE_TASK_FRAGMENT: { 1386 if (isInLockTaskMode) { 1387 final ActivityRecord bottomActivity = taskFragment.getActivity( 1388 a -> !a.finishing, false /* traverseTopToBottom */); 1389 if (bottomActivity != null 1390 && mService.getLockTaskController().activityBlockedFromFinish( 1391 bottomActivity)) { 1392 Slog.w(TAG, "Skip removing TaskFragment due in lock task mode."); 1393 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, 1394 taskFragment, opType, new IllegalStateException( 1395 "Not allow to delete task fragment in lock task mode.")); 1396 break; 1397 } 1398 } 1399 effects |= deleteTaskFragment(taskFragment, transition); 1400 break; 1401 } 1402 case OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: { 1403 final IBinder callerActivityToken = operation.getActivityToken(); 1404 final Intent activityIntent = operation.getActivityIntent(); 1405 final Bundle activityOptions = operation.getBundle(); 1406 final int result = waitAsyncStart(() -> mService.getActivityStartController() 1407 .startActivityInTaskFragment(taskFragment, activityIntent, activityOptions, 1408 callerActivityToken, caller.mUid, caller.mPid, 1409 errorCallbackToken)); 1410 if (!isStartResultSuccessful(result)) { 1411 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1412 opType, convertStartFailureToThrowable(result, activityIntent)); 1413 } else { 1414 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1415 } 1416 break; 1417 } 1418 case OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: { 1419 final IBinder activityToken = operation.getActivityToken(); 1420 ActivityRecord activity = ActivityRecord.forTokenLocked(activityToken); 1421 if (activity == null) { 1422 // The token may be a temporary token if the activity doesn't belong to 1423 // the organizer process. 1424 activity = mTaskFragmentOrganizerController 1425 .getReparentActivityFromTemporaryToken(organizer, activityToken); 1426 } 1427 if (activity == null) { 1428 final Throwable exception = new IllegalArgumentException( 1429 "Not allowed to operate with invalid activity."); 1430 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1431 opType, exception); 1432 break; 1433 } 1434 if (taskFragment.isAllowedToEmbedActivity(activity) != EMBEDDING_ALLOWED) { 1435 final Throwable exception = new SecurityException( 1436 "The task fragment is not allowed to embed the given activity."); 1437 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1438 opType, exception); 1439 break; 1440 } 1441 if (taskFragment.getTask() != activity.getTask()) { 1442 final Throwable exception = new SecurityException("The reparented activity is" 1443 + " not in the same Task as the target TaskFragment."); 1444 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1445 opType, exception); 1446 break; 1447 } 1448 if (transition != null) { 1449 transition.collect(activity); 1450 if (activity.getParent() != null) { 1451 // Collect the current parent. Its visibility may change as a result of 1452 // this reparenting. 1453 transition.collect(activity.getParent()); 1454 } 1455 transition.collect(taskFragment); 1456 } 1457 activity.reparent(taskFragment, POSITION_TOP); 1458 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1459 break; 1460 } 1461 case OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: { 1462 final IBinder secondaryFragmentToken = operation.getSecondaryFragmentToken(); 1463 final TaskFragment secondaryTaskFragment = 1464 mLaunchTaskFragments.get(secondaryFragmentToken); 1465 if (secondaryTaskFragment == null) { 1466 final Throwable exception = new IllegalArgumentException( 1467 "SecondaryFragmentToken must be set for setAdjacentTaskFragments."); 1468 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1469 opType, exception); 1470 break; 1471 } 1472 if (taskFragment.getAdjacentTaskFragment() != secondaryTaskFragment) { 1473 // Only have lifecycle effect if the adjacent changed. 1474 taskFragment.setAdjacentTaskFragment(secondaryTaskFragment); 1475 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1476 } 1477 1478 final Bundle bundle = hop.getLaunchOptions(); 1479 final WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams = 1480 bundle != null 1481 ? new WindowContainerTransaction.TaskFragmentAdjacentParams(bundle) 1482 : null; 1483 taskFragment.setDelayLastActivityRemoval(adjacentParams != null 1484 && adjacentParams.shouldDelayPrimaryLastActivityRemoval()); 1485 secondaryTaskFragment.setDelayLastActivityRemoval(adjacentParams != null 1486 && adjacentParams.shouldDelaySecondaryLastActivityRemoval()); 1487 break; 1488 } 1489 case OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS: { 1490 final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment(); 1491 if (adjacentTaskFragment == null) { 1492 break; 1493 } 1494 taskFragment.resetAdjacentTaskFragment(); 1495 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1496 1497 // Clear the focused app if the focused app is no longer visible after reset the 1498 // adjacent TaskFragments. 1499 final ActivityRecord focusedApp = taskFragment.getDisplayContent().mFocusedApp; 1500 final TaskFragment focusedTaskFragment = focusedApp != null 1501 ? focusedApp.getTaskFragment() 1502 : null; 1503 if ((focusedTaskFragment == taskFragment 1504 || focusedTaskFragment == adjacentTaskFragment) 1505 && !focusedTaskFragment.shouldBeVisible(null /* starting */)) { 1506 focusedTaskFragment.getDisplayContent().setFocusedApp(null /* newFocus */); 1507 } 1508 break; 1509 } 1510 case OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT: { 1511 final ActivityRecord curFocus = taskFragment.getDisplayContent().mFocusedApp; 1512 if (curFocus != null && curFocus.getTaskFragment() == taskFragment) { 1513 Slog.d(TAG, "The requested TaskFragment already has the focus."); 1514 break; 1515 } 1516 if (curFocus != null && curFocus.getTask() != taskFragment.getTask()) { 1517 Slog.d(TAG, "The Task of the requested TaskFragment doesn't have focus."); 1518 break; 1519 } 1520 final ActivityRecord targetFocus = taskFragment.getTopResumedActivity(); 1521 if (targetFocus == null) { 1522 Slog.d(TAG, "There is no resumed activity in the requested TaskFragment."); 1523 break; 1524 } 1525 taskFragment.getDisplayContent().setFocusedApp(targetFocus); 1526 break; 1527 } 1528 case OP_TYPE_SET_COMPANION_TASK_FRAGMENT: { 1529 final IBinder companionFragmentToken = operation.getSecondaryFragmentToken(); 1530 final TaskFragment companionTaskFragment = companionFragmentToken != null 1531 ? mLaunchTaskFragments.get(companionFragmentToken) 1532 : null; 1533 taskFragment.setCompanionTaskFragment(companionTaskFragment); 1534 break; 1535 } 1536 case OP_TYPE_SET_ANIMATION_PARAMS: { 1537 final TaskFragmentAnimationParams animationParams = operation.getAnimationParams(); 1538 if (animationParams == null) { 1539 final Throwable exception = new IllegalArgumentException( 1540 "TaskFragmentAnimationParams must be non-null"); 1541 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1542 opType, exception); 1543 break; 1544 } 1545 taskFragment.setAnimationParams(animationParams); 1546 break; 1547 } 1548 case OP_TYPE_REORDER_TO_FRONT: { 1549 final Task task = taskFragment.getTask(); 1550 if (task != null) { 1551 final TaskFragment topTaskFragment = task.getTaskFragment( 1552 tf -> tf.asTask() == null); 1553 if (topTaskFragment != null && topTaskFragment != taskFragment) { 1554 final int index = task.mChildren.indexOf(topTaskFragment); 1555 task.mChildren.remove(taskFragment); 1556 task.mChildren.add(index, taskFragment); 1557 if (!taskFragment.hasChild()) { 1558 // Ensure that the child layers are updated if the TaskFragment is empty 1559 task.assignChildLayers(); 1560 } 1561 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1562 } 1563 } 1564 break; 1565 } 1566 case OP_TYPE_SET_ISOLATED_NAVIGATION: { 1567 final boolean isolatedNav = operation.getBooleanValue(); 1568 taskFragment.setIsolatedNav(isolatedNav); 1569 break; 1570 } 1571 case OP_TYPE_REORDER_TO_BOTTOM_OF_TASK: { 1572 final Task task = taskFragment.getTask(); 1573 if (task != null) { 1574 if (task.mChildren.peekFirst() != taskFragment) { 1575 task.mChildren.remove(taskFragment); 1576 task.mChildren.add(0, taskFragment); 1577 if (!taskFragment.hasChild()) { 1578 // Ensure that the child layers are updated if the TaskFragment is 1579 // empty. 1580 task.assignChildLayers(); 1581 } 1582 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1583 } 1584 } 1585 break; 1586 } 1587 case OP_TYPE_REORDER_TO_TOP_OF_TASK: { 1588 final Task task = taskFragment.getTask(); 1589 if (task != null) { 1590 if (task.mChildren.peekLast() != taskFragment) { 1591 task.mChildren.remove(taskFragment); 1592 task.mChildren.add(taskFragment); 1593 if (!taskFragment.hasChild()) { 1594 // Ensure that the child layers are updated if the TaskFragment is 1595 // empty. 1596 task.assignChildLayers(); 1597 } 1598 effects |= TRANSACT_EFFECTS_LIFECYCLE; 1599 } 1600 } 1601 break; 1602 } 1603 case OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE: { 1604 final Task task = taskFragment.getTask(); 1605 if (task == null) { 1606 break; 1607 } 1608 // If any TaskFragment in the Task is collected by the transition, we make the decor 1609 // surface visible in sync with the TaskFragment transition. Otherwise, we make the 1610 // decor surface visible immediately. 1611 final TaskFragment syncTaskFragment = transition != null 1612 ? task.getTaskFragment(transition.mParticipants::contains) 1613 : null; 1614 1615 if (syncTaskFragment != null) { 1616 task.moveOrCreateDecorSurfaceFor(taskFragment, false /* visible */); 1617 task.setDecorSurfaceVisible(syncTaskFragment.getSyncTransaction()); 1618 } else { 1619 task.moveOrCreateDecorSurfaceFor(taskFragment, true /* visible */); 1620 } 1621 break; 1622 } 1623 case OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE: { 1624 final Task task = taskFragment.getTask(); 1625 if (task == null) { 1626 break; 1627 } 1628 task.removeDecorSurface(); 1629 break; 1630 } 1631 case OP_TYPE_SET_DIM_ON_TASK: { 1632 final boolean dimOnTask = operation.getBooleanValue(); 1633 taskFragment.setEmbeddedDimArea(dimOnTask ? EMBEDDED_DIM_AREA_PARENT_TASK 1634 : EMBEDDED_DIM_AREA_TASK_FRAGMENT); 1635 break; 1636 } 1637 case OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH: { 1638 taskFragment.setMoveToBottomIfClearWhenLaunch(operation.getBooleanValue()); 1639 break; 1640 } 1641 case OP_TYPE_SET_DECOR_SURFACE_BOOSTED: { 1642 if (Flags.activityEmbeddingInteractiveDividerFlag()) { 1643 final Task task = taskFragment.getTask(); 1644 if (task == null) { 1645 break; 1646 } 1647 final SurfaceControl.Transaction clientTransaction = 1648 operation.getSurfaceTransaction(); 1649 if (clientTransaction != null) { 1650 // Sanitize the client transaction. sanitize() silently removes invalid 1651 // operations and does not throw or provide signal about whether there are 1652 // any invalid operations. 1653 clientTransaction.sanitize(caller.mPid, caller.mUid); 1654 } 1655 1656 task.requestDecorSurfaceBoosted( 1657 taskFragment, 1658 operation.getBooleanValue() /* isBoosted */, 1659 clientTransaction); 1660 1661 // The decor surface boost/unboost must be applied after the transition is 1662 // completed. Otherwise, the decor surface could be moved before Shell completes 1663 // the transition, causing flicker. 1664 runAfterTransition(transition, task::commitDecorSurfaceBoostedState); 1665 } 1666 break; 1667 } 1668 case OP_TYPE_SET_PINNED: { 1669 final boolean pinned = operation.getBooleanValue(); 1670 taskFragment.setPinned(pinned); 1671 break; 1672 } 1673 } 1674 return effects; 1675 } 1676 1677 /** 1678 * Executes the provided {@code runnable} after the {@code transition}. If the 1679 * {@code transition} is {@code null}, the {@code runnable} is executed immediately. 1680 */ runAfterTransition( @ullable Transition transition, @NonNull Runnable runnable)1681 private static void runAfterTransition( 1682 @Nullable Transition transition, @NonNull Runnable runnable) { 1683 if (transition == null) { 1684 runnable.run(); 1685 } else { 1686 transition.addTransitionEndedListener(runnable); 1687 } 1688 } 1689 validateTaskFragmentOperation( @onNull WindowContainerTransaction.HierarchyOp hop, @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer)1690 private boolean validateTaskFragmentOperation( 1691 @NonNull WindowContainerTransaction.HierarchyOp hop, 1692 @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer) { 1693 final TaskFragmentOperation operation = hop.getTaskFragmentOperation(); 1694 final IBinder fragmentToken = hop.getContainer(); 1695 final TaskFragment taskFragment = mLaunchTaskFragments.get(fragmentToken); 1696 if (operation == null) { 1697 final Throwable exception = new IllegalArgumentException( 1698 "TaskFragmentOperation must be non-null"); 1699 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1700 OP_TYPE_UNKNOWN, exception); 1701 return false; 1702 } 1703 final int opType = operation.getOpType(); 1704 if (opType == OP_TYPE_CREATE_TASK_FRAGMENT) { 1705 // No need to check TaskFragment. 1706 return true; 1707 } 1708 1709 if (!validateTaskFragment(taskFragment, opType, errorCallbackToken, organizer)) { 1710 return false; 1711 } 1712 1713 if ((opType == OP_TYPE_REORDER_TO_BOTTOM_OF_TASK 1714 || opType == OP_TYPE_REORDER_TO_TOP_OF_TASK) 1715 && !mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) { 1716 final Throwable exception = new SecurityException( 1717 "Only a system organizer can perform OP_TYPE_REORDER_TO_BOTTOM_OF_TASK or " 1718 + "OP_TYPE_REORDER_TO_TOP_OF_TASK." 1719 ); 1720 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1721 opType, exception); 1722 return false; 1723 } 1724 1725 if ((opType == OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH) 1726 && !mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) { 1727 final Throwable exception = new SecurityException( 1728 "Only a system organizer can perform " 1729 + "OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH." 1730 ); 1731 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1732 opType, exception); 1733 return false; 1734 } 1735 1736 final IBinder secondaryFragmentToken = operation.getSecondaryFragmentToken(); 1737 return secondaryFragmentToken == null 1738 || validateTaskFragment(mLaunchTaskFragments.get(secondaryFragmentToken), opType, 1739 errorCallbackToken, organizer); 1740 } 1741 validateTaskFragment(@ullable TaskFragment taskFragment, @TaskFragmentOperation.OperationType int opType, @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer)1742 private boolean validateTaskFragment(@Nullable TaskFragment taskFragment, 1743 @TaskFragmentOperation.OperationType int opType, @Nullable IBinder errorCallbackToken, 1744 @Nullable ITaskFragmentOrganizer organizer) { 1745 if (taskFragment == null || !taskFragment.isAttached()) { 1746 // TaskFragment doesn't exist. 1747 final Throwable exception = new IllegalArgumentException( 1748 "Not allowed to apply operation on invalid fragment tokens opType=" + opType); 1749 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1750 opType, exception); 1751 return false; 1752 } 1753 if (taskFragment.isEmbeddedTaskFragmentInPip() 1754 && (opType != OP_TYPE_DELETE_TASK_FRAGMENT 1755 // When the Task enters PiP before the organizer removes the empty TaskFragment, we 1756 // should allow it to delete the TaskFragment for cleanup. 1757 || taskFragment.getTopNonFinishingActivity() != null)) { 1758 final Throwable exception = new IllegalArgumentException( 1759 "Not allowed to apply operation on PIP TaskFragment"); 1760 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment, 1761 opType, exception); 1762 return false; 1763 } 1764 return true; 1765 } 1766 1767 /** 1768 * Post and wait for the result of the activity start to prevent potential deadlock against 1769 * {@link WindowManagerGlobalLock}. 1770 */ waitAsyncStart(IntSupplier startActivity)1771 private int waitAsyncStart(IntSupplier startActivity) { 1772 final Integer[] starterResult = {null}; 1773 final Handler handler = (Looper.myLooper() == mService.mH.getLooper()) 1774 // uncommon case where a queued transaction is trying to start an activity. We can't 1775 // post to our own thread and wait (otherwise we deadlock), so use anim thread 1776 // instead (which is 1 higher priority). 1777 ? mService.mWindowManager.mAnimationHandler 1778 // Otherwise just put it on main handler 1779 : mService.mH; 1780 handler.post(() -> { 1781 try { 1782 starterResult[0] = startActivity.getAsInt(); 1783 } catch (Throwable t) { 1784 starterResult[0] = ActivityManager.START_CANCELED; 1785 Slog.w(TAG, t); 1786 } 1787 synchronized (mGlobalLock) { 1788 mGlobalLock.notifyAll(); 1789 } 1790 }); 1791 while (starterResult[0] == null) { 1792 try { 1793 mGlobalLock.wait(); 1794 } catch (InterruptedException ignored) { 1795 } 1796 } 1797 return starterResult[0]; 1798 } 1799 sanitizeAndApplyHierarchyOp(WindowContainer container, WindowContainerTransaction.HierarchyOp hop)1800 private int sanitizeAndApplyHierarchyOp(WindowContainer container, 1801 WindowContainerTransaction.HierarchyOp hop) { 1802 final Task task = container.asTask(); 1803 if (task == null) { 1804 throw new IllegalArgumentException("Invalid container in hierarchy op"); 1805 } 1806 final DisplayContent dc = task.getDisplayContent(); 1807 if (dc == null) { 1808 Slog.w(TAG, "Container is no longer attached: " + task); 1809 return TRANSACT_EFFECTS_NONE; 1810 } 1811 final Task as = task; 1812 1813 if (hop.isReparent()) { 1814 final boolean isNonOrganizedRootableTask = 1815 task.isRootTask() || task.getParent().asTask().mCreatedByOrganizer; 1816 if (isNonOrganizedRootableTask) { 1817 WindowContainer newParent = hop.getNewParent() == null 1818 ? dc.getDefaultTaskDisplayArea() 1819 : WindowContainer.fromBinder(hop.getNewParent()); 1820 if (newParent == null) { 1821 Slog.e(TAG, "Can't resolve parent window from token"); 1822 return TRANSACT_EFFECTS_NONE; 1823 } 1824 if (task.getParent() != newParent) { 1825 if (newParent.asTaskDisplayArea() != null) { 1826 // For now, reparenting to displayarea is different from other reparents... 1827 as.reparent(newParent.asTaskDisplayArea(), hop.getToTop()); 1828 } else if (newParent.asTask() != null) { 1829 if (newParent.inMultiWindowMode() && task.isLeafTask()) { 1830 if (newParent.inPinnedWindowingMode()) { 1831 Slog.w(TAG, "Can't support moving a task to another PIP window..." 1832 + " newParent=" + newParent + " task=" + task); 1833 return TRANSACT_EFFECTS_NONE; 1834 } 1835 if (!task.supportsMultiWindowInDisplayArea( 1836 newParent.asTask().getDisplayArea())) { 1837 Slog.w(TAG, "Can't support task that doesn't support multi-window" 1838 + " mode in multi-window mode... newParent=" + newParent 1839 + " task=" + task); 1840 return TRANSACT_EFFECTS_NONE; 1841 } 1842 } 1843 task.reparent((Task) newParent, 1844 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, 1845 false /*moveParents*/, "sanitizeAndApplyHierarchyOp"); 1846 } else { 1847 throw new RuntimeException("Can only reparent task to another task or" 1848 + " taskDisplayArea, but not " + newParent); 1849 } 1850 } else { 1851 final Task rootTask = (Task) ( 1852 (newParent != null && !(newParent instanceof TaskDisplayArea)) 1853 ? newParent : task.getRootTask()); 1854 as.getDisplayArea().positionChildAt( 1855 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, rootTask, 1856 false /* includingParents */); 1857 } 1858 } else { 1859 throw new RuntimeException("Reparenting leaf Tasks is not supported now. " + task); 1860 } 1861 } else { 1862 if (hop.getToTop() && task.isRootTask()) { 1863 final ActivityRecord pipCandidate = task.findEnterPipOnTaskSwitchCandidate( 1864 task.getDisplayArea().getTopRootTask()); 1865 task.enableEnterPipOnTaskSwitch(pipCandidate, task, null /* toFrontActivity */, 1866 null /* options */); 1867 } 1868 1869 task.getParent().positionChildAt( 1870 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, 1871 task, hop.includingParents()); 1872 } 1873 return TRANSACT_EFFECTS_LIFECYCLE; 1874 } 1875 isLockTaskModeViolation(WindowContainer parent, Task task, boolean isInLockTaskMode)1876 private boolean isLockTaskModeViolation(WindowContainer parent, Task task, 1877 boolean isInLockTaskMode) { 1878 if (!isInLockTaskMode || parent == null || task == null) { 1879 return false; 1880 } 1881 final LockTaskController lockTaskController = mService.getLockTaskController(); 1882 boolean taskViolation = lockTaskController.isLockTaskModeViolation(task); 1883 if (!taskViolation && parent.asTask() != null) { 1884 taskViolation = lockTaskController.isLockTaskModeViolation(parent.asTask()); 1885 } 1886 if (taskViolation) { 1887 Slog.w(TAG, "Can't support the operation since in lock task mode violation. " 1888 + " Task: " + task + " Parent : " + parent); 1889 } 1890 return taskViolation; 1891 } 1892 reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop, @Nullable Transition transition, int syncId, boolean isInLockTaskMode)1893 private int reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop, 1894 @Nullable Transition transition, int syncId, boolean isInLockTaskMode) { 1895 WindowContainer<?> currentParent = hop.getContainer() != null 1896 ? WindowContainer.fromBinder(hop.getContainer()) : null; 1897 WindowContainer newParent = hop.getNewParent() != null 1898 ? WindowContainer.fromBinder(hop.getNewParent()) : null; 1899 if (currentParent == null && newParent == null) { 1900 throw new IllegalArgumentException("reparentChildrenTasksHierarchyOp: " + hop); 1901 } else if (currentParent == null) { 1902 currentParent = newParent.asTask().getDisplayContent().getDefaultTaskDisplayArea(); 1903 } else if (newParent == null) { 1904 newParent = currentParent.asTask().getDisplayContent().getDefaultTaskDisplayArea(); 1905 } 1906 1907 if (currentParent == newParent) { 1908 Slog.e(TAG, "reparentChildrenTasksHierarchyOp parent not changing: " + hop); 1909 return TRANSACT_EFFECTS_NONE; 1910 } 1911 if (!currentParent.isAttached()) { 1912 Slog.e(TAG, "reparentChildrenTasksHierarchyOp currentParent detached=" 1913 + currentParent + " hop=" + hop); 1914 return TRANSACT_EFFECTS_NONE; 1915 } 1916 if (!newParent.isAttached()) { 1917 Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent detached=" 1918 + newParent + " hop=" + hop); 1919 return TRANSACT_EFFECTS_NONE; 1920 } 1921 if (newParent.inPinnedWindowingMode()) { 1922 Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent in PIP=" 1923 + newParent + " hop=" + hop); 1924 return TRANSACT_EFFECTS_NONE; 1925 } 1926 1927 final boolean newParentInMultiWindow = newParent.inMultiWindowMode(); 1928 final TaskDisplayArea newParentTda = newParent.asTask() != null 1929 ? newParent.asTask().getDisplayArea() 1930 : newParent.asTaskDisplayArea(); 1931 final WindowContainer finalCurrentParent = currentParent; 1932 final WindowContainer finalNewParent = newParent; 1933 Slog.i(TAG, "reparentChildrenTasksHierarchyOp" 1934 + " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop); 1935 1936 // We want to collect the tasks first before re-parenting to avoid array shifting on us. 1937 final ArrayList<Task> tasksToReparent = new ArrayList<>(); 1938 1939 currentParent.forAllTasks(task -> { 1940 Slog.i(TAG, " Processing task=" + task); 1941 final boolean reparent; 1942 if (task.mCreatedByOrganizer || task.getParent() != finalCurrentParent) { 1943 // We only care about non-organized task that are direct children of the thing we 1944 // are reparenting from. 1945 return false; 1946 } 1947 if (newParentInMultiWindow && !task.supportsMultiWindowInDisplayArea(newParentTda)) { 1948 Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task to multi window," 1949 + " task=" + task); 1950 return false; 1951 } 1952 if (!ArrayUtils.isEmpty(hop.getActivityTypes()) 1953 && !ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType())) { 1954 return false; 1955 } 1956 if (!ArrayUtils.isEmpty(hop.getWindowingModes()) 1957 && !ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) { 1958 return false; 1959 } 1960 if (isLockTaskModeViolation(finalNewParent, task, isInLockTaskMode)) { 1961 return false; 1962 } 1963 1964 if (hop.getToTop()) { 1965 tasksToReparent.add(0, task); 1966 } else { 1967 tasksToReparent.add(task); 1968 } 1969 return hop.getReparentTopOnly() && tasksToReparent.size() == 1; 1970 }); 1971 1972 final int count = tasksToReparent.size(); 1973 for (int i = 0; i < count; ++i) { 1974 final Task task = tasksToReparent.get(i); 1975 if (syncId >= 0) { 1976 addToSyncSet(syncId, task); 1977 } 1978 if (transition != null) transition.collect(task); 1979 1980 if (newParent instanceof TaskDisplayArea) { 1981 // For now, reparenting to display area is different from other reparents... 1982 task.reparent((TaskDisplayArea) newParent, hop.getToTop()); 1983 } else { 1984 task.reparent((Task) newParent, 1985 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, 1986 false /*moveParents*/, "processChildrenTaskReparentHierarchyOp"); 1987 } 1988 } 1989 1990 if (transition != null) transition.collect(newParent); 1991 1992 return TRANSACT_EFFECTS_LIFECYCLE; 1993 } 1994 setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop)1995 private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) { 1996 final WindowContainer wc1 = WindowContainer.fromBinder(hop.getContainer()); 1997 if (wc1 == null || !wc1.isAttached()) { 1998 Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc1); 1999 return TRANSACT_EFFECTS_NONE; 2000 } 2001 final TaskFragment root1 = wc1.asTaskFragment(); 2002 final WindowContainer wc2 = WindowContainer.fromBinder(hop.getAdjacentRoot()); 2003 if (wc2 == null || !wc2.isAttached()) { 2004 Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc2); 2005 return TRANSACT_EFFECTS_NONE; 2006 } 2007 final TaskFragment root2 = wc2.asTaskFragment(); 2008 if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) { 2009 throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" 2010 + " organizer root1=" + root1 + " root2=" + root2); 2011 } 2012 if (root1.getAdjacentTaskFragment() == root2) { 2013 return TRANSACT_EFFECTS_NONE; 2014 } 2015 root1.setAdjacentTaskFragment(root2); 2016 return TRANSACT_EFFECTS_LIFECYCLE; 2017 } 2018 clearAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop)2019 private int clearAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) { 2020 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); 2021 if (wc == null || !wc.isAttached()) { 2022 Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc); 2023 return TRANSACT_EFFECTS_NONE; 2024 } 2025 final TaskFragment root = wc.asTaskFragment(); 2026 if (!root.mCreatedByOrganizer) { 2027 throw new IllegalArgumentException("clearAdjacentRootsHierarchyOp: Not created by" 2028 + " organizer root=" + root); 2029 } 2030 if (root.getAdjacentTaskFragment() == null) { 2031 return TRANSACT_EFFECTS_NONE; 2032 } 2033 root.resetAdjacentTaskFragment(); 2034 return TRANSACT_EFFECTS_LIFECYCLE; 2035 } 2036 sanitizeWindowContainer(WindowContainer wc)2037 private void sanitizeWindowContainer(WindowContainer wc) { 2038 if (!(wc instanceof TaskFragment) && !(wc instanceof DisplayArea)) { 2039 throw new RuntimeException("Invalid token in task fragment or displayArea transaction"); 2040 } 2041 } 2042 applyWindowContainerChange(WindowContainer wc, WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken)2043 private int applyWindowContainerChange(WindowContainer wc, 2044 WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) { 2045 sanitizeWindowContainer(wc); 2046 if (wc.asDisplayArea() != null) { 2047 return applyDisplayAreaChanges(wc.asDisplayArea(), c); 2048 } else if (wc.asTask() != null) { 2049 return applyTaskChanges(wc.asTask(), c); 2050 } else if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbedded()) { 2051 return applyTaskFragmentChanges(wc.asTaskFragment(), c, errorCallbackToken); 2052 } else { 2053 return applyChanges(wc, c); 2054 } 2055 } 2056 2057 @Override getTaskOrganizerController()2058 public ITaskOrganizerController getTaskOrganizerController() { 2059 enforceTaskPermission("getTaskOrganizerController()"); 2060 return mTaskOrganizerController; 2061 } 2062 2063 @Override getDisplayAreaOrganizerController()2064 public IDisplayAreaOrganizerController getDisplayAreaOrganizerController() { 2065 enforceTaskPermission("getDisplayAreaOrganizerController()"); 2066 return mDisplayAreaOrganizerController; 2067 } 2068 2069 @Override getTaskFragmentOrganizerController()2070 public ITaskFragmentOrganizerController getTaskFragmentOrganizerController() { 2071 return mTaskFragmentOrganizerController; 2072 } 2073 2074 /** 2075 * This will prepare a {@link BLASTSyncEngine.SyncGroup} for the organizer to track, but the 2076 * {@link BLASTSyncEngine.SyncGroup} may not be active until the {@link BLASTSyncEngine} is 2077 * free. 2078 */ prepareSyncWithOrganizer( IWindowContainerTransactionCallback callback)2079 private BLASTSyncEngine.SyncGroup prepareSyncWithOrganizer( 2080 IWindowContainerTransactionCallback callback) { 2081 final BLASTSyncEngine.SyncGroup s = mService.mWindowManager.mSyncEngine 2082 .prepareSyncSet(this, "Organizer"); 2083 mTransactionCallbacksByPendingSyncId.put(s.mSyncId, callback); 2084 return s; 2085 } 2086 2087 @VisibleForTesting startSyncWithOrganizer(IWindowContainerTransactionCallback callback)2088 int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) { 2089 final BLASTSyncEngine.SyncGroup s = prepareSyncWithOrganizer(callback); 2090 mService.mWindowManager.mSyncEngine.startSyncSet(s); 2091 return s.mSyncId; 2092 } 2093 2094 @VisibleForTesting setSyncReady(int id)2095 void setSyncReady(int id) { 2096 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set sync ready, syncId=%d", id); 2097 mService.mWindowManager.mSyncEngine.setReady(id); 2098 } 2099 2100 @VisibleForTesting addToSyncSet(int syncId, WindowContainer wc)2101 void addToSyncSet(int syncId, WindowContainer wc) { 2102 mService.mWindowManager.mSyncEngine.addToSyncSet(syncId, wc); 2103 } 2104 2105 @Override onTransactionReady(int syncId, SurfaceControl.Transaction t)2106 public void onTransactionReady(int syncId, SurfaceControl.Transaction t) { 2107 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId); 2108 final IWindowContainerTransactionCallback callback = 2109 mTransactionCallbacksByPendingSyncId.get(syncId); 2110 2111 try { 2112 callback.onTransactionReady(syncId, t); 2113 } catch (RemoteException e) { 2114 Slog.e(TAG, "Failed to notify transaction (" + syncId + ") ready", e); 2115 // If there's an exception when trying to send the mergedTransaction to the client, we 2116 // should immediately apply it here so the transactions aren't lost. 2117 t.apply(); 2118 } 2119 2120 mTransactionCallbacksByPendingSyncId.remove(syncId); 2121 } 2122 2123 @Override registerTransitionPlayer(ITransitionPlayer player)2124 public void registerTransitionPlayer(ITransitionPlayer player) { 2125 enforceTaskPermission("registerTransitionPlayer()"); 2126 final int callerPid = Binder.getCallingPid(); 2127 final int callerUid = Binder.getCallingUid(); 2128 final long ident = Binder.clearCallingIdentity(); 2129 try { 2130 synchronized (mGlobalLock) { 2131 final WindowProcessController wpc = 2132 mService.getProcessController(callerPid, callerUid); 2133 mTransitionController.registerTransitionPlayer(player, wpc); 2134 } 2135 } finally { 2136 Binder.restoreCallingIdentity(ident); 2137 } 2138 } 2139 2140 @Override unregisterTransitionPlayer(ITransitionPlayer player)2141 public void unregisterTransitionPlayer(ITransitionPlayer player) { 2142 enforceTaskPermission("unregisterTransitionPlayer()"); 2143 final long ident = Binder.clearCallingIdentity(); 2144 try { 2145 synchronized (mGlobalLock) { 2146 mTransitionController.unregisterTransitionPlayer(player); 2147 } 2148 } finally { 2149 Binder.restoreCallingIdentity(ident); 2150 } 2151 } 2152 2153 @Override getTransitionMetricsReporter()2154 public ITransitionMetricsReporter getTransitionMetricsReporter() { 2155 return mTransitionController.mTransitionMetricsReporter; 2156 } 2157 2158 @Override getApplyToken()2159 public IBinder getApplyToken() { 2160 enforceTaskPermission("getApplyToken()"); 2161 return SurfaceControl.Transaction.getDefaultApplyToken(); 2162 } 2163 2164 /** Whether the configuration changes are important to report back to an organizer. */ configurationsAreEqualForOrganizer( Configuration newConfig, @Nullable Configuration oldConfig)2165 static boolean configurationsAreEqualForOrganizer( 2166 Configuration newConfig, @Nullable Configuration oldConfig) { 2167 if (oldConfig == null) { 2168 return false; 2169 } 2170 int cfgChanges = newConfig.diff(oldConfig); 2171 final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 2172 ? (int) newConfig.windowConfiguration.diff(oldConfig.windowConfiguration, 2173 true /* compareUndefined */) : 0; 2174 if ((winCfgChanges & CONTROLLABLE_WINDOW_CONFIGS) == 0) { 2175 cfgChanges &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION; 2176 } 2177 return (cfgChanges & CONTROLLABLE_CONFIGS) == 0; 2178 } 2179 2180 /** 2181 * Makes sure that the transaction only contains operations that are allowed for the 2182 * {@link WindowContainerTransaction#getTaskFragmentOrganizer()}. 2183 */ enforceTaskFragmentOrganizerPermission(@onNull String func, @NonNull ITaskFragmentOrganizer organizer, @NonNull WindowContainerTransaction t)2184 private void enforceTaskFragmentOrganizerPermission(@NonNull String func, 2185 @NonNull ITaskFragmentOrganizer organizer, @NonNull WindowContainerTransaction t) { 2186 // Configuration changes 2187 final Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries = 2188 t.getChanges().entrySet().iterator(); 2189 while (entries.hasNext()) { 2190 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next(); 2191 final WindowContainer wc = WindowContainer.fromBinder(entry.getKey()); 2192 enforceTaskFragmentConfigChangeAllowed(func, wc, entry.getValue(), organizer); 2193 } 2194 2195 // Hierarchy changes 2196 final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); 2197 for (int i = hops.size() - 1; i >= 0; i--) { 2198 final WindowContainerTransaction.HierarchyOp hop = hops.get(i); 2199 final int type = hop.getType(); 2200 // Check for each type of the operations that are allowed for TaskFragmentOrganizer. 2201 switch (type) { 2202 case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION: 2203 enforceTaskFragmentOrganized(func, hop.getContainer(), organizer); 2204 if (hop.getTaskFragmentOperation() != null 2205 && hop.getTaskFragmentOperation().getSecondaryFragmentToken() != null) { 2206 enforceTaskFragmentOrganized(func, 2207 hop.getTaskFragmentOperation().getSecondaryFragmentToken(), 2208 organizer); 2209 } 2210 break; 2211 case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: 2212 // Allow finish activity if it has the activity token. 2213 break; 2214 default: 2215 // Other types of hierarchy changes are not allowed. 2216 String msg = "Permission Denial: " + func + " from pid=" 2217 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 2218 + " trying to apply a hierarchy change that is not allowed for" 2219 + " TaskFragmentOrganizer=" + organizer; 2220 Slog.w(TAG, msg); 2221 throw new SecurityException(msg); 2222 } 2223 } 2224 } 2225 2226 /** 2227 * Makes sure that the {@link TaskFragment} of the given fragment token is created and organized 2228 * by the given {@link ITaskFragmentOrganizer}. 2229 */ enforceTaskFragmentOrganized(@onNull String func, @NonNull IBinder fragmentToken, @NonNull ITaskFragmentOrganizer organizer)2230 private void enforceTaskFragmentOrganized(@NonNull String func, 2231 @NonNull IBinder fragmentToken, @NonNull ITaskFragmentOrganizer organizer) { 2232 Objects.requireNonNull(fragmentToken); 2233 final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken); 2234 // When the TaskFragment is {@code null}, it means that the TaskFragment will be created 2235 // later in the same transaction, in which case it will always be organized by the given 2236 // organizer. 2237 if (tf != null && !tf.hasTaskFragmentOrganizer(organizer)) { 2238 String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid() 2239 + ", uid=" + Binder.getCallingUid() + " trying to modify TaskFragment not" 2240 + " belonging to the TaskFragmentOrganizer=" + organizer; 2241 Slog.w(TAG, msg); 2242 throw new SecurityException(msg); 2243 } 2244 } 2245 2246 /** 2247 * For config change on {@link TaskFragment}, we only support the following operations: 2248 * {@link WindowContainerTransaction#setRelativeBounds(WindowContainerToken, Rect)}, 2249 * {@link WindowContainerTransaction#setWindowingMode(WindowContainerToken, int)}. 2250 * 2251 * For a system organizer, we additionally support 2252 * {@link WindowContainerTransaction#setHidden(WindowContainerToken, boolean)}, and 2253 * {@link WindowContainerTransaction#setFocusable(WindowContainerToken, boolean)}. See 2254 * {@link TaskFragmentOrganizerController#registerOrganizer(ITaskFragmentOrganizer, boolean)} 2255 */ enforceTaskFragmentConfigChangeAllowed(@onNull String func, @Nullable WindowContainer wc, @NonNull WindowContainerTransaction.Change change, @NonNull ITaskFragmentOrganizer organizer)2256 private void enforceTaskFragmentConfigChangeAllowed(@NonNull String func, 2257 @Nullable WindowContainer wc, @NonNull WindowContainerTransaction.Change change, 2258 @NonNull ITaskFragmentOrganizer organizer) { 2259 if (wc == null) { 2260 Slog.e(TAG, "Attempt to operate on task fragment that no longer exists"); 2261 return; 2262 } 2263 final TaskFragment tf = wc.asTaskFragment(); 2264 if (tf == null || !tf.hasTaskFragmentOrganizer(organizer)) { 2265 // Only allow to apply changes to TaskFragment that is organized by this organizer. 2266 String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid() 2267 + ", uid=" + Binder.getCallingUid() + " trying to modify window container" 2268 + " not belonging to the TaskFragmentOrganizer=" + organizer; 2269 Slog.w(TAG, msg); 2270 throw new SecurityException(msg); 2271 } 2272 2273 final int originalChangeMask = change.getChangeMask(); 2274 final int originalConfigSetMask = change.getConfigSetMask(); 2275 final int originalWindowSetMask = change.getWindowSetMask(); 2276 2277 int changeMaskToBeChecked = originalChangeMask; 2278 int configSetMaskToBeChecked = originalConfigSetMask; 2279 int windowSetMaskToBeChecked = originalWindowSetMask; 2280 2281 if (mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) { 2282 // System organizer is allowed to update the hidden and focusable state. 2283 // We unset the CHANGE_HIDDEN, CHANGE_FOCUSABLE, and CHANGE_FORCE_TRANSLUCENT bits 2284 // because they are checked here. 2285 changeMaskToBeChecked &= ~CHANGE_HIDDEN; 2286 changeMaskToBeChecked &= ~CHANGE_FOCUSABLE; 2287 changeMaskToBeChecked &= ~CHANGE_FORCE_TRANSLUCENT; 2288 } 2289 2290 // setRelativeBounds is allowed. 2291 if ((changeMaskToBeChecked & CHANGE_RELATIVE_BOUNDS) != 0 2292 && (configSetMaskToBeChecked & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 2293 && (windowSetMaskToBeChecked & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) { 2294 // For setRelativeBounds, we don't need to check whether it is outside the Task 2295 // bounds, because it is possible that the Task is also resizing, for which we don't 2296 // want to throw an exception. The bounds will be adjusted in 2297 // TaskFragment#translateRelativeBoundsToAbsoluteBounds. 2298 changeMaskToBeChecked &= ~CHANGE_RELATIVE_BOUNDS; 2299 configSetMaskToBeChecked &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION; 2300 windowSetMaskToBeChecked &= ~WindowConfiguration.WINDOW_CONFIG_BOUNDS; 2301 } 2302 2303 if (changeMaskToBeChecked == 0 && configSetMaskToBeChecked == 0 2304 && windowSetMaskToBeChecked == 0) { 2305 // All the changes have been checked. 2306 // Note that setWindowingMode is always allowed, so we don't need to check the mask. 2307 return; 2308 } 2309 2310 final String msg = "Permission Denial: " + func + " from pid=" 2311 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 2312 + " trying to apply changes of changeMask=" + originalChangeMask 2313 + " configSetMask=" + originalConfigSetMask 2314 + " windowSetMask=" + originalWindowSetMask 2315 + " to TaskFragment=" + tf + " TaskFragmentOrganizer=" + organizer; 2316 Slog.w(TAG, msg); 2317 throw new SecurityException(msg); 2318 } 2319 createTaskFragment(@onNull TaskFragmentCreationParams creationParams, @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller, @Nullable Transition transition)2320 private void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams, 2321 @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller, 2322 @Nullable Transition transition) { 2323 final ActivityRecord ownerActivity = 2324 ActivityRecord.forTokenLocked(creationParams.getOwnerToken()); 2325 final ITaskFragmentOrganizer organizer = ITaskFragmentOrganizer.Stub.asInterface( 2326 creationParams.getOrganizer().asBinder()); 2327 2328 if (mLaunchTaskFragments.containsKey(creationParams.getFragmentToken())) { 2329 final Throwable exception = 2330 new IllegalArgumentException("TaskFragment token must be unique"); 2331 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, null /* taskFragment */, 2332 OP_TYPE_CREATE_TASK_FRAGMENT, exception); 2333 return; 2334 } 2335 if (ownerActivity == null || ownerActivity.getTask() == null) { 2336 final Throwable exception = 2337 new IllegalArgumentException("Not allowed to operate with invalid ownerToken"); 2338 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, null /* taskFragment */, 2339 OP_TYPE_CREATE_TASK_FRAGMENT, exception); 2340 return; 2341 } 2342 if (!ownerActivity.isResizeable()) { 2343 final IllegalArgumentException exception = new IllegalArgumentException("Not allowed" 2344 + " to operate with non-resizable owner Activity"); 2345 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, null /* taskFragment */, 2346 OP_TYPE_CREATE_TASK_FRAGMENT, exception); 2347 return; 2348 } 2349 // The ownerActivity has to belong to the same app as the target Task. 2350 final Task ownerTask = ownerActivity.getTask(); 2351 if (ownerTask.effectiveUid != ownerActivity.getUid() 2352 || ownerTask.effectiveUid != caller.mUid) { 2353 final Throwable exception = 2354 new SecurityException("Not allowed to operate with the ownerToken while " 2355 + "the root activity of the target task belong to the different app"); 2356 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, null /* taskFragment */, 2357 OP_TYPE_CREATE_TASK_FRAGMENT, exception); 2358 return; 2359 } 2360 if (ownerTask.inPinnedWindowingMode()) { 2361 final Throwable exception = new IllegalArgumentException( 2362 "Not allowed to create TaskFragment in PIP Task"); 2363 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, null /* taskFragment */, 2364 OP_TYPE_CREATE_TASK_FRAGMENT, exception); 2365 return; 2366 } 2367 final TaskFragment taskFragment = new TaskFragment(mService, 2368 creationParams.getFragmentToken(), true /* createdByOrganizer */); 2369 taskFragment.setAllowTransitionWhenEmpty(creationParams.getAllowTransitionWhenEmpty()); 2370 // Set task fragment organizer immediately, since it might have to be notified about further 2371 // actions. 2372 TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer(); 2373 taskFragment.setTaskFragmentOrganizer(organizerToken, 2374 ownerActivity.getUid(), ownerActivity.info.processName); 2375 if (mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken.asBinder())) { 2376 taskFragment.setOverrideOrientation(creationParams.getOverrideOrientation()); 2377 } 2378 final int position; 2379 if (creationParams.getPairedPrimaryFragmentToken() != null) { 2380 // When there is a paired primary TaskFragment, we want to place the new TaskFragment 2381 // right above the paired one to make sure there is no other window in between. 2382 final TaskFragment pairedPrimaryTaskFragment = getTaskFragment( 2383 creationParams.getPairedPrimaryFragmentToken()); 2384 final int pairedPosition = ownerTask.mChildren.indexOf(pairedPrimaryTaskFragment); 2385 position = pairedPosition != -1 ? pairedPosition + 1 : POSITION_TOP; 2386 } else if (creationParams.getPairedActivityToken() != null) { 2387 // When there is a paired Activity, we want to place the new TaskFragment right above 2388 // the paired Activity to make sure the Activity position is not changed after reparent. 2389 final ActivityRecord pairedActivity = ActivityRecord.forTokenLocked( 2390 creationParams.getPairedActivityToken()); 2391 final int pairedPosition = ownerTask.mChildren.indexOf(pairedActivity); 2392 position = pairedPosition != -1 ? pairedPosition + 1 : POSITION_TOP; 2393 } else { 2394 position = POSITION_TOP; 2395 } 2396 ownerTask.addChild(taskFragment, position); 2397 EventLogTags.writeWmTfCreated(System.identityHashCode(taskFragment), ownerTask.mTaskId); 2398 taskFragment.setWindowingMode(creationParams.getWindowingMode()); 2399 if (!creationParams.getInitialRelativeBounds().isEmpty()) { 2400 // The surface operations for the task fragment should sync with the transition. 2401 // This avoid using pending transaction before collectExistenceChange is called. 2402 if (transition != null) { 2403 addToSyncSet(transition.getSyncId(), taskFragment); 2404 } 2405 // Set relative bounds instead of using setBounds. This will avoid unnecessary update in 2406 // case the parent has resized since the last time parent info is sent to the organizer. 2407 taskFragment.setRelativeEmbeddedBounds(creationParams.getInitialRelativeBounds()); 2408 // Recompute configuration as the bounds will be calculated based on relative bounds in 2409 // TaskFragment#resolveOverrideConfiguration. 2410 taskFragment.recomputeConfiguration(); 2411 } 2412 mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment); 2413 2414 if (transition != null) transition.collectExistenceChange(taskFragment); 2415 } 2416 deleteTaskFragment(@onNull TaskFragment taskFragment, @Nullable Transition transition)2417 private int deleteTaskFragment(@NonNull TaskFragment taskFragment, 2418 @Nullable Transition transition) { 2419 if (transition != null) transition.collectExistenceChange(taskFragment); 2420 2421 mLaunchTaskFragments.remove(taskFragment.getFragmentToken()); 2422 taskFragment.remove(true /* withTransition */, "deleteTaskFragment"); 2423 return TRANSACT_EFFECTS_LIFECYCLE; 2424 } 2425 2426 @Nullable getTaskFragment(IBinder tfToken)2427 TaskFragment getTaskFragment(IBinder tfToken) { 2428 return mLaunchTaskFragments.get(tfToken); 2429 } 2430 cleanUpEmbeddedTaskFragment(TaskFragment taskFragment)2431 void cleanUpEmbeddedTaskFragment(TaskFragment taskFragment) { 2432 mLaunchTaskFragments.remove(taskFragment.getFragmentToken()); 2433 } 2434 2435 static class CallerInfo { 2436 final int mPid; 2437 final int mUid; 2438 CallerInfo()2439 CallerInfo() { 2440 mPid = Binder.getCallingPid(); 2441 mUid = Binder.getCallingUid(); 2442 } 2443 } 2444 sendTaskFragmentOperationFailure(@onNull ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment, @TaskFragmentOperation.OperationType int opType, @NonNull Throwable exception)2445 void sendTaskFragmentOperationFailure(@NonNull ITaskFragmentOrganizer organizer, 2446 @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment, 2447 @TaskFragmentOperation.OperationType int opType, @NonNull Throwable exception) { 2448 if (organizer == null) { 2449 throw new IllegalArgumentException("Not allowed to operate with invalid organizer"); 2450 } 2451 mService.mTaskFragmentOrganizerController 2452 .onTaskFragmentError(organizer, errorCallbackToken, taskFragment, opType, 2453 exception); 2454 } 2455 convertStartFailureToThrowable(int result, Intent intent)2456 private Throwable convertStartFailureToThrowable(int result, Intent intent) { 2457 switch (result) { 2458 case ActivityManager.START_INTENT_NOT_RESOLVED: 2459 case ActivityManager.START_CLASS_NOT_FOUND: 2460 return new ActivityNotFoundException("No Activity found to handle " + intent); 2461 case ActivityManager.START_PERMISSION_DENIED: 2462 return new SecurityException("Permission denied and not allowed to start activity " 2463 + intent); 2464 case ActivityManager.START_CANCELED: 2465 return new AndroidRuntimeException("Activity could not be started for " + intent 2466 + " with error code : " + result); 2467 default: 2468 return new AndroidRuntimeException("Start activity failed with error code : " 2469 + result + " when starting " + intent); 2470 } 2471 } 2472 } 2473