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.view.Display.DEFAULT_DISPLAY; 22 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; 23 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT; 24 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT; 25 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK; 26 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT; 27 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; 28 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; 29 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; 30 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN; 31 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS; 32 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; 33 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT; 34 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; 35 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT; 36 37 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; 38 import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED; 39 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; 40 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; 41 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; 42 import static com.android.server.wm.WindowContainer.POSITION_TOP; 43 44 import android.annotation.NonNull; 45 import android.annotation.Nullable; 46 import android.app.ActivityManager; 47 import android.app.ActivityOptions; 48 import android.app.IApplicationThread; 49 import android.app.WindowConfiguration; 50 import android.content.ActivityNotFoundException; 51 import android.content.Intent; 52 import android.content.pm.ActivityInfo; 53 import android.content.res.Configuration; 54 import android.graphics.Rect; 55 import android.os.Binder; 56 import android.os.Bundle; 57 import android.os.IBinder; 58 import android.os.Parcel; 59 import android.os.RemoteException; 60 import android.util.AndroidRuntimeException; 61 import android.util.ArrayMap; 62 import android.util.ArraySet; 63 import android.util.Slog; 64 import android.view.RemoteAnimationAdapter; 65 import android.view.SurfaceControl; 66 import android.window.IDisplayAreaOrganizerController; 67 import android.window.ITaskFragmentOrganizer; 68 import android.window.ITaskFragmentOrganizerController; 69 import android.window.ITaskOrganizerController; 70 import android.window.ITransitionMetricsReporter; 71 import android.window.ITransitionPlayer; 72 import android.window.IWindowContainerTransactionCallback; 73 import android.window.IWindowOrganizerController; 74 import android.window.TaskFragmentCreationParams; 75 import android.window.WindowContainerTransaction; 76 77 import com.android.internal.annotations.VisibleForTesting; 78 import com.android.internal.protolog.common.ProtoLog; 79 import com.android.internal.util.ArrayUtils; 80 import com.android.internal.util.function.pooled.PooledConsumer; 81 import com.android.internal.util.function.pooled.PooledLambda; 82 83 import java.util.ArrayList; 84 import java.util.HashMap; 85 import java.util.Iterator; 86 import java.util.List; 87 import java.util.Map; 88 import java.util.function.Function; 89 90 /** 91 * Server side implementation for the interface for organizing windows 92 * @see android.window.WindowOrganizer 93 */ 94 class WindowOrganizerController extends IWindowOrganizerController.Stub 95 implements BLASTSyncEngine.TransactionReadyListener { 96 97 private static final String TAG = "WindowOrganizerController"; 98 99 /** Flag indicating that an applied transaction may have effected lifecycle */ 100 private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1; 101 private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1; 102 103 /** 104 * Masks specifying which configurations task-organizers can control. Incoming transactions 105 * will be filtered to only include these. 106 */ 107 static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION 108 | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE; 109 static final int CONTROLLABLE_WINDOW_CONFIGS = WindowConfiguration.WINDOW_CONFIG_BOUNDS 110 | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; 111 112 private final ActivityTaskManagerService mService; 113 private final WindowManagerGlobalLock mGlobalLock; 114 115 private final HashMap<Integer, IWindowContainerTransactionCallback> 116 mTransactionCallbacksByPendingSyncId = new HashMap(); 117 118 final TaskOrganizerController mTaskOrganizerController; 119 final DisplayAreaOrganizerController mDisplayAreaOrganizerController; 120 final TaskFragmentOrganizerController mTaskFragmentOrganizerController; 121 122 TransitionController mTransitionController; 123 /** 124 * A Map which manages the relationship between 125 * {@link TaskFragmentCreationParams#getFragmentToken()} and {@link TaskFragment} 126 */ 127 @VisibleForTesting 128 final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>(); 129 WindowOrganizerController(ActivityTaskManagerService atm)130 WindowOrganizerController(ActivityTaskManagerService atm) { 131 mService = atm; 132 mGlobalLock = atm.mGlobalLock; 133 mTaskOrganizerController = new TaskOrganizerController(mService); 134 mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService); 135 mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm); 136 } 137 setWindowManager(WindowManagerService wms)138 void setWindowManager(WindowManagerService wms) { 139 mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController); 140 mTransitionController.registerLegacyListener(wms.mActivityManagerAppTransitionNotifier); 141 } 142 getTransitionController()143 TransitionController getTransitionController() { 144 return mTransitionController; 145 } 146 147 @Override onTransact(int code, Parcel data, Parcel reply, int flags)148 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 149 throws RemoteException { 150 try { 151 return super.onTransact(code, data, reply, flags); 152 } catch (RuntimeException e) { 153 throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e); 154 } 155 } 156 157 @Override applyTransaction(WindowContainerTransaction t)158 public void applyTransaction(WindowContainerTransaction t) { 159 if (t == null) { 160 throw new IllegalArgumentException("Null transaction passed to applyTransaction"); 161 } 162 enforceTaskPermission("applyTransaction()", t); 163 final CallerInfo caller = new CallerInfo(); 164 final long ident = Binder.clearCallingIdentity(); 165 try { 166 synchronized (mGlobalLock) { 167 applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller); 168 } 169 } finally { 170 Binder.restoreCallingIdentity(ident); 171 } 172 } 173 174 @Override applySyncTransaction(WindowContainerTransaction t, IWindowContainerTransactionCallback callback)175 public int applySyncTransaction(WindowContainerTransaction t, 176 IWindowContainerTransactionCallback callback) { 177 if (t == null) { 178 throw new IllegalArgumentException("Null transaction passed to applySyncTransaction"); 179 } 180 enforceTaskPermission("applySyncTransaction()", t); 181 final CallerInfo caller = new CallerInfo(); 182 final long ident = Binder.clearCallingIdentity(); 183 try { 184 synchronized (mGlobalLock) { 185 /** 186 * If callback is non-null we are looking to synchronize this transaction by 187 * collecting all the results in to a SurfaceFlinger transaction and then delivering 188 * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the 189 * details of the operation. But at a high level we create a sync operation with a 190 * given ID and an associated callback. Then we notify each WindowContainer in this 191 * WindowContainer transaction that it is participating in a sync operation with 192 * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady" 193 * which means that we have added everything to the set. At any point after this, 194 * all the WindowContainers will eventually finish applying their changes and notify 195 * the BLASTSyncEngine which will deliver the Transaction to the callback. 196 */ 197 int syncId = -1; 198 if (callback != null) { 199 syncId = startSyncWithOrganizer(callback); 200 } 201 applyTransaction(t, syncId, null /*transition*/, caller); 202 if (syncId >= 0) { 203 setSyncReady(syncId); 204 } 205 return syncId; 206 } 207 } finally { 208 Binder.restoreCallingIdentity(ident); 209 } 210 } 211 212 @Override startTransition(int type, @Nullable IBinder transitionToken, @Nullable WindowContainerTransaction t)213 public IBinder startTransition(int type, @Nullable IBinder transitionToken, 214 @Nullable WindowContainerTransaction t) { 215 enforceTaskPermission("startTransition()"); 216 final CallerInfo caller = new CallerInfo(); 217 final long ident = Binder.clearCallingIdentity(); 218 try { 219 synchronized (mGlobalLock) { 220 Transition transition = Transition.fromBinder(transitionToken); 221 // In cases where transition is already provided, the "readiness lifecycle" of the 222 // transition is determined outside of this transaction. However, if this is a 223 // direct call from shell, the entire transition lifecycle is contained in the 224 // provided transaction and thus we can setReady immediately after apply. 225 boolean needsSetReady = transition == null && t != null; 226 if (transition == null) { 227 if (type < 0) { 228 throw new IllegalArgumentException("Can't create transition with no type"); 229 } 230 if (mTransitionController.getTransitionPlayer() == null) { 231 Slog.w(TAG, "Using shell transitions API for legacy transitions."); 232 if (t == null) { 233 throw new IllegalArgumentException("Can't use legacy transitions in" 234 + " compatibility mode with no WCT."); 235 } 236 applyTransaction(t, -1 /* syncId */, null, caller); 237 return null; 238 } 239 transition = mTransitionController.createTransition(type); 240 } 241 transition.start(); 242 if (t == null) { 243 t = new WindowContainerTransaction(); 244 } 245 applyTransaction(t, -1 /*syncId*/, transition, caller); 246 if (needsSetReady) { 247 transition.setAllReady(); 248 } 249 return transition; 250 } 251 } finally { 252 Binder.restoreCallingIdentity(ident); 253 } 254 } 255 256 @Override startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter, @NonNull IWindowContainerTransactionCallback callback, @NonNull WindowContainerTransaction t)257 public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter, 258 @NonNull IWindowContainerTransactionCallback callback, 259 @NonNull WindowContainerTransaction t) { 260 enforceTaskPermission("startLegacyTransition()"); 261 final CallerInfo caller = new CallerInfo(); 262 final long ident = Binder.clearCallingIdentity(); 263 int syncId; 264 try { 265 synchronized (mGlobalLock) { 266 if (type < 0) { 267 throw new IllegalArgumentException("Can't create transition with no type"); 268 } 269 if (mTransitionController.getTransitionPlayer() != null) { 270 throw new IllegalArgumentException("Can't use legacy transitions in" 271 + " when shell transitions are enabled."); 272 } 273 final DisplayContent dc = 274 mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY); 275 if (dc.mAppTransition.isTransitionSet()) { 276 // a transition already exists, so the callback probably won't be called. 277 return -1; 278 } 279 adapter.setCallingPidUid(caller.mPid, caller.mUid); 280 dc.prepareAppTransition(type); 281 dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */); 282 syncId = startSyncWithOrganizer(callback); 283 applyTransaction(t, syncId, null /* transition */, caller); 284 setSyncReady(syncId); 285 } 286 } finally { 287 Binder.restoreCallingIdentity(ident); 288 } 289 return syncId; 290 } 291 292 @Override finishTransition(@onNull IBinder transitionToken, @Nullable WindowContainerTransaction t, @Nullable IWindowContainerTransactionCallback callback)293 public int finishTransition(@NonNull IBinder transitionToken, 294 @Nullable WindowContainerTransaction t, 295 @Nullable IWindowContainerTransactionCallback callback) { 296 enforceTaskPermission("finishTransition()"); 297 final CallerInfo caller = new CallerInfo(); 298 final long ident = Binder.clearCallingIdentity(); 299 try { 300 synchronized (mGlobalLock) { 301 int syncId = -1; 302 if (t != null && callback != null) { 303 syncId = startSyncWithOrganizer(callback); 304 } 305 // apply the incoming transaction before finish in case it alters the visibility 306 // of the participants. 307 if (t != null) { 308 applyTransaction(t, syncId, null /*transition*/, caller); 309 } 310 getTransitionController().finishTransition(transitionToken); 311 if (syncId >= 0) { 312 setSyncReady(syncId); 313 } 314 return syncId; 315 } 316 } finally { 317 Binder.restoreCallingIdentity(ident); 318 } 319 } 320 321 /** 322 * @param syncId If non-null, this will be a sync-transaction. 323 * @param transition A transition to collect changes into. 324 * @param caller Info about the calling process. 325 */ applyTransaction(@onNull WindowContainerTransaction t, int syncId, @Nullable Transition transition, @NonNull CallerInfo caller)326 private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId, 327 @Nullable Transition transition, @NonNull CallerInfo caller) { 328 int effects = 0; 329 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId); 330 mService.deferWindowLayout(); 331 mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */); 332 try { 333 if (transition != null) { 334 // First check if we have a display rotation transition and if so, update it. 335 final DisplayContent dc = DisplayRotation.getDisplayFromTransition(transition); 336 if (dc != null && transition.mChanges.get(dc).hasChanged(dc)) { 337 // Go through all tasks and collect them before the rotation 338 // TODO(shell-transitions): move collect() to onConfigurationChange once 339 // wallpaper handling is synchronized. 340 dc.forAllTasks(task -> { 341 if (task.isVisible()) transition.collect(task); 342 }); 343 dc.getInsetsStateController().addProvidersToTransition(); 344 dc.sendNewConfiguration(); 345 effects |= TRANSACT_EFFECTS_LIFECYCLE; 346 } 347 } 348 ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); 349 Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries = 350 t.getChanges().entrySet().iterator(); 351 while (entries.hasNext()) { 352 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next(); 353 final WindowContainer wc = WindowContainer.fromBinder(entry.getKey()); 354 if (wc == null || !wc.isAttached()) { 355 Slog.e(TAG, "Attempt to operate on detached container: " + wc); 356 continue; 357 } 358 // Make sure we add to the syncSet before performing 359 // operations so we don't end up splitting effects between the WM 360 // pending transaction and the BLASTSync transaction. 361 if (syncId >= 0) { 362 addToSyncSet(syncId, wc); 363 } 364 if (transition != null) transition.collect(wc); 365 366 int containerEffect = applyWindowContainerChange(wc, entry.getValue()); 367 effects |= containerEffect; 368 369 // Lifecycle changes will trigger ensureConfig for everything. 370 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 371 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { 372 haveConfigChanges.add(wc); 373 } 374 } 375 // Hierarchy changes 376 final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); 377 final int hopSize = hops.size(); 378 if (hopSize > 0) { 379 final boolean isInLockTaskMode = mService.isInLockTaskMode(); 380 for (int i = 0; i < hopSize; ++i) { 381 effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition, 382 isInLockTaskMode, caller, t.getErrorCallbackToken(), 383 t.getTaskFragmentOrganizer()); 384 } 385 } 386 // Queue-up bounds-change transactions for tasks which are now organized. Do 387 // this after hierarchy ops so we have the final organized state. 388 entries = t.getChanges().entrySet().iterator(); 389 while (entries.hasNext()) { 390 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next(); 391 final WindowContainer wc = WindowContainer.fromBinder(entry.getKey()); 392 if (wc == null || !wc.isAttached()) { 393 Slog.e(TAG, "Attempt to operate on detached container: " + wc); 394 continue; 395 } 396 final Task task = wc.asTask(); 397 final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds(); 398 if (task == null || !task.isAttached() || surfaceBounds == null) { 399 continue; 400 } 401 if (!task.isOrganized()) { 402 final Task parent = task.getParent() != null ? task.getParent().asTask() : null; 403 // Also allow direct children of created-by-organizer tasks to be 404 // controlled. In the future, these will become organized anyways. 405 if (parent == null || !parent.mCreatedByOrganizer) { 406 throw new IllegalArgumentException( 407 "Can't manipulate non-organized task surface " + task); 408 } 409 } 410 final SurfaceControl.Transaction sft = new SurfaceControl.Transaction(); 411 final SurfaceControl sc = task.getSurfaceControl(); 412 sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top); 413 if (surfaceBounds.isEmpty()) { 414 sft.setWindowCrop(sc, null); 415 } else { 416 sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height()); 417 } 418 task.setMainWindowSizeChangeTransaction(sft); 419 } 420 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) { 421 mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */); 422 // Already calls ensureActivityConfig 423 mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); 424 mService.mRootWindowContainer.resumeFocusedTasksTopActivities(); 425 } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { 426 final PooledConsumer f = PooledLambda.obtainConsumer( 427 ActivityRecord::ensureActivityConfiguration, 428 PooledLambda.__(ActivityRecord.class), 0, 429 true /* preserveWindow */); 430 try { 431 for (int i = haveConfigChanges.size() - 1; i >= 0; --i) { 432 haveConfigChanges.valueAt(i).forAllActivities(f); 433 } 434 } finally { 435 f.recycle(); 436 } 437 } 438 439 if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) == 0) { 440 mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED); 441 } 442 } finally { 443 mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */); 444 mService.continueWindowLayout(); 445 } 446 } 447 applyChanges(WindowContainer container, WindowContainerTransaction.Change change)448 private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) { 449 // The "client"-facing API should prevent bad changes; however, just in case, sanitize 450 // masks here. 451 final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS; 452 final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS; 453 int effects = 0; 454 final int windowingMode = change.getWindowingMode(); 455 if (configMask != 0) { 456 if (windowingMode > -1 && windowingMode != container.getWindowingMode()) { 457 // Special handling for when we are setting a windowingMode in the same transaction. 458 // Setting the windowingMode is going to call onConfigurationChanged so we don't 459 // need it called right now. Additionally, some logic requires everything in the 460 // configuration to change at the same time (ie. surface-freezer requires bounds 461 // and mode to change at the same time). 462 final Configuration c = container.getRequestedOverrideConfiguration(); 463 c.setTo(change.getConfiguration(), configMask, windowMask); 464 } else { 465 final Configuration c = 466 new Configuration(container.getRequestedOverrideConfiguration()); 467 c.setTo(change.getConfiguration(), configMask, windowMask); 468 container.onRequestedOverrideConfigurationChanged(c); 469 } 470 effects |= TRANSACT_EFFECTS_CLIENT_CONFIG; 471 if (windowMask != 0 && container.isEmbedded()) { 472 // Changing bounds of the embedded TaskFragments may result in lifecycle changes. 473 effects |= TRANSACT_EFFECTS_LIFECYCLE; 474 } 475 } 476 if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) { 477 if (container.setFocusable(change.getFocusable())) { 478 effects |= TRANSACT_EFFECTS_LIFECYCLE; 479 } 480 } 481 482 if (windowingMode > -1) { 483 if (mService.isInLockTaskMode() 484 && WindowConfiguration.inMultiWindowMode(windowingMode)) { 485 throw new UnsupportedOperationException("Not supported to set multi-window" 486 + " windowing mode during locked task mode."); 487 } 488 489 final int prevMode = container.getWindowingMode(); 490 container.setWindowingMode(windowingMode); 491 if (prevMode != container.getWindowingMode()) { 492 // The activity in the container may become focusable or non-focusable due to 493 // windowing modes changes (such as entering or leaving pinned windowing mode), 494 // so also apply the lifecycle effects to this transaction. 495 effects |= TRANSACT_EFFECTS_LIFECYCLE; 496 } 497 } 498 return effects; 499 } 500 applyTaskChanges(Task tr, WindowContainerTransaction.Change c)501 private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) { 502 int effects = 0; 503 final SurfaceControl.Transaction t = c.getBoundsChangeTransaction(); 504 505 if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) { 506 if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) { 507 effects = TRANSACT_EFFECTS_LIFECYCLE; 508 } 509 } 510 511 final int childWindowingMode = c.getActivityWindowingMode(); 512 if (childWindowingMode > -1) { 513 tr.setActivityWindowingMode(childWindowingMode); 514 } 515 516 if (t != null) { 517 tr.setMainWindowSizeChangeTransaction(t); 518 } 519 520 Rect enterPipBounds = c.getEnterPipBounds(); 521 if (enterPipBounds != null) { 522 tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds); 523 } 524 525 return effects; 526 } 527 applyDisplayAreaChanges(DisplayArea displayArea, WindowContainerTransaction.Change c)528 private int applyDisplayAreaChanges(DisplayArea displayArea, 529 WindowContainerTransaction.Change c) { 530 final int[] effects = new int[1]; 531 532 if ((c.getChangeMask() 533 & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) { 534 if (displayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) { 535 effects[0] |= TRANSACT_EFFECTS_LIFECYCLE; 536 } 537 } 538 539 displayArea.forAllTasks(task -> { 540 Task tr = (Task) task; 541 if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) { 542 if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) { 543 effects[0] |= TRANSACT_EFFECTS_LIFECYCLE; 544 } 545 } 546 }); 547 548 return effects[0]; 549 } 550 applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects, int syncId, @Nullable Transition transition, boolean isInLockTaskMode, @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer)551 private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects, 552 int syncId, @Nullable Transition transition, boolean isInLockTaskMode, 553 @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken, 554 @Nullable ITaskFragmentOrganizer organizer) { 555 final int type = hop.getType(); 556 switch (type) { 557 case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { 558 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); 559 final Task task = wc != null ? wc.asTask() : null; 560 if (task != null) { 561 task.getDisplayArea().setLaunchRootTask(task, 562 hop.getWindowingModes(), hop.getActivityTypes()); 563 } else { 564 throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc); 565 } 566 break; 567 } 568 case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: { 569 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); 570 final Task task = wc != null ? wc.asTask() : null; 571 final boolean clearRoot = hop.getToTop(); 572 if (task == null) { 573 throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc); 574 } else if (!task.mCreatedByOrganizer) { 575 throw new UnsupportedOperationException( 576 "Cannot set non-organized task as adjacent flag root: " + wc); 577 } else if (task.getAdjacentTaskFragment() == null && !clearRoot) { 578 throw new UnsupportedOperationException( 579 "Cannot set non-adjacent task as adjacent flag root: " + wc); 580 } 581 582 task.getDisplayArea().setLaunchAdjacentFlagRootTask(clearRoot ? null : task); 583 break; 584 } 585 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: { 586 effects |= setAdjacentRootsHierarchyOp(hop); 587 break; 588 } 589 case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: { 590 final TaskFragmentCreationParams taskFragmentCreationOptions = 591 hop.getTaskFragmentCreationOptions(); 592 createTaskFragment(taskFragmentCreationOptions, errorCallbackToken, caller); 593 break; 594 } 595 case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: { 596 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); 597 if (wc == null || !wc.isAttached()) { 598 Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc); 599 break; 600 } 601 final TaskFragment taskFragment = wc.asTaskFragment(); 602 if (taskFragment == null || taskFragment.asTask() != null) { 603 throw new IllegalArgumentException( 604 "Can only delete organized TaskFragment, but not Task."); 605 } 606 if (isInLockTaskMode) { 607 final ActivityRecord bottomActivity = taskFragment.getActivity( 608 a -> !a.finishing, false /* traverseTopToBottom */); 609 if (bottomActivity != null 610 && mService.getLockTaskController().activityBlockedFromFinish( 611 bottomActivity)) { 612 Slog.w(TAG, "Skip removing TaskFragment due in lock task mode."); 613 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, 614 new IllegalStateException( 615 "Not allow to delete task fragment in lock task mode.")); 616 break; 617 } 618 } 619 effects |= deleteTaskFragment(taskFragment, errorCallbackToken); 620 break; 621 } 622 case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: { 623 final IBinder fragmentToken = hop.getContainer(); 624 if (!mLaunchTaskFragments.containsKey(fragmentToken)) { 625 final Throwable exception = new IllegalArgumentException( 626 "Not allowed to operate with invalid fragment token"); 627 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); 628 break; 629 } 630 final Intent activityIntent = hop.getActivityIntent(); 631 final Bundle activityOptions = hop.getLaunchOptions(); 632 final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken); 633 final int result = mService.getActivityStartController() 634 .startActivityInTaskFragment(tf, activityIntent, activityOptions, 635 hop.getCallingActivity(), caller.mUid, caller.mPid); 636 if (!isStartResultSuccessful(result)) { 637 sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(), 638 errorCallbackToken, 639 convertStartFailureToThrowable(result, activityIntent)); 640 } 641 break; 642 } 643 case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: { 644 final IBinder fragmentToken = hop.getNewParent(); 645 final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer()); 646 if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) { 647 final Throwable exception = new IllegalArgumentException( 648 "Not allowed to operate with invalid fragment token or activity."); 649 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); 650 break; 651 } 652 activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP); 653 effects |= TRANSACT_EFFECTS_LIFECYCLE; 654 break; 655 } 656 case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: { 657 final IBinder fragmentToken = hop.getContainer(); 658 final IBinder adjacentFragmentToken = hop.getAdjacentRoot(); 659 final TaskFragment tf1 = mLaunchTaskFragments.get(fragmentToken); 660 final TaskFragment tf2 = adjacentFragmentToken != null 661 ? mLaunchTaskFragments.get(adjacentFragmentToken) 662 : null; 663 if (tf1 == null || (adjacentFragmentToken != null && tf2 == null)) { 664 final Throwable exception = new IllegalArgumentException( 665 "Not allowed to set adjacent on invalid fragment tokens"); 666 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); 667 break; 668 } 669 tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */); 670 effects |= TRANSACT_EFFECTS_LIFECYCLE; 671 672 final Bundle bundle = hop.getLaunchOptions(); 673 final WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams = 674 bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentParams( 675 bundle) : null; 676 if (adjacentParams == null) { 677 break; 678 } 679 680 tf1.setDelayLastActivityRemoval( 681 adjacentParams.shouldDelayPrimaryLastActivityRemoval()); 682 if (tf2 != null) { 683 tf2.setDelayLastActivityRemoval( 684 adjacentParams.shouldDelaySecondaryLastActivityRemoval()); 685 } 686 break; 687 } 688 default: { 689 // The other operations may change task order so they are skipped while in lock 690 // task mode. The above operations are still allowed because they don't move 691 // tasks. And it may be necessary such as clearing launch root after entering 692 // lock task mode. 693 if (isInLockTaskMode) { 694 Slog.w(TAG, "Skip applying hierarchy operation " + hop 695 + " while in lock task mode"); 696 return effects; 697 } 698 } 699 } 700 701 switch (type) { 702 case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: { 703 effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); 704 break; 705 } 706 case HIERARCHY_OP_TYPE_REORDER: 707 case HIERARCHY_OP_TYPE_REPARENT: { 708 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); 709 if (wc == null || !wc.isAttached()) { 710 Slog.e(TAG, "Attempt to operate on detached container: " + wc); 711 break; 712 } 713 if (syncId >= 0) { 714 addToSyncSet(syncId, wc); 715 } 716 if (transition != null) { 717 transition.collect(wc); 718 if (hop.isReparent()) { 719 if (wc.getParent() != null) { 720 // Collect the current parent. It's visibility may change as 721 // a result of this reparenting. 722 transition.collect(wc.getParent()); 723 } 724 if (hop.getNewParent() != null) { 725 final WindowContainer parentWc = 726 WindowContainer.fromBinder(hop.getNewParent()); 727 if (parentWc == null) { 728 Slog.e(TAG, "Can't resolve parent window from token"); 729 break; 730 } 731 transition.collect(parentWc); 732 } 733 } 734 } 735 effects |= sanitizeAndApplyHierarchyOp(wc, hop); 736 break; 737 } 738 case HIERARCHY_OP_TYPE_LAUNCH_TASK: { 739 mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, 740 "launchTask HierarchyOp"); 741 final Bundle launchOpts = hop.getLaunchOptions(); 742 final int taskId = launchOpts.getInt( 743 WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); 744 launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); 745 final SafeActivityOptions safeOptions = 746 SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid); 747 final Integer[] starterResult = {null}; 748 // startActivityFromRecents should not be called in lock. 749 mService.mH.post(() -> { 750 try { 751 starterResult[0] = mService.mTaskSupervisor.startActivityFromRecents( 752 caller.mPid, caller.mUid, taskId, safeOptions); 753 } catch (Throwable t) { 754 starterResult[0] = ActivityManager.START_CANCELED; 755 Slog.w(TAG, t); 756 } 757 synchronized (mGlobalLock) { 758 mGlobalLock.notifyAll(); 759 } 760 }); 761 while (starterResult[0] == null) { 762 try { 763 mGlobalLock.wait(); 764 } catch (InterruptedException ignored) { 765 } 766 } 767 break; 768 } 769 case HIERARCHY_OP_TYPE_PENDING_INTENT: { 770 String resolvedType = hop.getActivityIntent() != null 771 ? hop.getActivityIntent().resolveTypeIfNeeded( 772 mService.mContext.getContentResolver()) 773 : null; 774 775 Bundle options = null; 776 if (hop.getPendingIntent().isActivity()) { 777 // Set the context display id as preferred for this activity launches, so that 778 // it can land on caller's display. Or just brought the task to front at the 779 // display where it was on since it has higher preference. 780 ActivityOptions activityOptions = hop.getLaunchOptions() != null 781 ? new ActivityOptions(hop.getLaunchOptions()) 782 : ActivityOptions.makeBasic(); 783 activityOptions.setCallerDisplayId(DEFAULT_DISPLAY); 784 options = activityOptions.toBundle(); 785 } 786 787 mService.mAmInternal.sendIntentSender(hop.getPendingIntent().getTarget(), 788 hop.getPendingIntent().getWhitelistToken(), 0 /* code */, 789 hop.getActivityIntent(), resolvedType, null /* finishReceiver */, 790 null /* requiredPermission */, options); 791 break; 792 } 793 case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: { 794 final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer()); 795 final WindowContainer newParent = hop.getNewParent() != null 796 ? WindowContainer.fromBinder(hop.getNewParent()) 797 : null; 798 if (oldParent == null || !oldParent.isAttached()) { 799 Slog.e(TAG, "Attempt to operate on unknown or detached container: " 800 + oldParent); 801 break; 802 } 803 reparentTaskFragment(oldParent, newParent, errorCallbackToken); 804 effects |= TRANSACT_EFFECTS_LIFECYCLE; 805 break; 806 } 807 } 808 return effects; 809 } 810 sanitizeAndApplyHierarchyOp(WindowContainer container, WindowContainerTransaction.HierarchyOp hop)811 private int sanitizeAndApplyHierarchyOp(WindowContainer container, 812 WindowContainerTransaction.HierarchyOp hop) { 813 final Task task = container.asTask(); 814 if (task == null) { 815 throw new IllegalArgumentException("Invalid container in hierarchy op"); 816 } 817 final DisplayContent dc = task.getDisplayContent(); 818 if (dc == null) { 819 Slog.w(TAG, "Container is no longer attached: " + task); 820 return 0; 821 } 822 final Task as = task; 823 824 if (hop.isReparent()) { 825 final boolean isNonOrganizedRootableTask = 826 task.isRootTask() || task.getParent().asTask().mCreatedByOrganizer; 827 if (isNonOrganizedRootableTask) { 828 WindowContainer newParent = hop.getNewParent() == null 829 ? dc.getDefaultTaskDisplayArea() 830 : WindowContainer.fromBinder(hop.getNewParent()); 831 if (newParent == null) { 832 Slog.e(TAG, "Can't resolve parent window from token"); 833 return 0; 834 } 835 if (task.getParent() != newParent) { 836 if (newParent.asTaskDisplayArea() != null) { 837 // For now, reparenting to displayarea is different from other reparents... 838 as.reparent(newParent.asTaskDisplayArea(), hop.getToTop()); 839 } else if (newParent.asTask() != null) { 840 if (newParent.inMultiWindowMode() && task.isLeafTask()) { 841 if (newParent.inPinnedWindowingMode()) { 842 Slog.w(TAG, "Can't support moving a task to another PIP window..." 843 + " newParent=" + newParent + " task=" + task); 844 return 0; 845 } 846 if (!task.supportsMultiWindowInDisplayArea( 847 newParent.asTask().getDisplayArea())) { 848 Slog.w(TAG, "Can't support task that doesn't support multi-window" 849 + " mode in multi-window mode... newParent=" + newParent 850 + " task=" + task); 851 return 0; 852 } 853 } 854 task.reparent((Task) newParent, 855 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, 856 false /*moveParents*/, "sanitizeAndApplyHierarchyOp"); 857 } else { 858 throw new RuntimeException("Can only reparent task to another task or" 859 + " taskDisplayArea, but not " + newParent); 860 } 861 } else { 862 final Task rootTask = (Task) ( 863 (newParent != null && !(newParent instanceof TaskDisplayArea)) 864 ? newParent : task.getRootTask()); 865 as.getDisplayArea().positionChildAt( 866 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, rootTask, 867 false /* includingParents */); 868 } 869 } else { 870 throw new RuntimeException("Reparenting leaf Tasks is not supported now. " + task); 871 } 872 } else { 873 task.getParent().positionChildAt( 874 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, 875 task, false /* includingParents */); 876 } 877 return TRANSACT_EFFECTS_LIFECYCLE; 878 } 879 reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop, @Nullable Transition transition, int syncId)880 private int reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop, 881 @Nullable Transition transition, int syncId) { 882 WindowContainer currentParent = hop.getContainer() != null 883 ? WindowContainer.fromBinder(hop.getContainer()) : null; 884 WindowContainer newParent = hop.getNewParent() != null 885 ? WindowContainer.fromBinder(hop.getNewParent()) : null; 886 if (currentParent == null && newParent == null) { 887 throw new IllegalArgumentException("reparentChildrenTasksHierarchyOp: " + hop); 888 } else if (currentParent == null) { 889 currentParent = newParent.asTask().getDisplayContent().getDefaultTaskDisplayArea(); 890 } else if (newParent == null) { 891 newParent = currentParent.asTask().getDisplayContent().getDefaultTaskDisplayArea(); 892 } 893 894 if (currentParent == newParent) { 895 Slog.e(TAG, "reparentChildrenTasksHierarchyOp parent not changing: " + hop); 896 return 0; 897 } 898 if (!currentParent.isAttached()) { 899 Slog.e(TAG, "reparentChildrenTasksHierarchyOp currentParent detached=" 900 + currentParent + " hop=" + hop); 901 return 0; 902 } 903 if (!newParent.isAttached()) { 904 Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent detached=" 905 + newParent + " hop=" + hop); 906 return 0; 907 } 908 if (newParent.inPinnedWindowingMode()) { 909 Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent in PIP=" 910 + newParent + " hop=" + hop); 911 return 0; 912 } 913 914 final boolean newParentInMultiWindow = newParent.inMultiWindowMode(); 915 final TaskDisplayArea newParentTda = newParent.asTask() != null 916 ? newParent.asTask().getDisplayArea() 917 : newParent.asTaskDisplayArea(); 918 final WindowContainer finalCurrentParent = currentParent; 919 Slog.i(TAG, "reparentChildrenTasksHierarchyOp" 920 + " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop); 921 922 // We want to collect the tasks first before re-parenting to avoid array shifting on us. 923 final ArrayList<Task> tasksToReparent = new ArrayList<>(); 924 925 currentParent.forAllTasks((Function<Task, Boolean>) task -> { 926 Slog.i(TAG, " Processing task=" + task); 927 final boolean reparent; 928 if (task.mCreatedByOrganizer || task.getParent() != finalCurrentParent) { 929 // We only care about non-organized task that are direct children of the thing we 930 // are reparenting from. 931 return false; 932 } 933 if (newParentInMultiWindow && !task.supportsMultiWindowInDisplayArea(newParentTda)) { 934 Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task to multi window," 935 + " task=" + task); 936 return false; 937 } 938 if (!ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType()) 939 || !ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) { 940 return false; 941 } 942 943 if (hop.getToTop()) { 944 tasksToReparent.add(0, task); 945 } else { 946 tasksToReparent.add(task); 947 } 948 return hop.getReparentTopOnly() && tasksToReparent.size() == 1; 949 }); 950 951 final int count = tasksToReparent.size(); 952 for (int i = 0; i < count; ++i) { 953 final Task task = tasksToReparent.get(i); 954 if (syncId >= 0) { 955 addToSyncSet(syncId, task); 956 } 957 if (transition != null) transition.collect(task); 958 959 if (newParent instanceof TaskDisplayArea) { 960 // For now, reparenting to display area is different from other reparents... 961 task.reparent((TaskDisplayArea) newParent, hop.getToTop()); 962 } else { 963 task.reparent((Task) newParent, 964 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, 965 false /*moveParents*/, "processChildrenTaskReparentHierarchyOp"); 966 } 967 } 968 969 if (transition != null) transition.collect(newParent); 970 971 return TRANSACT_EFFECTS_LIFECYCLE; 972 } 973 setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop)974 private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) { 975 final TaskFragment root1 = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment(); 976 final TaskFragment root2 = 977 WindowContainer.fromBinder(hop.getAdjacentRoot()).asTaskFragment(); 978 if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) { 979 throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" 980 + " organizer root1=" + root1 + " root2=" + root2); 981 } 982 root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether()); 983 return TRANSACT_EFFECTS_LIFECYCLE; 984 } 985 sanitizeWindowContainer(WindowContainer wc)986 private void sanitizeWindowContainer(WindowContainer wc) { 987 if (!(wc instanceof TaskFragment) && !(wc instanceof DisplayArea)) { 988 throw new RuntimeException("Invalid token in task fragment or displayArea transaction"); 989 } 990 } 991 applyWindowContainerChange(WindowContainer wc, WindowContainerTransaction.Change c)992 private int applyWindowContainerChange(WindowContainer wc, 993 WindowContainerTransaction.Change c) { 994 sanitizeWindowContainer(wc); 995 996 int effects = applyChanges(wc, c); 997 998 if (wc instanceof DisplayArea) { 999 effects |= applyDisplayAreaChanges(wc.asDisplayArea(), c); 1000 } else if (wc instanceof Task) { 1001 effects |= applyTaskChanges(wc.asTask(), c); 1002 } 1003 1004 return effects; 1005 } 1006 1007 @Override getTaskOrganizerController()1008 public ITaskOrganizerController getTaskOrganizerController() { 1009 enforceTaskPermission("getTaskOrganizerController()"); 1010 return mTaskOrganizerController; 1011 } 1012 1013 @Override getDisplayAreaOrganizerController()1014 public IDisplayAreaOrganizerController getDisplayAreaOrganizerController() { 1015 enforceTaskPermission("getDisplayAreaOrganizerController()"); 1016 return mDisplayAreaOrganizerController; 1017 } 1018 1019 @Override getTaskFragmentOrganizerController()1020 public ITaskFragmentOrganizerController getTaskFragmentOrganizerController() { 1021 return mTaskFragmentOrganizerController; 1022 } 1023 1024 @VisibleForTesting startSyncWithOrganizer(IWindowContainerTransactionCallback callback)1025 int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) { 1026 int id = mService.mWindowManager.mSyncEngine.startSyncSet(this); 1027 mTransactionCallbacksByPendingSyncId.put(id, callback); 1028 return id; 1029 } 1030 1031 @VisibleForTesting setSyncReady(int id)1032 void setSyncReady(int id) { 1033 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set sync ready, syncId=%d", id); 1034 mService.mWindowManager.mSyncEngine.setReady(id); 1035 } 1036 1037 @VisibleForTesting addToSyncSet(int syncId, WindowContainer wc)1038 void addToSyncSet(int syncId, WindowContainer wc) { 1039 mService.mWindowManager.mSyncEngine.addToSyncSet(syncId, wc); 1040 } 1041 1042 @Override onTransactionReady(int syncId, SurfaceControl.Transaction t)1043 public void onTransactionReady(int syncId, SurfaceControl.Transaction t) { 1044 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId); 1045 final IWindowContainerTransactionCallback callback = 1046 mTransactionCallbacksByPendingSyncId.get(syncId); 1047 1048 try { 1049 callback.onTransactionReady(syncId, t); 1050 } catch (RemoteException e) { 1051 // If there's an exception when trying to send the mergedTransaction to the client, we 1052 // should immediately apply it here so the transactions aren't lost. 1053 t.apply(); 1054 } 1055 1056 mTransactionCallbacksByPendingSyncId.remove(syncId); 1057 } 1058 1059 @Override registerTransitionPlayer(ITransitionPlayer player)1060 public void registerTransitionPlayer(ITransitionPlayer player) { 1061 enforceTaskPermission("registerTransitionPlayer()"); 1062 final int callerPid = Binder.getCallingPid(); 1063 final int callerUid = Binder.getCallingUid(); 1064 final long ident = Binder.clearCallingIdentity(); 1065 try { 1066 synchronized (mGlobalLock) { 1067 final WindowProcessController wpc = 1068 mService.getProcessController(callerPid, callerUid); 1069 IApplicationThread appThread = null; 1070 if (wpc != null) { 1071 appThread = wpc.getThread(); 1072 } 1073 mTransitionController.registerTransitionPlayer(player, appThread); 1074 } 1075 } finally { 1076 Binder.restoreCallingIdentity(ident); 1077 } 1078 } 1079 1080 @Override getTransitionMetricsReporter()1081 public ITransitionMetricsReporter getTransitionMetricsReporter() { 1082 return mTransitionController.mTransitionMetricsReporter; 1083 } 1084 1085 /** Whether the configuration changes are important to report back to an organizer. */ configurationsAreEqualForOrganizer( Configuration newConfig, @Nullable Configuration oldConfig)1086 static boolean configurationsAreEqualForOrganizer( 1087 Configuration newConfig, @Nullable Configuration oldConfig) { 1088 if (oldConfig == null) { 1089 return false; 1090 } 1091 int cfgChanges = newConfig.diff(oldConfig); 1092 final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 1093 ? (int) newConfig.windowConfiguration.diff(oldConfig.windowConfiguration, 1094 true /* compareUndefined */) : 0; 1095 if ((winCfgChanges & CONTROLLABLE_WINDOW_CONFIGS) == 0) { 1096 cfgChanges &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION; 1097 } 1098 return (cfgChanges & CONTROLLABLE_CONFIGS) == 0; 1099 } 1100 enforceTaskPermission(String func)1101 private void enforceTaskPermission(String func) { 1102 mService.enforceTaskPermission(func); 1103 } 1104 enforceTaskPermission(String func, WindowContainerTransaction t)1105 private void enforceTaskPermission(String func, WindowContainerTransaction t) { 1106 if (t == null || t.getTaskFragmentOrganizer() == null) { 1107 enforceTaskPermission(func); 1108 return; 1109 } 1110 1111 // Apps may not have the permission to manage Tasks, but we are allowing apps to manage 1112 // TaskFragments belonging to their own Task. 1113 enforceOperationsAllowedForTaskFragmentOrganizer(func, t); 1114 } 1115 1116 /** 1117 * Makes sure that the transaction only contains operations that are allowed for the 1118 * {@link WindowContainerTransaction#getTaskFragmentOrganizer()}. 1119 */ enforceOperationsAllowedForTaskFragmentOrganizer( String func, WindowContainerTransaction t)1120 private void enforceOperationsAllowedForTaskFragmentOrganizer( 1121 String func, WindowContainerTransaction t) { 1122 final ITaskFragmentOrganizer organizer = t.getTaskFragmentOrganizer(); 1123 1124 // Configuration changes 1125 final Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries = 1126 t.getChanges().entrySet().iterator(); 1127 while (entries.hasNext()) { 1128 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next(); 1129 // Only allow to apply changes to TaskFragment that is created by this organizer. 1130 enforceTaskFragmentOrganized(func, WindowContainer.fromBinder(entry.getKey()), 1131 organizer); 1132 } 1133 1134 // Hierarchy changes 1135 final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); 1136 for (int i = hops.size() - 1; i >= 0; i--) { 1137 final WindowContainerTransaction.HierarchyOp hop = hops.get(i); 1138 final int type = hop.getType(); 1139 // Check for each type of the operations that are allowed for TaskFragmentOrganizer. 1140 switch (type) { 1141 case HIERARCHY_OP_TYPE_REORDER: 1142 case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: 1143 enforceTaskFragmentOrganized(func, 1144 WindowContainer.fromBinder(hop.getContainer()), organizer); 1145 break; 1146 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: 1147 enforceTaskFragmentOrganized(func, 1148 WindowContainer.fromBinder(hop.getContainer()), organizer); 1149 enforceTaskFragmentOrganized(func, 1150 WindowContainer.fromBinder(hop.getAdjacentRoot()), 1151 organizer); 1152 break; 1153 case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: 1154 // We are allowing organizer to create TaskFragment. We will check the 1155 // ownerToken in #createTaskFragment, and trigger error callback if that is not 1156 // valid. 1157 case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: 1158 case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: 1159 case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: 1160 // We are allowing organizer to start/reparent activity to a TaskFragment it 1161 // created, or set two TaskFragments adjacent to each other. Nothing to check 1162 // here because the TaskFragment may not be created yet, but will be created in 1163 // the same transaction. 1164 break; 1165 case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: 1166 enforceTaskFragmentOrganized(func, 1167 WindowContainer.fromBinder(hop.getContainer()), organizer); 1168 if (hop.getNewParent() != null) { 1169 enforceTaskFragmentOrganized(func, 1170 WindowContainer.fromBinder(hop.getNewParent()), 1171 organizer); 1172 } 1173 break; 1174 default: 1175 // Other types of hierarchy changes are not allowed. 1176 String msg = "Permission Denial: " + func + " from pid=" 1177 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 1178 + " trying to apply a hierarchy change that is not allowed for" 1179 + " TaskFragmentOrganizer=" + organizer; 1180 Slog.w(TAG, msg); 1181 throw new SecurityException(msg); 1182 } 1183 } 1184 } 1185 enforceTaskFragmentOrganized(String func, @Nullable WindowContainer wc, ITaskFragmentOrganizer organizer)1186 private void enforceTaskFragmentOrganized(String func, @Nullable WindowContainer wc, 1187 ITaskFragmentOrganizer organizer) { 1188 if (wc == null) { 1189 Slog.e(TAG, "Attempt to operate on window that no longer exists"); 1190 return; 1191 } 1192 1193 final TaskFragment tf = wc.asTaskFragment(); 1194 if (tf == null || !tf.hasTaskFragmentOrganizer(organizer)) { 1195 String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid() 1196 + ", uid=" + Binder.getCallingUid() + " trying to modify window container not" 1197 + " belonging to the TaskFragmentOrganizer=" + organizer; 1198 Slog.w(TAG, msg); 1199 throw new SecurityException(msg); 1200 } 1201 } 1202 createTaskFragment(@onNull TaskFragmentCreationParams creationParams, @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller)1203 void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams, 1204 @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller) { 1205 final ActivityRecord ownerActivity = 1206 ActivityRecord.forTokenLocked(creationParams.getOwnerToken()); 1207 final ITaskFragmentOrganizer organizer = ITaskFragmentOrganizer.Stub.asInterface( 1208 creationParams.getOrganizer().asBinder()); 1209 1210 if (ownerActivity == null || ownerActivity.getTask() == null) { 1211 final Throwable exception = 1212 new IllegalArgumentException("Not allowed to operate with invalid ownerToken"); 1213 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); 1214 return; 1215 } 1216 if (!ownerActivity.isResizeable()) { 1217 final IllegalArgumentException exception = new IllegalArgumentException("Not allowed" 1218 + " to operate with non-resizable owner Activity"); 1219 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); 1220 return; 1221 } 1222 // The ownerActivity has to belong to the same app as the target Task. 1223 if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid() 1224 || ownerActivity.getTask().effectiveUid != caller.mUid) { 1225 final Throwable exception = 1226 new IllegalArgumentException("Not allowed to operate with the ownerToken while " 1227 + "the root activity of the target task belong to the different app"); 1228 sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); 1229 return; 1230 } 1231 final TaskFragment taskFragment = new TaskFragment(mService, 1232 creationParams.getFragmentToken(), true /* createdByOrganizer */); 1233 // Set task fragment organizer immediately, since it might have to be notified about further 1234 // actions. 1235 taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(), 1236 ownerActivity.getUid(), ownerActivity.info.processName); 1237 ownerActivity.getTask().addChild(taskFragment, POSITION_TOP); 1238 taskFragment.setWindowingMode(creationParams.getWindowingMode()); 1239 taskFragment.setBounds(creationParams.getInitialBounds()); 1240 mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment); 1241 } 1242 reparentTaskFragment(@onNull WindowContainer oldParent, @Nullable WindowContainer newParent, @Nullable IBinder errorCallbackToken)1243 void reparentTaskFragment(@NonNull WindowContainer oldParent, 1244 @Nullable WindowContainer newParent, @Nullable IBinder errorCallbackToken) { 1245 WindowContainer parent = newParent; 1246 if (parent == null && oldParent.asTaskFragment() != null) { 1247 parent = oldParent.asTaskFragment().getTask(); 1248 } 1249 if (parent == null) { 1250 final Throwable exception = 1251 new IllegalArgumentException("Not allowed to operate with invalid container"); 1252 sendTaskFragmentOperationFailure(oldParent.asTaskFragment().getTaskFragmentOrganizer(), 1253 errorCallbackToken, exception); 1254 return; 1255 } 1256 while (oldParent.hasChild()) { 1257 oldParent.getChildAt(0).reparent(parent, POSITION_TOP); 1258 } 1259 } 1260 deleteTaskFragment(@onNull TaskFragment taskFragment, @Nullable IBinder errorCallbackToken)1261 private int deleteTaskFragment(@NonNull TaskFragment taskFragment, 1262 @Nullable IBinder errorCallbackToken) { 1263 final int index = mLaunchTaskFragments.indexOfValue(taskFragment); 1264 if (index < 0) { 1265 final Throwable exception = 1266 new IllegalArgumentException("Not allowed to operate with invalid " 1267 + "taskFragment"); 1268 sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(), 1269 errorCallbackToken, exception); 1270 return 0; 1271 } 1272 mLaunchTaskFragments.removeAt(index); 1273 taskFragment.remove(true /* withTransition */, "deleteTaskFragment"); 1274 return TRANSACT_EFFECTS_LIFECYCLE; 1275 } 1276 1277 @Nullable getTaskFragment(IBinder tfToken)1278 TaskFragment getTaskFragment(IBinder tfToken) { 1279 return mLaunchTaskFragments.get(tfToken); 1280 } 1281 1282 static class CallerInfo { 1283 final int mPid; 1284 final int mUid; 1285 CallerInfo()1286 CallerInfo() { 1287 mPid = Binder.getCallingPid(); 1288 mUid = Binder.getCallingUid(); 1289 } 1290 } 1291 sendTaskFragmentOperationFailure(@onNull ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken, @NonNull Throwable exception)1292 void sendTaskFragmentOperationFailure(@NonNull ITaskFragmentOrganizer organizer, 1293 @Nullable IBinder errorCallbackToken, @NonNull Throwable exception) { 1294 if (organizer == null) { 1295 throw new IllegalArgumentException("Not allowed to operate with invalid organizer"); 1296 } 1297 mService.mTaskFragmentOrganizerController 1298 .onTaskFragmentError(organizer, errorCallbackToken, exception); 1299 } 1300 convertStartFailureToThrowable(int result, Intent intent)1301 private Throwable convertStartFailureToThrowable(int result, Intent intent) { 1302 switch (result) { 1303 case ActivityManager.START_INTENT_NOT_RESOLVED: 1304 case ActivityManager.START_CLASS_NOT_FOUND: 1305 return new ActivityNotFoundException("No Activity found to handle " + intent); 1306 case ActivityManager.START_PERMISSION_DENIED: 1307 return new SecurityException("Permission denied and not allowed to start activity " 1308 + intent); 1309 case ActivityManager.START_CANCELED: 1310 return new AndroidRuntimeException("Activity could not be started for " + intent 1311 + " with error code : " + result); 1312 default: 1313 return new AndroidRuntimeException("Start activity failed with error code : " 1314 + result + " when starting " + intent); 1315 } 1316 } 1317 } 1318