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