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.view.Display.DEFAULT_DISPLAY;
22 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
23 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
24 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
25 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK;
26 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
27 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
28 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
29 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
30 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN;
31 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
32 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
33 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
34 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
35 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
36 
37 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
38 import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
39 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
40 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
41 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
42 import static com.android.server.wm.WindowContainer.POSITION_TOP;
43 
44 import android.annotation.NonNull;
45 import android.annotation.Nullable;
46 import android.app.ActivityManager;
47 import android.app.ActivityOptions;
48 import android.app.IApplicationThread;
49 import android.app.WindowConfiguration;
50 import android.content.ActivityNotFoundException;
51 import android.content.Intent;
52 import android.content.pm.ActivityInfo;
53 import android.content.res.Configuration;
54 import android.graphics.Rect;
55 import android.os.Binder;
56 import android.os.Bundle;
57 import android.os.IBinder;
58 import android.os.Parcel;
59 import android.os.RemoteException;
60 import android.util.AndroidRuntimeException;
61 import android.util.ArrayMap;
62 import android.util.ArraySet;
63 import android.util.Slog;
64 import android.view.RemoteAnimationAdapter;
65 import android.view.SurfaceControl;
66 import android.window.IDisplayAreaOrganizerController;
67 import android.window.ITaskFragmentOrganizer;
68 import android.window.ITaskFragmentOrganizerController;
69 import android.window.ITaskOrganizerController;
70 import android.window.ITransitionMetricsReporter;
71 import android.window.ITransitionPlayer;
72 import android.window.IWindowContainerTransactionCallback;
73 import android.window.IWindowOrganizerController;
74 import android.window.TaskFragmentCreationParams;
75 import android.window.WindowContainerTransaction;
76 
77 import com.android.internal.annotations.VisibleForTesting;
78 import com.android.internal.protolog.common.ProtoLog;
79 import com.android.internal.util.ArrayUtils;
80 import com.android.internal.util.function.pooled.PooledConsumer;
81 import com.android.internal.util.function.pooled.PooledLambda;
82 
83 import java.util.ArrayList;
84 import java.util.HashMap;
85 import java.util.Iterator;
86 import java.util.List;
87 import java.util.Map;
88 import java.util.function.Function;
89 
90 /**
91  * Server side implementation for the interface for organizing windows
92  * @see android.window.WindowOrganizer
93  */
94 class WindowOrganizerController extends IWindowOrganizerController.Stub
95     implements BLASTSyncEngine.TransactionReadyListener {
96 
97     private static final String TAG = "WindowOrganizerController";
98 
99     /** Flag indicating that an applied transaction may have effected lifecycle */
100     private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1;
101     private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1;
102 
103     /**
104      * Masks specifying which configurations task-organizers can control. Incoming transactions
105      * will be filtered to only include these.
106      */
107     static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION
108             | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE;
109     static final int CONTROLLABLE_WINDOW_CONFIGS = WindowConfiguration.WINDOW_CONFIG_BOUNDS
110             | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
111 
112     private final ActivityTaskManagerService mService;
113     private final WindowManagerGlobalLock mGlobalLock;
114 
115     private final HashMap<Integer, IWindowContainerTransactionCallback>
116             mTransactionCallbacksByPendingSyncId = new HashMap();
117 
118     final TaskOrganizerController mTaskOrganizerController;
119     final DisplayAreaOrganizerController mDisplayAreaOrganizerController;
120     final TaskFragmentOrganizerController mTaskFragmentOrganizerController;
121 
122     TransitionController mTransitionController;
123     /**
124      * A Map which manages the relationship between
125      * {@link TaskFragmentCreationParams#getFragmentToken()} and {@link TaskFragment}
126      */
127     @VisibleForTesting
128     final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
129 
WindowOrganizerController(ActivityTaskManagerService atm)130     WindowOrganizerController(ActivityTaskManagerService atm) {
131         mService = atm;
132         mGlobalLock = atm.mGlobalLock;
133         mTaskOrganizerController = new TaskOrganizerController(mService);
134         mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
135         mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm);
136     }
137 
setWindowManager(WindowManagerService wms)138     void setWindowManager(WindowManagerService wms) {
139         mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController);
140         mTransitionController.registerLegacyListener(wms.mActivityManagerAppTransitionNotifier);
141     }
142 
getTransitionController()143     TransitionController getTransitionController() {
144         return mTransitionController;
145     }
146 
147     @Override
onTransact(int code, Parcel data, Parcel reply, int flags)148     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
149             throws RemoteException {
150         try {
151             return super.onTransact(code, data, reply, flags);
152         } catch (RuntimeException e) {
153             throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e);
154         }
155     }
156 
157     @Override
applyTransaction(WindowContainerTransaction t)158     public void applyTransaction(WindowContainerTransaction t) {
159         if (t == null) {
160             throw new IllegalArgumentException("Null transaction passed to applyTransaction");
161         }
162         enforceTaskPermission("applyTransaction()", t);
163         final CallerInfo caller = new CallerInfo();
164         final long ident = Binder.clearCallingIdentity();
165         try {
166             synchronized (mGlobalLock) {
167                 applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller);
168             }
169         } finally {
170             Binder.restoreCallingIdentity(ident);
171         }
172     }
173 
174     @Override
applySyncTransaction(WindowContainerTransaction t, IWindowContainerTransactionCallback callback)175     public int applySyncTransaction(WindowContainerTransaction t,
176             IWindowContainerTransactionCallback callback) {
177         if (t == null) {
178             throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
179         }
180         enforceTaskPermission("applySyncTransaction()", t);
181         final CallerInfo caller = new CallerInfo();
182         final long ident = Binder.clearCallingIdentity();
183         try {
184             synchronized (mGlobalLock) {
185                 /**
186                  * If callback is non-null we are looking to synchronize this transaction by
187                  * collecting all the results in to a SurfaceFlinger transaction and then delivering
188                  * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the
189                  * details of the operation. But at a high level we create a sync operation with a
190                  * given ID and an associated callback. Then we notify each WindowContainer in this
191                  * WindowContainer transaction that it is participating in a sync operation with
192                  * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady"
193                  * which means that we have added everything to the set. At any point after this,
194                  * all the WindowContainers will eventually finish applying their changes and notify
195                  * the BLASTSyncEngine which will deliver the Transaction to the callback.
196                  */
197                 int syncId = -1;
198                 if (callback != null) {
199                     syncId = startSyncWithOrganizer(callback);
200                 }
201                 applyTransaction(t, syncId, null /*transition*/, caller);
202                 if (syncId >= 0) {
203                     setSyncReady(syncId);
204                 }
205                 return syncId;
206             }
207         } finally {
208             Binder.restoreCallingIdentity(ident);
209         }
210     }
211 
212     @Override
startTransition(int type, @Nullable IBinder transitionToken, @Nullable WindowContainerTransaction t)213     public IBinder startTransition(int type, @Nullable IBinder transitionToken,
214             @Nullable WindowContainerTransaction t) {
215         enforceTaskPermission("startTransition()");
216         final CallerInfo caller = new CallerInfo();
217         final long ident = Binder.clearCallingIdentity();
218         try {
219             synchronized (mGlobalLock) {
220                 Transition transition = Transition.fromBinder(transitionToken);
221                 // In cases where transition is already provided, the "readiness lifecycle" of the
222                 // transition is determined outside of this transaction. However, if this is a
223                 // direct call from shell, the entire transition lifecycle is contained in the
224                 // provided transaction and thus we can setReady immediately after apply.
225                 boolean needsSetReady = transition == null && t != null;
226                 if (transition == null) {
227                     if (type < 0) {
228                         throw new IllegalArgumentException("Can't create transition with no type");
229                     }
230                     if (mTransitionController.getTransitionPlayer() == null) {
231                         Slog.w(TAG, "Using shell transitions API for legacy transitions.");
232                         if (t == null) {
233                             throw new IllegalArgumentException("Can't use legacy transitions in"
234                                     + " compatibility mode with no WCT.");
235                         }
236                         applyTransaction(t, -1 /* syncId */, null, caller);
237                         return null;
238                     }
239                     transition = mTransitionController.createTransition(type);
240                 }
241                 transition.start();
242                 if (t == null) {
243                     t = new WindowContainerTransaction();
244                 }
245                 applyTransaction(t, -1 /*syncId*/, transition, caller);
246                 if (needsSetReady) {
247                     transition.setAllReady();
248                 }
249                 return transition;
250             }
251         } finally {
252             Binder.restoreCallingIdentity(ident);
253         }
254     }
255 
256     @Override
startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter, @NonNull IWindowContainerTransactionCallback callback, @NonNull WindowContainerTransaction t)257     public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
258             @NonNull IWindowContainerTransactionCallback callback,
259             @NonNull WindowContainerTransaction t) {
260         enforceTaskPermission("startLegacyTransition()");
261         final CallerInfo caller = new CallerInfo();
262         final long ident = Binder.clearCallingIdentity();
263         int syncId;
264         try {
265             synchronized (mGlobalLock) {
266                 if (type < 0) {
267                     throw new IllegalArgumentException("Can't create transition with no type");
268                 }
269                 if (mTransitionController.getTransitionPlayer() != null) {
270                     throw new IllegalArgumentException("Can't use legacy transitions in"
271                             + " when shell transitions are enabled.");
272                 }
273                 final DisplayContent dc =
274                         mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
275                 if (dc.mAppTransition.isTransitionSet()) {
276                     // a transition already exists, so the callback probably won't be called.
277                     return -1;
278                 }
279                 adapter.setCallingPidUid(caller.mPid, caller.mUid);
280                 dc.prepareAppTransition(type);
281                 dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */);
282                 syncId = startSyncWithOrganizer(callback);
283                 applyTransaction(t, syncId, null /* transition */, caller);
284                 setSyncReady(syncId);
285             }
286         } finally {
287             Binder.restoreCallingIdentity(ident);
288         }
289         return syncId;
290     }
291 
292     @Override
finishTransition(@onNull IBinder transitionToken, @Nullable WindowContainerTransaction t, @Nullable IWindowContainerTransactionCallback callback)293     public int finishTransition(@NonNull IBinder transitionToken,
294             @Nullable WindowContainerTransaction t,
295             @Nullable IWindowContainerTransactionCallback callback) {
296         enforceTaskPermission("finishTransition()");
297         final CallerInfo caller = new CallerInfo();
298         final long ident = Binder.clearCallingIdentity();
299         try {
300             synchronized (mGlobalLock) {
301                 int syncId = -1;
302                 if (t != null && callback != null) {
303                     syncId = startSyncWithOrganizer(callback);
304                 }
305                 // apply the incoming transaction before finish in case it alters the visibility
306                 // of the participants.
307                 if (t != null) {
308                     applyTransaction(t, syncId, null /*transition*/, caller);
309                 }
310                 getTransitionController().finishTransition(transitionToken);
311                 if (syncId >= 0) {
312                     setSyncReady(syncId);
313                 }
314                 return syncId;
315             }
316         } finally {
317             Binder.restoreCallingIdentity(ident);
318         }
319     }
320 
321     /**
322      * @param syncId If non-null, this will be a sync-transaction.
323      * @param transition A transition to collect changes into.
324      * @param caller Info about the calling process.
325      */
applyTransaction(@onNull WindowContainerTransaction t, int syncId, @Nullable Transition transition, @NonNull CallerInfo caller)326     private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
327             @Nullable Transition transition, @NonNull CallerInfo caller) {
328         int effects = 0;
329         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
330         mService.deferWindowLayout();
331         mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
332         try {
333             if (transition != null) {
334                 // First check if we have a display rotation transition and if so, update it.
335                 final DisplayContent dc = DisplayRotation.getDisplayFromTransition(transition);
336                 if (dc != null && transition.mChanges.get(dc).hasChanged(dc)) {
337                     // Go through all tasks and collect them before the rotation
338                     // TODO(shell-transitions): move collect() to onConfigurationChange once
339                     //       wallpaper handling is synchronized.
340                     dc.forAllTasks(task -> {
341                         if (task.isVisible()) transition.collect(task);
342                     });
343                     dc.getInsetsStateController().addProvidersToTransition();
344                     dc.sendNewConfiguration();
345                     effects |= TRANSACT_EFFECTS_LIFECYCLE;
346                 }
347             }
348             ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
349             Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
350                     t.getChanges().entrySet().iterator();
351             while (entries.hasNext()) {
352                 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
353                 final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
354                 if (wc == null || !wc.isAttached()) {
355                     Slog.e(TAG, "Attempt to operate on detached container: " + wc);
356                     continue;
357                 }
358                 // Make sure we add to the syncSet before performing
359                 // operations so we don't end up splitting effects between the WM
360                 // pending transaction and the BLASTSync transaction.
361                 if (syncId >= 0) {
362                     addToSyncSet(syncId, wc);
363                 }
364                 if (transition != null) transition.collect(wc);
365 
366                 int containerEffect = applyWindowContainerChange(wc, entry.getValue());
367                 effects |= containerEffect;
368 
369                 // Lifecycle changes will trigger ensureConfig for everything.
370                 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
371                         && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
372                     haveConfigChanges.add(wc);
373                 }
374             }
375             // Hierarchy changes
376             final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
377             final int hopSize = hops.size();
378             if (hopSize > 0) {
379                 final boolean isInLockTaskMode = mService.isInLockTaskMode();
380                 for (int i = 0; i < hopSize; ++i) {
381                     effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
382                             isInLockTaskMode, caller, t.getErrorCallbackToken(),
383                             t.getTaskFragmentOrganizer());
384                 }
385             }
386             // Queue-up bounds-change transactions for tasks which are now organized. Do
387             // this after hierarchy ops so we have the final organized state.
388             entries = t.getChanges().entrySet().iterator();
389             while (entries.hasNext()) {
390                 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
391                 final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
392                 if (wc == null || !wc.isAttached()) {
393                     Slog.e(TAG, "Attempt to operate on detached container: " + wc);
394                     continue;
395                 }
396                 final Task task = wc.asTask();
397                 final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds();
398                 if (task == null || !task.isAttached() || surfaceBounds == null) {
399                     continue;
400                 }
401                 if (!task.isOrganized()) {
402                     final Task parent = task.getParent() != null ? task.getParent().asTask() : null;
403                     // Also allow direct children of created-by-organizer tasks to be
404                     // controlled. In the future, these will become organized anyways.
405                     if (parent == null || !parent.mCreatedByOrganizer) {
406                         throw new IllegalArgumentException(
407                                 "Can't manipulate non-organized task surface " + task);
408                     }
409                 }
410                 final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();
411                 final SurfaceControl sc = task.getSurfaceControl();
412                 sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top);
413                 if (surfaceBounds.isEmpty()) {
414                     sft.setWindowCrop(sc, null);
415                 } else {
416                     sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height());
417                 }
418                 task.setMainWindowSizeChangeTransaction(sft);
419             }
420             if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
421                 mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
422                 // Already calls ensureActivityConfig
423                 mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
424                 mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
425             } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
426                 final PooledConsumer f = PooledLambda.obtainConsumer(
427                         ActivityRecord::ensureActivityConfiguration,
428                         PooledLambda.__(ActivityRecord.class), 0,
429                         true /* preserveWindow */);
430                 try {
431                     for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
432                         haveConfigChanges.valueAt(i).forAllActivities(f);
433                     }
434                 } finally {
435                     f.recycle();
436                 }
437             }
438 
439             if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) == 0) {
440                 mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED);
441             }
442         } finally {
443             mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
444             mService.continueWindowLayout();
445         }
446     }
447 
applyChanges(WindowContainer container, WindowContainerTransaction.Change change)448     private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) {
449         // The "client"-facing API should prevent bad changes; however, just in case, sanitize
450         // masks here.
451         final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS;
452         final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS;
453         int effects = 0;
454         final int windowingMode = change.getWindowingMode();
455         if (configMask != 0) {
456             if (windowingMode > -1 && windowingMode != container.getWindowingMode()) {
457                 // Special handling for when we are setting a windowingMode in the same transaction.
458                 // Setting the windowingMode is going to call onConfigurationChanged so we don't
459                 // need it called right now. Additionally, some logic requires everything in the
460                 // configuration to change at the same time (ie. surface-freezer requires bounds
461                 // and mode to change at the same time).
462                 final Configuration c = container.getRequestedOverrideConfiguration();
463                 c.setTo(change.getConfiguration(), configMask, windowMask);
464             } else {
465                 final Configuration c =
466                         new Configuration(container.getRequestedOverrideConfiguration());
467                 c.setTo(change.getConfiguration(), configMask, windowMask);
468                 container.onRequestedOverrideConfigurationChanged(c);
469             }
470             effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
471             if (windowMask != 0 && container.isEmbedded()) {
472                 // Changing bounds of the embedded TaskFragments may result in lifecycle changes.
473                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
474             }
475         }
476         if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
477             if (container.setFocusable(change.getFocusable())) {
478                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
479             }
480         }
481 
482         if (windowingMode > -1) {
483             if (mService.isInLockTaskMode()
484                     && WindowConfiguration.inMultiWindowMode(windowingMode)) {
485                 throw new UnsupportedOperationException("Not supported to set multi-window"
486                         + " windowing mode during locked task mode.");
487             }
488 
489             final int prevMode = container.getWindowingMode();
490             container.setWindowingMode(windowingMode);
491             if (prevMode != container.getWindowingMode()) {
492                 // The activity in the container may become focusable or non-focusable due to
493                 // windowing modes changes (such as entering or leaving pinned windowing mode),
494                 // so also apply the lifecycle effects to this transaction.
495                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
496             }
497         }
498         return effects;
499     }
500 
applyTaskChanges(Task tr, WindowContainerTransaction.Change c)501     private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) {
502         int effects = 0;
503         final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
504 
505         if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
506             if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
507                 effects = TRANSACT_EFFECTS_LIFECYCLE;
508             }
509         }
510 
511         final int childWindowingMode = c.getActivityWindowingMode();
512         if (childWindowingMode > -1) {
513             tr.setActivityWindowingMode(childWindowingMode);
514         }
515 
516         if (t != null) {
517             tr.setMainWindowSizeChangeTransaction(t);
518         }
519 
520         Rect enterPipBounds = c.getEnterPipBounds();
521         if (enterPipBounds != null) {
522             tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds);
523         }
524 
525         return effects;
526     }
527 
applyDisplayAreaChanges(DisplayArea displayArea, WindowContainerTransaction.Change c)528     private int applyDisplayAreaChanges(DisplayArea displayArea,
529             WindowContainerTransaction.Change c) {
530         final int[] effects = new int[1];
531 
532         if ((c.getChangeMask()
533                 & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
534             if (displayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) {
535                 effects[0] |= TRANSACT_EFFECTS_LIFECYCLE;
536             }
537         }
538 
539         displayArea.forAllTasks(task -> {
540             Task tr = (Task) task;
541             if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
542                 if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
543                     effects[0] |= TRANSACT_EFFECTS_LIFECYCLE;
544                 }
545             }
546         });
547 
548         return effects[0];
549     }
550 
applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects, int syncId, @Nullable Transition transition, boolean isInLockTaskMode, @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer)551     private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
552             int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
553             @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,
554             @Nullable ITaskFragmentOrganizer organizer) {
555         final int type = hop.getType();
556         switch (type) {
557             case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
558                 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
559                 final Task task = wc != null ? wc.asTask() : null;
560                 if (task != null) {
561                     task.getDisplayArea().setLaunchRootTask(task,
562                             hop.getWindowingModes(), hop.getActivityTypes());
563                 } else {
564                     throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc);
565                 }
566                 break;
567             }
568             case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: {
569                 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
570                 final Task task = wc != null ? wc.asTask() : null;
571                 final boolean clearRoot = hop.getToTop();
572                 if (task == null) {
573                     throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc);
574                 } else if (!task.mCreatedByOrganizer) {
575                     throw new UnsupportedOperationException(
576                             "Cannot set non-organized task as adjacent flag root: " + wc);
577                 } else if (task.getAdjacentTaskFragment() == null && !clearRoot) {
578                     throw new UnsupportedOperationException(
579                             "Cannot set non-adjacent task as adjacent flag root: " + wc);
580                 }
581 
582                 task.getDisplayArea().setLaunchAdjacentFlagRootTask(clearRoot ? null : task);
583                 break;
584             }
585             case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: {
586                 effects |= setAdjacentRootsHierarchyOp(hop);
587                 break;
588             }
589             case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: {
590                 final TaskFragmentCreationParams taskFragmentCreationOptions =
591                         hop.getTaskFragmentCreationOptions();
592                 createTaskFragment(taskFragmentCreationOptions, errorCallbackToken, caller);
593                 break;
594             }
595             case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: {
596                 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
597                 if (wc == null || !wc.isAttached()) {
598                     Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
599                     break;
600                 }
601                 final TaskFragment taskFragment = wc.asTaskFragment();
602                 if (taskFragment == null || taskFragment.asTask() != null) {
603                     throw new IllegalArgumentException(
604                             "Can only delete organized TaskFragment, but not Task.");
605                 }
606                 if (isInLockTaskMode) {
607                     final ActivityRecord bottomActivity = taskFragment.getActivity(
608                             a -> !a.finishing, false /* traverseTopToBottom */);
609                     if (bottomActivity != null
610                             && mService.getLockTaskController().activityBlockedFromFinish(
611                                     bottomActivity)) {
612                         Slog.w(TAG, "Skip removing TaskFragment due in lock task mode.");
613                         sendTaskFragmentOperationFailure(organizer, errorCallbackToken,
614                                 new IllegalStateException(
615                                         "Not allow to delete task fragment in lock task mode."));
616                         break;
617                     }
618                 }
619                 effects |= deleteTaskFragment(taskFragment, errorCallbackToken);
620                 break;
621             }
622             case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
623                 final IBinder fragmentToken = hop.getContainer();
624                 if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
625                     final Throwable exception = new IllegalArgumentException(
626                             "Not allowed to operate with invalid fragment token");
627                     sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
628                     break;
629                 }
630                 final Intent activityIntent = hop.getActivityIntent();
631                 final Bundle activityOptions = hop.getLaunchOptions();
632                 final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
633                 final int result = mService.getActivityStartController()
634                         .startActivityInTaskFragment(tf, activityIntent, activityOptions,
635                                 hop.getCallingActivity(), caller.mUid, caller.mPid);
636                 if (!isStartResultSuccessful(result)) {
637                     sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
638                             errorCallbackToken,
639                             convertStartFailureToThrowable(result, activityIntent));
640                 }
641                 break;
642             }
643             case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: {
644                 final IBinder fragmentToken = hop.getNewParent();
645                 final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
646                 if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
647                     final Throwable exception = new IllegalArgumentException(
648                             "Not allowed to operate with invalid fragment token or activity.");
649                     sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
650                     break;
651                 }
652                 activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
653                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
654                 break;
655             }
656             case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: {
657                 final IBinder fragmentToken = hop.getContainer();
658                 final IBinder adjacentFragmentToken = hop.getAdjacentRoot();
659                 final TaskFragment tf1 = mLaunchTaskFragments.get(fragmentToken);
660                 final TaskFragment tf2 = adjacentFragmentToken != null
661                         ? mLaunchTaskFragments.get(adjacentFragmentToken)
662                         : null;
663                 if (tf1 == null || (adjacentFragmentToken != null && tf2 == null)) {
664                     final Throwable exception = new IllegalArgumentException(
665                             "Not allowed to set adjacent on invalid fragment tokens");
666                     sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
667                     break;
668                 }
669                 tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */);
670                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
671 
672                 final Bundle bundle = hop.getLaunchOptions();
673                 final WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams =
674                         bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentParams(
675                                 bundle) : null;
676                 if (adjacentParams == null) {
677                     break;
678                 }
679 
680                 tf1.setDelayLastActivityRemoval(
681                         adjacentParams.shouldDelayPrimaryLastActivityRemoval());
682                 if (tf2 != null) {
683                     tf2.setDelayLastActivityRemoval(
684                             adjacentParams.shouldDelaySecondaryLastActivityRemoval());
685                 }
686                 break;
687             }
688             default: {
689                 // The other operations may change task order so they are skipped while in lock
690                 // task mode. The above operations are still allowed because they don't move
691                 // tasks. And it may be necessary such as clearing launch root after entering
692                 // lock task mode.
693                 if (isInLockTaskMode) {
694                     Slog.w(TAG, "Skip applying hierarchy operation " + hop
695                             + " while in lock task mode");
696                     return effects;
697                 }
698             }
699         }
700 
701         switch (type) {
702             case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: {
703                 effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
704                 break;
705             }
706             case HIERARCHY_OP_TYPE_REORDER:
707             case HIERARCHY_OP_TYPE_REPARENT: {
708                 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
709                 if (wc == null || !wc.isAttached()) {
710                     Slog.e(TAG, "Attempt to operate on detached container: " + wc);
711                     break;
712                 }
713                 if (syncId >= 0) {
714                     addToSyncSet(syncId, wc);
715                 }
716                 if (transition != null) {
717                     transition.collect(wc);
718                     if (hop.isReparent()) {
719                         if (wc.getParent() != null) {
720                             // Collect the current parent. It's visibility may change as
721                             // a result of this reparenting.
722                             transition.collect(wc.getParent());
723                         }
724                         if (hop.getNewParent() != null) {
725                             final WindowContainer parentWc =
726                                     WindowContainer.fromBinder(hop.getNewParent());
727                             if (parentWc == null) {
728                                 Slog.e(TAG, "Can't resolve parent window from token");
729                                 break;
730                             }
731                             transition.collect(parentWc);
732                         }
733                     }
734                 }
735                 effects |= sanitizeAndApplyHierarchyOp(wc, hop);
736                 break;
737             }
738             case HIERARCHY_OP_TYPE_LAUNCH_TASK: {
739                 mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
740                         "launchTask HierarchyOp");
741                 final Bundle launchOpts = hop.getLaunchOptions();
742                 final int taskId = launchOpts.getInt(
743                         WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
744                 launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
745                 final SafeActivityOptions safeOptions =
746                         SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);
747                 final Integer[] starterResult = {null};
748                 // startActivityFromRecents should not be called in lock.
749                 mService.mH.post(() -> {
750                     try {
751                         starterResult[0] = mService.mTaskSupervisor.startActivityFromRecents(
752                                 caller.mPid, caller.mUid, taskId, safeOptions);
753                     } catch (Throwable t) {
754                         starterResult[0] = ActivityManager.START_CANCELED;
755                         Slog.w(TAG, t);
756                     }
757                     synchronized (mGlobalLock) {
758                         mGlobalLock.notifyAll();
759                     }
760                 });
761                 while (starterResult[0] == null) {
762                     try {
763                         mGlobalLock.wait();
764                     } catch (InterruptedException ignored) {
765                     }
766                 }
767                 break;
768             }
769             case HIERARCHY_OP_TYPE_PENDING_INTENT: {
770                 String resolvedType = hop.getActivityIntent() != null
771                         ? hop.getActivityIntent().resolveTypeIfNeeded(
772                         mService.mContext.getContentResolver())
773                         : null;
774 
775                 Bundle options = null;
776                 if (hop.getPendingIntent().isActivity()) {
777                     // Set the context display id as preferred for this activity launches, so that
778                     // it can land on caller's display. Or just brought the task to front at the
779                     // display where it was on since it has higher preference.
780                     ActivityOptions activityOptions = hop.getLaunchOptions() != null
781                             ? new ActivityOptions(hop.getLaunchOptions())
782                             : ActivityOptions.makeBasic();
783                     activityOptions.setCallerDisplayId(DEFAULT_DISPLAY);
784                     options = activityOptions.toBundle();
785                 }
786 
787                 mService.mAmInternal.sendIntentSender(hop.getPendingIntent().getTarget(),
788                         hop.getPendingIntent().getWhitelistToken(), 0 /* code */,
789                         hop.getActivityIntent(), resolvedType, null /* finishReceiver */,
790                         null /* requiredPermission */, options);
791                 break;
792             }
793             case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: {
794                 final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
795                 final WindowContainer newParent = hop.getNewParent() != null
796                         ? WindowContainer.fromBinder(hop.getNewParent())
797                         : null;
798                 if (oldParent == null || !oldParent.isAttached()) {
799                     Slog.e(TAG, "Attempt to operate on unknown or detached container: "
800                             + oldParent);
801                     break;
802                 }
803                 reparentTaskFragment(oldParent, newParent, errorCallbackToken);
804                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
805                 break;
806             }
807         }
808         return effects;
809     }
810 
sanitizeAndApplyHierarchyOp(WindowContainer container, WindowContainerTransaction.HierarchyOp hop)811     private int sanitizeAndApplyHierarchyOp(WindowContainer container,
812             WindowContainerTransaction.HierarchyOp hop) {
813         final Task task = container.asTask();
814         if (task == null) {
815             throw new IllegalArgumentException("Invalid container in hierarchy op");
816         }
817         final DisplayContent dc = task.getDisplayContent();
818         if (dc == null) {
819             Slog.w(TAG, "Container is no longer attached: " + task);
820             return 0;
821         }
822         final Task as = task;
823 
824         if (hop.isReparent()) {
825             final boolean isNonOrganizedRootableTask =
826                     task.isRootTask() || task.getParent().asTask().mCreatedByOrganizer;
827             if (isNonOrganizedRootableTask) {
828                 WindowContainer newParent = hop.getNewParent() == null
829                         ? dc.getDefaultTaskDisplayArea()
830                         : WindowContainer.fromBinder(hop.getNewParent());
831                 if (newParent == null) {
832                     Slog.e(TAG, "Can't resolve parent window from token");
833                     return 0;
834                 }
835                 if (task.getParent() != newParent) {
836                     if (newParent.asTaskDisplayArea() != null) {
837                         // For now, reparenting to displayarea is different from other reparents...
838                         as.reparent(newParent.asTaskDisplayArea(), hop.getToTop());
839                     } else if (newParent.asTask() != null) {
840                         if (newParent.inMultiWindowMode() && task.isLeafTask()) {
841                             if (newParent.inPinnedWindowingMode()) {
842                                 Slog.w(TAG, "Can't support moving a task to another PIP window..."
843                                         + " newParent=" + newParent + " task=" + task);
844                                 return 0;
845                             }
846                             if (!task.supportsMultiWindowInDisplayArea(
847                                     newParent.asTask().getDisplayArea())) {
848                                 Slog.w(TAG, "Can't support task that doesn't support multi-window"
849                                         + " mode in multi-window mode... newParent=" + newParent
850                                         + " task=" + task);
851                                 return 0;
852                             }
853                         }
854                         task.reparent((Task) newParent,
855                                 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
856                                 false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
857                     } else {
858                         throw new RuntimeException("Can only reparent task to another task or"
859                                 + " taskDisplayArea, but not " + newParent);
860                     }
861                 } else {
862                     final Task rootTask = (Task) (
863                             (newParent != null && !(newParent instanceof TaskDisplayArea))
864                                     ? newParent : task.getRootTask());
865                     as.getDisplayArea().positionChildAt(
866                             hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, rootTask,
867                             false /* includingParents */);
868                 }
869             } else {
870                 throw new RuntimeException("Reparenting leaf Tasks is not supported now. " + task);
871             }
872         } else {
873             task.getParent().positionChildAt(
874                     hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
875                     task, false /* includingParents */);
876         }
877         return TRANSACT_EFFECTS_LIFECYCLE;
878     }
879 
reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop, @Nullable Transition transition, int syncId)880     private int reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop,
881             @Nullable Transition transition, int syncId) {
882         WindowContainer currentParent = hop.getContainer() != null
883                 ? WindowContainer.fromBinder(hop.getContainer()) : null;
884         WindowContainer newParent = hop.getNewParent() != null
885                 ? WindowContainer.fromBinder(hop.getNewParent()) : null;
886         if (currentParent == null && newParent == null) {
887             throw new IllegalArgumentException("reparentChildrenTasksHierarchyOp: " + hop);
888         } else if (currentParent == null) {
889             currentParent = newParent.asTask().getDisplayContent().getDefaultTaskDisplayArea();
890         } else if (newParent == null) {
891             newParent = currentParent.asTask().getDisplayContent().getDefaultTaskDisplayArea();
892         }
893 
894         if (currentParent == newParent) {
895             Slog.e(TAG, "reparentChildrenTasksHierarchyOp parent not changing: " + hop);
896             return 0;
897         }
898         if (!currentParent.isAttached()) {
899             Slog.e(TAG, "reparentChildrenTasksHierarchyOp currentParent detached="
900                     + currentParent + " hop=" + hop);
901             return 0;
902         }
903         if (!newParent.isAttached()) {
904             Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent detached="
905                     + newParent + " hop=" + hop);
906             return 0;
907         }
908         if (newParent.inPinnedWindowingMode()) {
909             Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent in PIP="
910                     + newParent + " hop=" + hop);
911             return 0;
912         }
913 
914         final boolean newParentInMultiWindow = newParent.inMultiWindowMode();
915         final TaskDisplayArea newParentTda = newParent.asTask() != null
916                 ? newParent.asTask().getDisplayArea()
917                 : newParent.asTaskDisplayArea();
918         final WindowContainer finalCurrentParent = currentParent;
919         Slog.i(TAG, "reparentChildrenTasksHierarchyOp"
920                 + " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop);
921 
922         // We want to collect the tasks first before re-parenting to avoid array shifting on us.
923         final ArrayList<Task> tasksToReparent = new ArrayList<>();
924 
925         currentParent.forAllTasks((Function<Task, Boolean>) task -> {
926             Slog.i(TAG, " Processing task=" + task);
927             final boolean reparent;
928             if (task.mCreatedByOrganizer || task.getParent() != finalCurrentParent) {
929                 // We only care about non-organized task that are direct children of the thing we
930                 // are reparenting from.
931                 return false;
932             }
933             if (newParentInMultiWindow && !task.supportsMultiWindowInDisplayArea(newParentTda)) {
934                 Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task to multi window,"
935                         + " task=" + task);
936                 return false;
937             }
938             if (!ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType())
939                     || !ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) {
940                 return false;
941             }
942 
943             if (hop.getToTop()) {
944                 tasksToReparent.add(0, task);
945             } else {
946                 tasksToReparent.add(task);
947             }
948             return hop.getReparentTopOnly() && tasksToReparent.size() == 1;
949         });
950 
951         final int count = tasksToReparent.size();
952         for (int i = 0; i < count; ++i) {
953             final Task task = tasksToReparent.get(i);
954             if (syncId >= 0) {
955                 addToSyncSet(syncId, task);
956             }
957             if (transition != null) transition.collect(task);
958 
959             if (newParent instanceof TaskDisplayArea) {
960                 // For now, reparenting to display area is different from other reparents...
961                 task.reparent((TaskDisplayArea) newParent, hop.getToTop());
962             } else {
963                 task.reparent((Task) newParent,
964                         hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
965                         false /*moveParents*/, "processChildrenTaskReparentHierarchyOp");
966             }
967         }
968 
969         if (transition != null) transition.collect(newParent);
970 
971         return TRANSACT_EFFECTS_LIFECYCLE;
972     }
973 
setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop)974     private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
975         final TaskFragment root1 = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment();
976         final TaskFragment root2 =
977                 WindowContainer.fromBinder(hop.getAdjacentRoot()).asTaskFragment();
978         if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) {
979             throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
980                     + " organizer root1=" + root1 + " root2=" + root2);
981         }
982         root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether());
983         return TRANSACT_EFFECTS_LIFECYCLE;
984     }
985 
sanitizeWindowContainer(WindowContainer wc)986     private void sanitizeWindowContainer(WindowContainer wc) {
987         if (!(wc instanceof TaskFragment) && !(wc instanceof DisplayArea)) {
988             throw new RuntimeException("Invalid token in task fragment or displayArea transaction");
989         }
990     }
991 
applyWindowContainerChange(WindowContainer wc, WindowContainerTransaction.Change c)992     private int applyWindowContainerChange(WindowContainer wc,
993             WindowContainerTransaction.Change c) {
994         sanitizeWindowContainer(wc);
995 
996         int effects = applyChanges(wc, c);
997 
998         if (wc instanceof DisplayArea) {
999             effects |= applyDisplayAreaChanges(wc.asDisplayArea(), c);
1000         } else if (wc instanceof Task) {
1001             effects |= applyTaskChanges(wc.asTask(), c);
1002         }
1003 
1004         return effects;
1005     }
1006 
1007     @Override
getTaskOrganizerController()1008     public ITaskOrganizerController getTaskOrganizerController() {
1009         enforceTaskPermission("getTaskOrganizerController()");
1010         return mTaskOrganizerController;
1011     }
1012 
1013     @Override
getDisplayAreaOrganizerController()1014     public IDisplayAreaOrganizerController getDisplayAreaOrganizerController() {
1015         enforceTaskPermission("getDisplayAreaOrganizerController()");
1016         return mDisplayAreaOrganizerController;
1017     }
1018 
1019     @Override
getTaskFragmentOrganizerController()1020     public ITaskFragmentOrganizerController getTaskFragmentOrganizerController() {
1021         return mTaskFragmentOrganizerController;
1022     }
1023 
1024     @VisibleForTesting
startSyncWithOrganizer(IWindowContainerTransactionCallback callback)1025     int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
1026         int id = mService.mWindowManager.mSyncEngine.startSyncSet(this);
1027         mTransactionCallbacksByPendingSyncId.put(id, callback);
1028         return id;
1029     }
1030 
1031     @VisibleForTesting
setSyncReady(int id)1032     void setSyncReady(int id) {
1033         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set sync ready, syncId=%d", id);
1034         mService.mWindowManager.mSyncEngine.setReady(id);
1035     }
1036 
1037     @VisibleForTesting
addToSyncSet(int syncId, WindowContainer wc)1038     void addToSyncSet(int syncId, WindowContainer wc) {
1039         mService.mWindowManager.mSyncEngine.addToSyncSet(syncId, wc);
1040     }
1041 
1042     @Override
onTransactionReady(int syncId, SurfaceControl.Transaction t)1043     public void onTransactionReady(int syncId, SurfaceControl.Transaction t) {
1044         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId);
1045         final IWindowContainerTransactionCallback callback =
1046                 mTransactionCallbacksByPendingSyncId.get(syncId);
1047 
1048         try {
1049             callback.onTransactionReady(syncId, t);
1050         } catch (RemoteException e) {
1051             // If there's an exception when trying to send the mergedTransaction to the client, we
1052             // should immediately apply it here so the transactions aren't lost.
1053             t.apply();
1054         }
1055 
1056         mTransactionCallbacksByPendingSyncId.remove(syncId);
1057     }
1058 
1059     @Override
registerTransitionPlayer(ITransitionPlayer player)1060     public void registerTransitionPlayer(ITransitionPlayer player) {
1061         enforceTaskPermission("registerTransitionPlayer()");
1062         final int callerPid = Binder.getCallingPid();
1063         final int callerUid = Binder.getCallingUid();
1064         final long ident = Binder.clearCallingIdentity();
1065         try {
1066             synchronized (mGlobalLock) {
1067                 final WindowProcessController wpc =
1068                         mService.getProcessController(callerPid, callerUid);
1069                 IApplicationThread appThread = null;
1070                 if (wpc != null) {
1071                     appThread = wpc.getThread();
1072                 }
1073                 mTransitionController.registerTransitionPlayer(player, appThread);
1074             }
1075         } finally {
1076             Binder.restoreCallingIdentity(ident);
1077         }
1078     }
1079 
1080     @Override
getTransitionMetricsReporter()1081     public ITransitionMetricsReporter getTransitionMetricsReporter() {
1082         return mTransitionController.mTransitionMetricsReporter;
1083     }
1084 
1085     /** Whether the configuration changes are important to report back to an organizer. */
configurationsAreEqualForOrganizer( Configuration newConfig, @Nullable Configuration oldConfig)1086     static boolean configurationsAreEqualForOrganizer(
1087             Configuration newConfig, @Nullable Configuration oldConfig) {
1088         if (oldConfig == null) {
1089             return false;
1090         }
1091         int cfgChanges = newConfig.diff(oldConfig);
1092         final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
1093                 ? (int) newConfig.windowConfiguration.diff(oldConfig.windowConfiguration,
1094                 true /* compareUndefined */) : 0;
1095         if ((winCfgChanges & CONTROLLABLE_WINDOW_CONFIGS) == 0) {
1096             cfgChanges &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
1097         }
1098         return (cfgChanges & CONTROLLABLE_CONFIGS) == 0;
1099     }
1100 
enforceTaskPermission(String func)1101     private void enforceTaskPermission(String func) {
1102         mService.enforceTaskPermission(func);
1103     }
1104 
enforceTaskPermission(String func, WindowContainerTransaction t)1105     private void enforceTaskPermission(String func, WindowContainerTransaction t) {
1106         if (t == null || t.getTaskFragmentOrganizer() == null) {
1107             enforceTaskPermission(func);
1108             return;
1109         }
1110 
1111         // Apps may not have the permission to manage Tasks, but we are allowing apps to manage
1112         // TaskFragments belonging to their own Task.
1113         enforceOperationsAllowedForTaskFragmentOrganizer(func, t);
1114     }
1115 
1116     /**
1117      * Makes sure that the transaction only contains operations that are allowed for the
1118      * {@link WindowContainerTransaction#getTaskFragmentOrganizer()}.
1119      */
enforceOperationsAllowedForTaskFragmentOrganizer( String func, WindowContainerTransaction t)1120     private void enforceOperationsAllowedForTaskFragmentOrganizer(
1121             String func, WindowContainerTransaction t) {
1122         final ITaskFragmentOrganizer organizer = t.getTaskFragmentOrganizer();
1123 
1124         // Configuration changes
1125         final Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
1126                 t.getChanges().entrySet().iterator();
1127         while (entries.hasNext()) {
1128             final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
1129             // Only allow to apply changes to TaskFragment that is created by this organizer.
1130             enforceTaskFragmentOrganized(func, WindowContainer.fromBinder(entry.getKey()),
1131                     organizer);
1132         }
1133 
1134         // Hierarchy changes
1135         final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
1136         for (int i = hops.size() - 1; i >= 0; i--) {
1137             final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
1138             final int type = hop.getType();
1139             // Check for each type of the operations that are allowed for TaskFragmentOrganizer.
1140             switch (type) {
1141                 case HIERARCHY_OP_TYPE_REORDER:
1142                 case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
1143                     enforceTaskFragmentOrganized(func,
1144                             WindowContainer.fromBinder(hop.getContainer()), organizer);
1145                     break;
1146                 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
1147                     enforceTaskFragmentOrganized(func,
1148                             WindowContainer.fromBinder(hop.getContainer()), organizer);
1149                     enforceTaskFragmentOrganized(func,
1150                             WindowContainer.fromBinder(hop.getAdjacentRoot()),
1151                             organizer);
1152                     break;
1153                 case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
1154                     // We are allowing organizer to create TaskFragment. We will check the
1155                     // ownerToken in #createTaskFragment, and trigger error callback if that is not
1156                     // valid.
1157                 case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
1158                 case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
1159                 case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
1160                     // We are allowing organizer to start/reparent activity to a TaskFragment it
1161                     // created, or set two TaskFragments adjacent to each other. Nothing to check
1162                     // here because the TaskFragment may not be created yet, but will be created in
1163                     // the same transaction.
1164                     break;
1165                 case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
1166                     enforceTaskFragmentOrganized(func,
1167                             WindowContainer.fromBinder(hop.getContainer()), organizer);
1168                     if (hop.getNewParent() != null) {
1169                         enforceTaskFragmentOrganized(func,
1170                                 WindowContainer.fromBinder(hop.getNewParent()),
1171                                 organizer);
1172                     }
1173                     break;
1174                 default:
1175                     // Other types of hierarchy changes are not allowed.
1176                     String msg = "Permission Denial: " + func + " from pid="
1177                             + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
1178                             + " trying to apply a hierarchy change that is not allowed for"
1179                             + " TaskFragmentOrganizer=" + organizer;
1180                     Slog.w(TAG, msg);
1181                     throw new SecurityException(msg);
1182             }
1183         }
1184     }
1185 
enforceTaskFragmentOrganized(String func, @Nullable WindowContainer wc, ITaskFragmentOrganizer organizer)1186     private void enforceTaskFragmentOrganized(String func, @Nullable WindowContainer wc,
1187             ITaskFragmentOrganizer organizer) {
1188         if (wc == null) {
1189             Slog.e(TAG, "Attempt to operate on window that no longer exists");
1190             return;
1191         }
1192 
1193         final TaskFragment tf = wc.asTaskFragment();
1194         if (tf == null || !tf.hasTaskFragmentOrganizer(organizer)) {
1195             String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
1196                     + ", uid=" + Binder.getCallingUid() + " trying to modify window container not"
1197                     + " belonging to the TaskFragmentOrganizer=" + organizer;
1198             Slog.w(TAG, msg);
1199             throw new SecurityException(msg);
1200         }
1201     }
1202 
createTaskFragment(@onNull TaskFragmentCreationParams creationParams, @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller)1203     void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
1204             @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller) {
1205         final ActivityRecord ownerActivity =
1206                 ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
1207         final ITaskFragmentOrganizer organizer = ITaskFragmentOrganizer.Stub.asInterface(
1208                 creationParams.getOrganizer().asBinder());
1209 
1210         if (ownerActivity == null || ownerActivity.getTask() == null) {
1211             final Throwable exception =
1212                     new IllegalArgumentException("Not allowed to operate with invalid ownerToken");
1213             sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
1214             return;
1215         }
1216         if (!ownerActivity.isResizeable()) {
1217             final IllegalArgumentException exception = new IllegalArgumentException("Not allowed"
1218                     + " to operate with non-resizable owner Activity");
1219             sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
1220             return;
1221         }
1222         // The ownerActivity has to belong to the same app as the target Task.
1223         if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
1224                 || ownerActivity.getTask().effectiveUid != caller.mUid) {
1225             final Throwable exception =
1226                     new IllegalArgumentException("Not allowed to operate with the ownerToken while "
1227                             + "the root activity of the target task belong to the different app");
1228             sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
1229             return;
1230         }
1231         final TaskFragment taskFragment = new TaskFragment(mService,
1232                 creationParams.getFragmentToken(), true /* createdByOrganizer */);
1233         // Set task fragment organizer immediately, since it might have to be notified about further
1234         // actions.
1235         taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(),
1236                 ownerActivity.getUid(), ownerActivity.info.processName);
1237         ownerActivity.getTask().addChild(taskFragment, POSITION_TOP);
1238         taskFragment.setWindowingMode(creationParams.getWindowingMode());
1239         taskFragment.setBounds(creationParams.getInitialBounds());
1240         mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
1241     }
1242 
reparentTaskFragment(@onNull WindowContainer oldParent, @Nullable WindowContainer newParent, @Nullable IBinder errorCallbackToken)1243     void reparentTaskFragment(@NonNull WindowContainer oldParent,
1244             @Nullable WindowContainer newParent,  @Nullable IBinder errorCallbackToken) {
1245         WindowContainer parent = newParent;
1246         if (parent == null && oldParent.asTaskFragment() != null) {
1247             parent = oldParent.asTaskFragment().getTask();
1248         }
1249         if (parent == null) {
1250             final Throwable exception =
1251                     new IllegalArgumentException("Not allowed to operate with invalid container");
1252             sendTaskFragmentOperationFailure(oldParent.asTaskFragment().getTaskFragmentOrganizer(),
1253                     errorCallbackToken, exception);
1254             return;
1255         }
1256         while (oldParent.hasChild()) {
1257             oldParent.getChildAt(0).reparent(parent, POSITION_TOP);
1258         }
1259     }
1260 
deleteTaskFragment(@onNull TaskFragment taskFragment, @Nullable IBinder errorCallbackToken)1261     private int deleteTaskFragment(@NonNull TaskFragment taskFragment,
1262             @Nullable IBinder errorCallbackToken) {
1263         final int index = mLaunchTaskFragments.indexOfValue(taskFragment);
1264         if (index < 0) {
1265             final Throwable exception =
1266                     new IllegalArgumentException("Not allowed to operate with invalid "
1267                             + "taskFragment");
1268             sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(),
1269                     errorCallbackToken, exception);
1270             return 0;
1271         }
1272         mLaunchTaskFragments.removeAt(index);
1273         taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
1274         return TRANSACT_EFFECTS_LIFECYCLE;
1275     }
1276 
1277     @Nullable
getTaskFragment(IBinder tfToken)1278     TaskFragment getTaskFragment(IBinder tfToken) {
1279         return mLaunchTaskFragments.get(tfToken);
1280     }
1281 
1282     static class CallerInfo {
1283         final int mPid;
1284         final int mUid;
1285 
CallerInfo()1286         CallerInfo() {
1287             mPid = Binder.getCallingPid();
1288             mUid = Binder.getCallingUid();
1289         }
1290     }
1291 
sendTaskFragmentOperationFailure(@onNull ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken, @NonNull Throwable exception)1292     void sendTaskFragmentOperationFailure(@NonNull ITaskFragmentOrganizer organizer,
1293             @Nullable IBinder errorCallbackToken, @NonNull Throwable exception) {
1294         if (organizer == null) {
1295             throw new IllegalArgumentException("Not allowed to operate with invalid organizer");
1296         }
1297         mService.mTaskFragmentOrganizerController
1298                 .onTaskFragmentError(organizer, errorCallbackToken, exception);
1299     }
1300 
convertStartFailureToThrowable(int result, Intent intent)1301     private Throwable convertStartFailureToThrowable(int result, Intent intent) {
1302         switch (result) {
1303             case ActivityManager.START_INTENT_NOT_RESOLVED:
1304             case ActivityManager.START_CLASS_NOT_FOUND:
1305                 return new ActivityNotFoundException("No Activity found to handle " + intent);
1306             case ActivityManager.START_PERMISSION_DENIED:
1307                 return new SecurityException("Permission denied and not allowed to start activity "
1308                         + intent);
1309             case ActivityManager.START_CANCELED:
1310                 return new AndroidRuntimeException("Activity could not be started for " + intent
1311                         + " with error code : " + result);
1312             default:
1313                 return new AndroidRuntimeException("Start activity failed with error code : "
1314                         + result + " when starting " + intent);
1315         }
1316     }
1317 }
1318