blob: e416e8073a75d268a2f104cc7d675fabca1702b9 [file] [log] [blame]
Wale Ogunwale568f9f412020-03-21 22:27:35 -07001/*
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
17package com.android.server.wm;
18
19import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
20
21import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
22import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
23import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
24import static com.android.server.wm.WindowContainer.POSITION_TOP;
25
26import android.app.WindowConfiguration;
27import android.content.pm.ActivityInfo;
28import android.content.res.Configuration;
29import android.graphics.Rect;
30import android.os.Binder;
31import android.os.IBinder;
32import android.os.RemoteException;
33import android.util.ArraySet;
34import android.util.Slog;
35import android.view.SurfaceControl;
Wale Ogunwaledec34082020-03-22 09:45:00 -070036import android.window.IDisplayAreaOrganizerController;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070037import android.window.ITaskOrganizerController;
38import android.window.IWindowContainerTransactionCallback;
39import android.window.IWindowOrganizerController;
40import android.window.WindowContainerTransaction;
41
42import com.android.internal.util.function.pooled.PooledConsumer;
43import com.android.internal.util.function.pooled.PooledLambda;
44
45import java.util.HashMap;
46import java.util.Iterator;
47import java.util.List;
48import java.util.Map;
49
50/**
51 * Server side implementation for the interface for organizing windows
52 * @see android.window.WindowOrganizer
53 */
54class WindowOrganizerController extends IWindowOrganizerController.Stub
55 implements BLASTSyncEngine.TransactionReadyListener {
56
57 private static final String TAG = "WindowOrganizerController";
58
59 /** Flag indicating that an applied transaction may have effected lifecycle */
60 private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1;
61 private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1;
62
63 /**
64 * Masks specifying which configurations task-organizers can control. Incoming transactions
65 * will be filtered to only include these.
66 */
67 static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION
68 | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE;
69 static final int CONTROLLABLE_WINDOW_CONFIGS = WindowConfiguration.WINDOW_CONFIG_BOUNDS
70 | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
71
72 private final ActivityTaskManagerService mService;
73 private final WindowManagerGlobalLock mGlobalLock;
74
75 private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
76 private final HashMap<Integer, IWindowContainerTransactionCallback>
77 mTransactionCallbacksByPendingSyncId = new HashMap();
78
79 final TaskOrganizerController mTaskOrganizerController;
Wale Ogunwaledec34082020-03-22 09:45:00 -070080 final DisplayAreaOrganizerController mDisplayAreaOrganizerController;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070081
82 WindowOrganizerController(ActivityTaskManagerService atm) {
83 mService = atm;
84 mGlobalLock = atm.mGlobalLock;
85 mTaskOrganizerController = new TaskOrganizerController(mService);
Wale Ogunwaledec34082020-03-22 09:45:00 -070086 mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
Wale Ogunwale568f9f412020-03-21 22:27:35 -070087 }
88
89 @Override
90 public void applyTransaction(WindowContainerTransaction t) {
91 applySyncTransaction(t, null /*callback*/);
92 }
93
94 @Override
95 public int applySyncTransaction(WindowContainerTransaction t,
96 IWindowContainerTransactionCallback callback) {
97 enforceStackPermission("applySyncTransaction()");
98 int syncId = -1;
99 if (t == null) {
100 throw new IllegalArgumentException(
101 "Null transaction passed to applySyncTransaction");
102 }
103 long ident = Binder.clearCallingIdentity();
104 try {
105 synchronized (mGlobalLock) {
106 int effects = 0;
107
108 /**
109 * If callback is non-null we are looking to synchronize this transaction by
110 * collecting all the results in to a SurfaceFlinger transaction and then delivering
111 * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the
112 * details of the operation. But at a high level we create a sync operation with a
113 * given ID and an associated callback. Then we notify each WindowContainer in this
114 * WindowContainer transaction that it is participating in a sync operation with
115 * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady"
116 * which means that we have added everything to the set. At any point after this,
117 * all the WindowContainers will eventually finish applying their changes and notify
118 * the BLASTSyncEngine which will deliver the Transaction to the callback.
119 */
120 if (callback != null) {
121 syncId = startSyncWithOrganizer(callback);
122 }
123 mService.deferWindowLayout();
124 try {
125 ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
126 Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
127 t.getChanges().entrySet().iterator();
128 while (entries.hasNext()) {
129 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
130 entries.next();
131 final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
132 int containerEffect = applyWindowContainerChange(wc, entry.getValue());
133 effects |= containerEffect;
134
135 // Lifecycle changes will trigger ensureConfig for everything.
136 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
137 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
138 haveConfigChanges.add(wc);
139 }
140 if (syncId >= 0) {
141 mBLASTSyncEngine.addToSyncSet(syncId, wc);
142 }
143 }
144 // Hierarchy changes
145 final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
146 for (int i = 0, n = hops.size(); i < n; ++i) {
147 final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
148 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
149 effects |= sanitizeAndApplyHierarchyOp(wc, hop);
150 }
151 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
152 // Already calls ensureActivityConfig
153 mService.mRootWindowContainer.ensureActivitiesVisible(
154 null, 0, PRESERVE_WINDOWS);
155 } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
156 final PooledConsumer f = PooledLambda.obtainConsumer(
157 ActivityRecord::ensureActivityConfiguration,
158 PooledLambda.__(ActivityRecord.class), 0,
159 false /* preserveWindow */);
160 try {
161 for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
162 final WindowContainer wc = haveConfigChanges.valueAt(i);
163 final Task task = wc.asTask();
164 final TaskTile tile = task != null ? task.asTile() : null;
165 if (tile != null) {
166 // Special case for tile. Can't override normal forAllActivities
167 // because it generates duplicate calls and messes up existing
168 // code-paths.
169 tile.forAllTileActivities(f);
170 } else {
171 wc.forAllActivities(f);
172 }
173 }
174 } finally {
175 f.recycle();
176 }
177 }
178 } finally {
179 mService.continueWindowLayout();
180 if (syncId >= 0) {
181 setSyncReady(syncId);
182 }
183 }
184 }
185 } finally {
186 Binder.restoreCallingIdentity(ident);
187 }
188 return syncId;
189 }
190
191 private int sanitizeAndApplyChange(WindowContainer container,
192 WindowContainerTransaction.Change change) {
193 if (!(container instanceof Task)) {
194 throw new RuntimeException("Invalid token in task transaction");
195 }
196 final Task task = (Task) container;
197 // The "client"-facing API should prevent bad changes; however, just in case, sanitize
198 // masks here.
199 final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS;
200 final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS;
201 int effects = 0;
202 if (configMask != 0) {
203 Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
204 c.setTo(change.getConfiguration(), configMask, windowMask);
205 container.onRequestedOverrideConfigurationChanged(c);
206 // TODO(b/145675353): remove the following once we could apply new bounds to the
207 // pinned stack together with its children.
208 resizePinnedStackIfNeeded(container, configMask, windowMask, c);
209 effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
210 }
211 if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
212 if (container.setFocusable(change.getFocusable())) {
213 effects |= TRANSACT_EFFECTS_LIFECYCLE;
214 }
215 }
216 if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
217 if (task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, change.getHidden())) {
218 effects |= TRANSACT_EFFECTS_LIFECYCLE;
219 }
220 }
221 return effects;
222 }
223
224 private int sanitizeAndApplyHierarchyOp(WindowContainer container,
225 WindowContainerTransaction.HierarchyOp hop) {
226 if (!(container instanceof Task)) {
227 throw new IllegalArgumentException("Invalid container in hierarchy op");
228 }
229 if (container.getDisplayContent() == null) {
230 Slog.w(TAG, "Container is no longer attached: " + container);
231 return 0;
232 }
233 if (hop.isReparent()) {
234 // special case for tiles since they are "virtual" parents
235 if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
236 ActivityStack as = (ActivityStack) container;
237 TaskTile newParent = hop.getNewParent() == null ? null
238 : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
239 if (as.getTile() != newParent) {
240 if (as.getTile() != null) {
241 as.getTile().removeChild(as);
242 }
243 if (newParent != null) {
244 if (!as.affectedBySplitScreenResize()) {
245 return 0;
246 }
247 newParent.addChild(as, POSITION_TOP);
248 }
249 }
250 if (hop.getToTop()) {
251 as.getDisplay().positionStackAtTop(as, false /* includingParents */);
252 } else {
253 as.getDisplay().positionStackAtBottom(as);
254 }
255 } else if (container instanceof Task) {
256 throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
257 }
258 } else {
259 // Ugh, of course ActivityStack has its own special reorder logic...
260 if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
261 ActivityStack as = (ActivityStack) container;
262 if (hop.getToTop()) {
263 as.getDisplay().positionStackAtTop(as, false /* includingParents */);
264 } else {
265 as.getDisplay().positionStackAtBottom(as);
266 }
267 } else {
268 container.getParent().positionChildAt(
269 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
270 container, false /* includingParents */);
271 }
272 }
273 return TRANSACT_EFFECTS_LIFECYCLE;
274 }
275
276 private int applyWindowContainerChange(WindowContainer wc,
277 WindowContainerTransaction.Change c) {
278 int effects = sanitizeAndApplyChange(wc, c);
279
280 final Task tr = wc.asTask();
281
282 final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
283 if (t != null) {
284 tr.setMainWindowSizeChangeTransaction(t);
285 }
286
287 Rect enterPipBounds = c.getEnterPipBounds();
288 if (enterPipBounds != null) {
289 mService.mStackSupervisor.updatePictureInPictureMode(tr,
290 enterPipBounds, true);
291 }
292
293 final int windowingMode = c.getWindowingMode();
294 if (windowingMode > -1) {
295 tr.setWindowingMode(windowingMode);
296 }
297 final int childWindowingMode = c.getActivityWindowingMode();
298 if (childWindowingMode > -1) {
299 tr.setActivityWindowingMode(childWindowingMode);
300 }
301
302 return effects;
303 }
304
305 private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
306 int windowMask, Configuration config) {
307 if ((container instanceof ActivityStack)
308 && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
309 && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
310 final ActivityStack stack = (ActivityStack) container;
311 if (stack.inPinnedWindowingMode()) {
312 stack.resize(config.windowConfiguration.getBounds(),
313 null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */);
314 }
315 }
316 }
317
318 @Override
319 public ITaskOrganizerController getTaskOrganizerController() {
320 enforceStackPermission("getTaskOrganizerController()");
321 return mTaskOrganizerController;
322 }
323
Wale Ogunwaledec34082020-03-22 09:45:00 -0700324 @Override
325 public IDisplayAreaOrganizerController getDisplayAreaOrganizerController() {
326 enforceStackPermission("getDisplayAreaOrganizerController()");
327 return mDisplayAreaOrganizerController;
328 }
329
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700330 int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
331 int id = mBLASTSyncEngine.startSyncSet(this);
332 mTransactionCallbacksByPendingSyncId.put(id, callback);
333 return id;
334 }
335
336 void setSyncReady(int id) {
337 mBLASTSyncEngine.setReady(id);
338 }
339
340 @Override
341 public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) {
342 final IWindowContainerTransactionCallback callback =
343 mTransactionCallbacksByPendingSyncId.get(mSyncId);
344
345 try {
346 callback.transactionReady(mSyncId, mergedTransaction);
347 } catch (RemoteException e) {
348 }
349
350 mTransactionCallbacksByPendingSyncId.remove(mSyncId);
351 }
352
353 private void enforceStackPermission(String func) {
354 mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
355 }
356}