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