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