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