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