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