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