blob: 707a7898f8b6c56b356473feb8b4c9943de4b86d [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;
chaviw5ef1e932020-04-21 12:55:37 -070020import static android.Manifest.permission.READ_FRAME_BUFFER;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070021
22import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
chaviw4412e232020-04-21 17:27:23 -070023import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070024import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
25import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
26import static com.android.server.wm.WindowContainer.POSITION_TOP;
27
28import android.app.WindowConfiguration;
29import android.content.pm.ActivityInfo;
30import android.content.res.Configuration;
chaviw5ef1e932020-04-21 12:55:37 -070031import android.graphics.PixelFormat;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070032import android.graphics.Rect;
33import android.os.Binder;
34import android.os.IBinder;
35import android.os.RemoteException;
36import android.util.ArraySet;
37import android.util.Slog;
chaviw5ef1e932020-04-21 12:55:37 -070038import android.view.Surface;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070039import android.view.SurfaceControl;
Wale Ogunwaledec34082020-03-22 09:45:00 -070040import android.window.IDisplayAreaOrganizerController;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070041import android.window.ITaskOrganizerController;
42import android.window.IWindowContainerTransactionCallback;
43import android.window.IWindowOrganizerController;
chaviw5ef1e932020-04-21 12:55:37 -070044import android.window.WindowContainerToken;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070045import android.window.WindowContainerTransaction;
46
47import com.android.internal.util.function.pooled.PooledConsumer;
48import com.android.internal.util.function.pooled.PooledLambda;
49
50import java.util.HashMap;
51import java.util.Iterator;
52import java.util.List;
53import java.util.Map;
54
55/**
56 * Server side implementation for the interface for organizing windows
57 * @see android.window.WindowOrganizer
58 */
59class WindowOrganizerController extends IWindowOrganizerController.Stub
60 implements BLASTSyncEngine.TransactionReadyListener {
61
62 private static final String TAG = "WindowOrganizerController";
63
64 /** Flag indicating that an applied transaction may have effected lifecycle */
65 private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1;
66 private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1;
67
68 /**
69 * Masks specifying which configurations task-organizers can control. Incoming transactions
70 * will be filtered to only include these.
71 */
72 static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION
73 | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE;
74 static final int CONTROLLABLE_WINDOW_CONFIGS = WindowConfiguration.WINDOW_CONFIG_BOUNDS
75 | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
76
77 private final ActivityTaskManagerService mService;
78 private final WindowManagerGlobalLock mGlobalLock;
79
80 private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
81 private final HashMap<Integer, IWindowContainerTransactionCallback>
82 mTransactionCallbacksByPendingSyncId = new HashMap();
83
84 final TaskOrganizerController mTaskOrganizerController;
Wale Ogunwaledec34082020-03-22 09:45:00 -070085 final DisplayAreaOrganizerController mDisplayAreaOrganizerController;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070086
87 WindowOrganizerController(ActivityTaskManagerService atm) {
88 mService = atm;
89 mGlobalLock = atm.mGlobalLock;
90 mTaskOrganizerController = new TaskOrganizerController(mService);
Wale Ogunwaledec34082020-03-22 09:45:00 -070091 mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
Wale Ogunwale568f9f412020-03-21 22:27:35 -070092 }
93
94 @Override
95 public void applyTransaction(WindowContainerTransaction t) {
96 applySyncTransaction(t, null /*callback*/);
97 }
98
99 @Override
100 public int applySyncTransaction(WindowContainerTransaction t,
101 IWindowContainerTransactionCallback callback) {
102 enforceStackPermission("applySyncTransaction()");
103 int syncId = -1;
104 if (t == null) {
105 throw new IllegalArgumentException(
106 "Null transaction passed to applySyncTransaction");
107 }
108 long ident = Binder.clearCallingIdentity();
109 try {
110 synchronized (mGlobalLock) {
111 int effects = 0;
112
113 /**
114 * If callback is non-null we are looking to synchronize this transaction by
115 * collecting all the results in to a SurfaceFlinger transaction and then delivering
116 * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the
117 * details of the operation. But at a high level we create a sync operation with a
118 * given ID and an associated callback. Then we notify each WindowContainer in this
119 * WindowContainer transaction that it is participating in a sync operation with
120 * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady"
121 * which means that we have added everything to the set. At any point after this,
122 * all the WindowContainers will eventually finish applying their changes and notify
123 * the BLASTSyncEngine which will deliver the Transaction to the callback.
124 */
125 if (callback != null) {
126 syncId = startSyncWithOrganizer(callback);
127 }
128 mService.deferWindowLayout();
129 try {
130 ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
131 Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
132 t.getChanges().entrySet().iterator();
133 while (entries.hasNext()) {
134 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
135 entries.next();
136 final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
Evan Rosky688c8382020-04-03 17:27:08 -0700137 if (!wc.isAttached()) {
138 Slog.e(TAG, "Attempt to operate on detached container: " + wc);
139 continue;
140 }
Robert Carr8e653c82020-05-13 13:25:51 -0700141 // Make sure we add to the syncSet before performing
142 // operations so we don't end up splitting effects between the WM
143 // pending transaction and the BLASTSync transaction.
144 if (syncId >= 0) {
145 mBLASTSyncEngine.addToSyncSet(syncId, wc);
146 }
147
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700148 int containerEffect = applyWindowContainerChange(wc, entry.getValue());
149 effects |= containerEffect;
150
151 // Lifecycle changes will trigger ensureConfig for everything.
152 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
153 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
154 haveConfigChanges.add(wc);
155 }
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700156 }
157 // Hierarchy changes
158 final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
159 for (int i = 0, n = hops.size(); i < n; ++i) {
160 final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
161 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
Evan Rosky688c8382020-04-03 17:27:08 -0700162 if (!wc.isAttached()) {
163 Slog.e(TAG, "Attempt to operate on detached container: " + wc);
164 continue;
165 }
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700166 effects |= sanitizeAndApplyHierarchyOp(wc, hop);
167 }
168 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
169 // Already calls ensureActivityConfig
170 mService.mRootWindowContainer.ensureActivitiesVisible(
171 null, 0, PRESERVE_WINDOWS);
172 } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
173 final PooledConsumer f = PooledLambda.obtainConsumer(
174 ActivityRecord::ensureActivityConfiguration,
175 PooledLambda.__(ActivityRecord.class), 0,
Vishnu Nair40b4afb2020-04-10 08:55:25 -0700176 true /* preserveWindow */);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700177 try {
178 for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
Louis Changa009c762020-02-26 11:21:31 +0800179 haveConfigChanges.valueAt(i).forAllActivities(f);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700180 }
181 } finally {
182 f.recycle();
183 }
184 }
chaviw4412e232020-04-21 17:27:23 -0700185
186 if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) == 0) {
187 mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED);
188 }
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700189 } finally {
190 mService.continueWindowLayout();
191 if (syncId >= 0) {
192 setSyncReady(syncId);
193 }
194 }
195 }
196 } finally {
197 Binder.restoreCallingIdentity(ident);
198 }
199 return syncId;
200 }
201
chaviw6630d852020-04-15 19:06:56 -0700202 private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) {
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700203 // The "client"-facing API should prevent bad changes; however, just in case, sanitize
204 // masks here.
205 final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS;
206 final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS;
207 int effects = 0;
208 if (configMask != 0) {
209 Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
210 c.setTo(change.getConfiguration(), configMask, windowMask);
211 container.onRequestedOverrideConfigurationChanged(c);
212 // TODO(b/145675353): remove the following once we could apply new bounds to the
213 // pinned stack together with its children.
214 resizePinnedStackIfNeeded(container, configMask, windowMask, c);
215 effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
216 }
217 if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
218 if (container.setFocusable(change.getFocusable())) {
219 effects |= TRANSACT_EFFECTS_LIFECYCLE;
220 }
221 }
chaviw6630d852020-04-15 19:06:56 -0700222
223 final int windowingMode = change.getWindowingMode();
224 if (windowingMode > -1) {
225 container.setWindowingMode(windowingMode);
226 }
227 return effects;
228 }
229
230 private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) {
231 int effects = 0;
232 final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
233
234 if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
235 if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
236 effects = TRANSACT_EFFECTS_LIFECYCLE;
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700237 }
238 }
chaviw6630d852020-04-15 19:06:56 -0700239
240 final int childWindowingMode = c.getActivityWindowingMode();
241 if (childWindowingMode > -1) {
242 tr.setActivityWindowingMode(childWindowingMode);
243 }
244
245 if (t != null) {
246 tr.setMainWindowSizeChangeTransaction(t);
247 }
248
249 Rect enterPipBounds = c.getEnterPipBounds();
250 if (enterPipBounds != null) {
251 mService.mStackSupervisor.updatePictureInPictureMode(tr, enterPipBounds, true);
252 }
253
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700254 return effects;
255 }
256
257 private int sanitizeAndApplyHierarchyOp(WindowContainer container,
258 WindowContainerTransaction.HierarchyOp hop) {
Louis Changa009c762020-02-26 11:21:31 +0800259 final Task task = container.asTask();
260 if (task == null) {
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700261 throw new IllegalArgumentException("Invalid container in hierarchy op");
262 }
Louis Changa009c762020-02-26 11:21:31 +0800263 final DisplayContent dc = task.getDisplayContent();
264 if (dc == null) {
265 Slog.w(TAG, "Container is no longer attached: " + task);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700266 return 0;
267 }
Louis Changa009c762020-02-26 11:21:31 +0800268 final ActivityStack as = (ActivityStack) task;
269
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700270 if (hop.isReparent()) {
Louis Changa009c762020-02-26 11:21:31 +0800271 final boolean isNonOrganizedRootableTask =
272 (task.isRootTask() && !task.mCreatedByOrganizer)
273 || task.getParent().asTask().mCreatedByOrganizer;
274 if (isNonOrganizedRootableTask) {
275 Task newParent = hop.getNewParent() == null ? null
276 : WindowContainer.fromBinder(hop.getNewParent()).asTask();
277 if (task.getParent() != newParent) {
278 if (newParent == null) {
279 // Re-parent task to display as a root task.
Andrii Kulian4c0fd0d2020-03-29 13:32:14 -0700280 as.reparent(dc.getDefaultTaskDisplayArea(), hop.getToTop());
Louis Changa009c762020-02-26 11:21:31 +0800281 } else if (newParent.inMultiWindowMode() && !task.isResizeable()
282 && task.isLeafTask()) {
283 Slog.w(TAG, "Can't support task that doesn't support multi-window mode in"
284 + " multi-window mode... newParent=" + newParent + " task=" + task);
285 return 0;
286 } else {
Louis Changa009c762020-02-26 11:21:31 +0800287 task.reparent((ActivityStack) newParent,
288 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
289 false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700290 }
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700291 } else {
Louis Changa009c762020-02-26 11:21:31 +0800292 final ActivityStack rootTask =
293 (ActivityStack) (newParent != null ? newParent : task.getRootTask());
294 if (hop.getToTop()) {
Andrii Kulian86d676c2020-03-27 19:34:54 -0700295 as.getDisplayArea().positionStackAtTop(rootTask,
Andrii Kulian9ea12da2020-03-27 17:16:38 -0700296 false /* includingParents */);
Louis Changa009c762020-02-26 11:21:31 +0800297 } else {
Andrii Kulian86d676c2020-03-27 19:34:54 -0700298 as.getDisplayArea().positionStackAtBottom(rootTask);
Louis Changa009c762020-02-26 11:21:31 +0800299 }
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700300 }
Louis Changa009c762020-02-26 11:21:31 +0800301 } else {
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700302 throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
303 }
304 } else {
305 // Ugh, of course ActivityStack has its own special reorder logic...
Louis Changa009c762020-02-26 11:21:31 +0800306 if (task.isRootTask()) {
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700307 if (hop.getToTop()) {
Andrii Kulian86d676c2020-03-27 19:34:54 -0700308 as.getDisplayArea().positionStackAtTop(as, false /* includingParents */);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700309 } else {
Andrii Kulian86d676c2020-03-27 19:34:54 -0700310 as.getDisplayArea().positionStackAtBottom(as);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700311 }
312 } else {
Louis Changa009c762020-02-26 11:21:31 +0800313 task.getParent().positionChildAt(
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700314 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
Louis Changa009c762020-02-26 11:21:31 +0800315 task, false /* includingParents */);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700316 }
317 }
318 return TRANSACT_EFFECTS_LIFECYCLE;
319 }
320
chaviw6630d852020-04-15 19:06:56 -0700321 private void sanitizeWindowContainer(WindowContainer wc) {
322 if (!(wc instanceof Task) && !(wc instanceof DisplayArea)) {
323 throw new RuntimeException("Invalid token in task or displayArea transaction");
324 }
325 }
326
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700327 private int applyWindowContainerChange(WindowContainer wc,
328 WindowContainerTransaction.Change c) {
chaviw6630d852020-04-15 19:06:56 -0700329 sanitizeWindowContainer(wc);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700330
chaviw6630d852020-04-15 19:06:56 -0700331 int effects = applyChanges(wc, c);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700332
chaviw6630d852020-04-15 19:06:56 -0700333 if (wc instanceof Task) {
334 effects |= applyTaskChanges(wc.asTask(), c);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700335 }
336
337 return effects;
338 }
339
340 private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
341 int windowMask, Configuration config) {
342 if ((container instanceof ActivityStack)
343 && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
344 && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
345 final ActivityStack stack = (ActivityStack) container;
346 if (stack.inPinnedWindowingMode()) {
347 stack.resize(config.windowConfiguration.getBounds(),
Evan Rosky6ecd67c2020-04-14 11:50:43 -0700348 PRESERVE_WINDOWS, true /* deferResume */);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700349 }
350 }
351 }
352
353 @Override
354 public ITaskOrganizerController getTaskOrganizerController() {
355 enforceStackPermission("getTaskOrganizerController()");
356 return mTaskOrganizerController;
357 }
358
Wale Ogunwaledec34082020-03-22 09:45:00 -0700359 @Override
360 public IDisplayAreaOrganizerController getDisplayAreaOrganizerController() {
361 enforceStackPermission("getDisplayAreaOrganizerController()");
362 return mDisplayAreaOrganizerController;
363 }
364
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700365 int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
366 int id = mBLASTSyncEngine.startSyncSet(this);
367 mTransactionCallbacksByPendingSyncId.put(id, callback);
368 return id;
369 }
370
371 void setSyncReady(int id) {
372 mBLASTSyncEngine.setReady(id);
373 }
374
375 @Override
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700376 public void onTransactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) {
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700377 final IWindowContainerTransactionCallback callback =
378 mTransactionCallbacksByPendingSyncId.get(mSyncId);
379
380 try {
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700381 callback.onTransactionReady(mSyncId, mergedTransaction);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700382 } catch (RemoteException e) {
383 }
384
385 mTransactionCallbacksByPendingSyncId.remove(mSyncId);
386 }
387
chaviw5ef1e932020-04-21 12:55:37 -0700388 @Override
389 public boolean takeScreenshot(WindowContainerToken token, SurfaceControl outSurfaceControl) {
390 mService.mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeScreenshot()");
391 final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
392 if (wc == null) {
393 throw new RuntimeException("Invalid token in screenshot transaction");
394 }
395
396 final Rect bounds = new Rect();
397 wc.getBounds(bounds);
398 bounds.offsetTo(0, 0);
399 SurfaceControl.ScreenshotGraphicBuffer buffer = SurfaceControl.captureLayers(
400 wc.getSurfaceControl(), bounds, 1);
401
402 if (buffer == null || buffer.getGraphicBuffer() == null) {
403 return false;
404 }
405
406 SurfaceControl screenshot = mService.mWindowManager.mSurfaceControlFactory.apply(null)
407 .setName(wc.getName() + " - Organizer Screenshot")
408 .setBufferSize(bounds.width(), bounds.height())
409 .setFormat(PixelFormat.TRANSLUCENT)
410 .setParent(wc.getParentSurfaceControl())
411 .build();
412
413 Surface surface = new Surface();
414 surface.copyFrom(screenshot);
415 surface.attachAndQueueBufferWithColorSpace(buffer.getGraphicBuffer(), null);
416 surface.release();
417
418 outSurfaceControl.copyFrom(screenshot);
419 return true;
420 }
421
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700422 private void enforceStackPermission(String func) {
423 mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
424 }
425}