blob: fbc5afadac6ba9e7c7879b117c4017a35ba7bb46 [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
chaviw54521692020-06-12 14:34:30 -070047import com.android.internal.annotations.VisibleForTesting;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070048import com.android.internal.util.function.pooled.PooledConsumer;
49import com.android.internal.util.function.pooled.PooledLambda;
50
51import java.util.HashMap;
52import java.util.Iterator;
53import java.util.List;
54import java.util.Map;
chaviw54521692020-06-12 14:34:30 -070055import java.util.Set;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070056
57/**
58 * Server side implementation for the interface for organizing windows
59 * @see android.window.WindowOrganizer
60 */
61class WindowOrganizerController extends IWindowOrganizerController.Stub
62 implements BLASTSyncEngine.TransactionReadyListener {
63
64 private static final String TAG = "WindowOrganizerController";
65
66 /** Flag indicating that an applied transaction may have effected lifecycle */
67 private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1;
68 private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1;
69
70 /**
71 * Masks specifying which configurations task-organizers can control. Incoming transactions
72 * will be filtered to only include these.
73 */
74 static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION
75 | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE;
76 static final int CONTROLLABLE_WINDOW_CONFIGS = WindowConfiguration.WINDOW_CONFIG_BOUNDS
77 | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
78
79 private final ActivityTaskManagerService mService;
80 private final WindowManagerGlobalLock mGlobalLock;
81
82 private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
83 private final HashMap<Integer, IWindowContainerTransactionCallback>
84 mTransactionCallbacksByPendingSyncId = new HashMap();
85
86 final TaskOrganizerController mTaskOrganizerController;
Wale Ogunwaledec34082020-03-22 09:45:00 -070087 final DisplayAreaOrganizerController mDisplayAreaOrganizerController;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070088
89 WindowOrganizerController(ActivityTaskManagerService atm) {
90 mService = atm;
91 mGlobalLock = atm.mGlobalLock;
92 mTaskOrganizerController = new TaskOrganizerController(mService);
Wale Ogunwaledec34082020-03-22 09:45:00 -070093 mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
Wale Ogunwale568f9f412020-03-21 22:27:35 -070094 }
95
96 @Override
97 public void applyTransaction(WindowContainerTransaction t) {
98 applySyncTransaction(t, null /*callback*/);
99 }
100
101 @Override
102 public int applySyncTransaction(WindowContainerTransaction t,
103 IWindowContainerTransactionCallback callback) {
104 enforceStackPermission("applySyncTransaction()");
105 int syncId = -1;
106 if (t == null) {
107 throw new IllegalArgumentException(
108 "Null transaction passed to applySyncTransaction");
109 }
110 long ident = Binder.clearCallingIdentity();
111 try {
112 synchronized (mGlobalLock) {
113 int effects = 0;
114
115 /**
116 * If callback is non-null we are looking to synchronize this transaction by
117 * collecting all the results in to a SurfaceFlinger transaction and then delivering
118 * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the
119 * details of the operation. But at a high level we create a sync operation with a
120 * given ID and an associated callback. Then we notify each WindowContainer in this
121 * WindowContainer transaction that it is participating in a sync operation with
122 * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady"
123 * which means that we have added everything to the set. At any point after this,
124 * all the WindowContainers will eventually finish applying their changes and notify
125 * the BLASTSyncEngine which will deliver the Transaction to the callback.
126 */
127 if (callback != null) {
128 syncId = startSyncWithOrganizer(callback);
129 }
130 mService.deferWindowLayout();
131 try {
132 ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
133 Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
134 t.getChanges().entrySet().iterator();
135 while (entries.hasNext()) {
136 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
137 entries.next();
138 final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
Evan Rosky688c8382020-04-03 17:27:08 -0700139 if (!wc.isAttached()) {
140 Slog.e(TAG, "Attempt to operate on detached container: " + wc);
141 continue;
142 }
Robert Carr8e653c82020-05-13 13:25:51 -0700143 // Make sure we add to the syncSet before performing
144 // operations so we don't end up splitting effects between the WM
145 // pending transaction and the BLASTSync transaction.
146 if (syncId >= 0) {
chaviw54521692020-06-12 14:34:30 -0700147 addToSyncSet(syncId, wc);
Robert Carr8e653c82020-05-13 13:25:51 -0700148 }
149
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700150 int containerEffect = applyWindowContainerChange(wc, entry.getValue());
151 effects |= containerEffect;
152
153 // Lifecycle changes will trigger ensureConfig for everything.
154 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
155 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
156 haveConfigChanges.add(wc);
157 }
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700158 }
159 // Hierarchy changes
160 final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
161 for (int i = 0, n = hops.size(); i < n; ++i) {
162 final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
163 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
Evan Rosky688c8382020-04-03 17:27:08 -0700164 if (!wc.isAttached()) {
165 Slog.e(TAG, "Attempt to operate on detached container: " + wc);
166 continue;
167 }
Evan Rosky0c86ced2020-05-20 13:38:34 -0700168 if (syncId >= 0) {
chaviw54521692020-06-12 14:34:30 -0700169 addToSyncSet(syncId, wc);
Evan Rosky0c86ced2020-05-20 13:38:34 -0700170 }
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700171 effects |= sanitizeAndApplyHierarchyOp(wc, hop);
172 }
Evan Rosky0c86ced2020-05-20 13:38:34 -0700173 // Queue-up bounds-change transactions for tasks which are now organized. Do
174 // this after hierarchy ops so we have the final organized state.
175 entries = t.getChanges().entrySet().iterator();
176 while (entries.hasNext()) {
177 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
178 entries.next();
179 final Task task = WindowContainer.fromBinder(entry.getKey()).asTask();
180 final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds();
181 if (task == null || !task.isAttached() || surfaceBounds == null) {
182 continue;
183 }
184 if (!task.isOrganized()) {
185 final Task parent =
186 task.getParent() != null ? task.getParent().asTask() : null;
187 // Also allow direct children of created-by-organizer tasks to be
188 // controlled. In the future, these will become organized anyways.
189 if (parent == null || !parent.mCreatedByOrganizer) {
190 throw new IllegalArgumentException(
191 "Can't manipulate non-organized task surface " + task);
192 }
193 }
194 final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();
195 final SurfaceControl sc = task.getSurfaceControl();
196 sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top);
197 if (surfaceBounds.isEmpty()) {
198 sft.setWindowCrop(sc, null);
199 } else {
200 sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height());
201 }
202 task.setMainWindowSizeChangeTransaction(sft);
203 }
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700204 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
205 // Already calls ensureActivityConfig
206 mService.mRootWindowContainer.ensureActivitiesVisible(
207 null, 0, PRESERVE_WINDOWS);
208 } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
209 final PooledConsumer f = PooledLambda.obtainConsumer(
210 ActivityRecord::ensureActivityConfiguration,
211 PooledLambda.__(ActivityRecord.class), 0,
Vishnu Nair40b4afb2020-04-10 08:55:25 -0700212 true /* preserveWindow */);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700213 try {
214 for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
Louis Changa009c762020-02-26 11:21:31 +0800215 haveConfigChanges.valueAt(i).forAllActivities(f);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700216 }
217 } finally {
218 f.recycle();
219 }
220 }
chaviw4412e232020-04-21 17:27:23 -0700221
222 if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) == 0) {
223 mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED);
224 }
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700225 } finally {
226 mService.continueWindowLayout();
227 if (syncId >= 0) {
228 setSyncReady(syncId);
229 }
230 }
231 }
232 } finally {
233 Binder.restoreCallingIdentity(ident);
234 }
235 return syncId;
236 }
237
chaviw6630d852020-04-15 19:06:56 -0700238 private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) {
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700239 // The "client"-facing API should prevent bad changes; however, just in case, sanitize
240 // masks here.
241 final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS;
242 final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS;
243 int effects = 0;
244 if (configMask != 0) {
245 Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
246 c.setTo(change.getConfiguration(), configMask, windowMask);
247 container.onRequestedOverrideConfigurationChanged(c);
248 // TODO(b/145675353): remove the following once we could apply new bounds to the
249 // pinned stack together with its children.
250 resizePinnedStackIfNeeded(container, configMask, windowMask, c);
251 effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
252 }
253 if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
254 if (container.setFocusable(change.getFocusable())) {
255 effects |= TRANSACT_EFFECTS_LIFECYCLE;
256 }
257 }
chaviw6630d852020-04-15 19:06:56 -0700258
259 final int windowingMode = change.getWindowingMode();
260 if (windowingMode > -1) {
261 container.setWindowingMode(windowingMode);
262 }
263 return effects;
264 }
265
266 private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) {
267 int effects = 0;
268 final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
269
270 if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
271 if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
272 effects = TRANSACT_EFFECTS_LIFECYCLE;
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700273 }
274 }
chaviw6630d852020-04-15 19:06:56 -0700275
276 final int childWindowingMode = c.getActivityWindowingMode();
277 if (childWindowingMode > -1) {
278 tr.setActivityWindowingMode(childWindowingMode);
279 }
280
281 if (t != null) {
282 tr.setMainWindowSizeChangeTransaction(t);
283 }
284
285 Rect enterPipBounds = c.getEnterPipBounds();
286 if (enterPipBounds != null) {
287 mService.mStackSupervisor.updatePictureInPictureMode(tr, enterPipBounds, true);
288 }
289
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700290 return effects;
291 }
292
293 private int sanitizeAndApplyHierarchyOp(WindowContainer container,
294 WindowContainerTransaction.HierarchyOp hop) {
Louis Changa009c762020-02-26 11:21:31 +0800295 final Task task = container.asTask();
296 if (task == null) {
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700297 throw new IllegalArgumentException("Invalid container in hierarchy op");
298 }
Louis Changa009c762020-02-26 11:21:31 +0800299 final DisplayContent dc = task.getDisplayContent();
300 if (dc == null) {
301 Slog.w(TAG, "Container is no longer attached: " + task);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700302 return 0;
303 }
Louis Changa009c762020-02-26 11:21:31 +0800304 final ActivityStack as = (ActivityStack) task;
305
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700306 if (hop.isReparent()) {
Louis Changa009c762020-02-26 11:21:31 +0800307 final boolean isNonOrganizedRootableTask =
308 (task.isRootTask() && !task.mCreatedByOrganizer)
309 || task.getParent().asTask().mCreatedByOrganizer;
310 if (isNonOrganizedRootableTask) {
311 Task newParent = hop.getNewParent() == null ? null
312 : WindowContainer.fromBinder(hop.getNewParent()).asTask();
313 if (task.getParent() != newParent) {
314 if (newParent == null) {
315 // Re-parent task to display as a root task.
Andrii Kulian4c0fd0d2020-03-29 13:32:14 -0700316 as.reparent(dc.getDefaultTaskDisplayArea(), hop.getToTop());
Louis Changa009c762020-02-26 11:21:31 +0800317 } else if (newParent.inMultiWindowMode() && !task.isResizeable()
318 && task.isLeafTask()) {
319 Slog.w(TAG, "Can't support task that doesn't support multi-window mode in"
320 + " multi-window mode... newParent=" + newParent + " task=" + task);
321 return 0;
322 } else {
Louis Changa009c762020-02-26 11:21:31 +0800323 task.reparent((ActivityStack) newParent,
324 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
325 false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700326 }
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700327 } else {
Louis Changa009c762020-02-26 11:21:31 +0800328 final ActivityStack rootTask =
329 (ActivityStack) (newParent != null ? newParent : task.getRootTask());
330 if (hop.getToTop()) {
Andrii Kulian86d676c2020-03-27 19:34:54 -0700331 as.getDisplayArea().positionStackAtTop(rootTask,
Andrii Kulian9ea12da2020-03-27 17:16:38 -0700332 false /* includingParents */);
Louis Changa009c762020-02-26 11:21:31 +0800333 } else {
Andrii Kulian86d676c2020-03-27 19:34:54 -0700334 as.getDisplayArea().positionStackAtBottom(rootTask);
Louis Changa009c762020-02-26 11:21:31 +0800335 }
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700336 }
Louis Changa009c762020-02-26 11:21:31 +0800337 } else {
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700338 throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
339 }
340 } else {
341 // Ugh, of course ActivityStack has its own special reorder logic...
Louis Changa009c762020-02-26 11:21:31 +0800342 if (task.isRootTask()) {
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700343 if (hop.getToTop()) {
Andrii Kulian86d676c2020-03-27 19:34:54 -0700344 as.getDisplayArea().positionStackAtTop(as, false /* includingParents */);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700345 } else {
Andrii Kulian86d676c2020-03-27 19:34:54 -0700346 as.getDisplayArea().positionStackAtBottom(as);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700347 }
348 } else {
Louis Changa009c762020-02-26 11:21:31 +0800349 task.getParent().positionChildAt(
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700350 hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
Louis Changa009c762020-02-26 11:21:31 +0800351 task, false /* includingParents */);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700352 }
353 }
354 return TRANSACT_EFFECTS_LIFECYCLE;
355 }
356
chaviw6630d852020-04-15 19:06:56 -0700357 private void sanitizeWindowContainer(WindowContainer wc) {
358 if (!(wc instanceof Task) && !(wc instanceof DisplayArea)) {
359 throw new RuntimeException("Invalid token in task or displayArea transaction");
360 }
361 }
362
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700363 private int applyWindowContainerChange(WindowContainer wc,
364 WindowContainerTransaction.Change c) {
chaviw6630d852020-04-15 19:06:56 -0700365 sanitizeWindowContainer(wc);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700366
chaviw6630d852020-04-15 19:06:56 -0700367 int effects = applyChanges(wc, c);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700368
chaviw6630d852020-04-15 19:06:56 -0700369 if (wc instanceof Task) {
370 effects |= applyTaskChanges(wc.asTask(), c);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700371 }
372
373 return effects;
374 }
375
376 private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
377 int windowMask, Configuration config) {
378 if ((container instanceof ActivityStack)
379 && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
380 && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
381 final ActivityStack stack = (ActivityStack) container;
382 if (stack.inPinnedWindowingMode()) {
383 stack.resize(config.windowConfiguration.getBounds(),
Evan Rosky6ecd67c2020-04-14 11:50:43 -0700384 PRESERVE_WINDOWS, true /* deferResume */);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700385 }
386 }
387 }
388
389 @Override
390 public ITaskOrganizerController getTaskOrganizerController() {
391 enforceStackPermission("getTaskOrganizerController()");
392 return mTaskOrganizerController;
393 }
394
Wale Ogunwaledec34082020-03-22 09:45:00 -0700395 @Override
396 public IDisplayAreaOrganizerController getDisplayAreaOrganizerController() {
397 enforceStackPermission("getDisplayAreaOrganizerController()");
398 return mDisplayAreaOrganizerController;
399 }
400
chaviw54521692020-06-12 14:34:30 -0700401 @VisibleForTesting
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700402 int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
403 int id = mBLASTSyncEngine.startSyncSet(this);
404 mTransactionCallbacksByPendingSyncId.put(id, callback);
405 return id;
406 }
407
chaviw54521692020-06-12 14:34:30 -0700408 @VisibleForTesting
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700409 void setSyncReady(int id) {
410 mBLASTSyncEngine.setReady(id);
411 }
412
chaviw54521692020-06-12 14:34:30 -0700413 @VisibleForTesting
414 void addToSyncSet(int syncId, WindowContainer wc) {
415 mBLASTSyncEngine.addToSyncSet(syncId, wc);
416 }
417
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700418 @Override
chaviw54521692020-06-12 14:34:30 -0700419 public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) {
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700420 final IWindowContainerTransactionCallback callback =
421 mTransactionCallbacksByPendingSyncId.get(mSyncId);
422
chaviw54521692020-06-12 14:34:30 -0700423 SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction();
424 for (WindowContainer container : windowContainersReady) {
425 container.mergeBlastSyncTransaction(mergedTransaction);
426 }
427
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700428 try {
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700429 callback.onTransactionReady(mSyncId, mergedTransaction);
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700430 } catch (RemoteException e) {
431 }
432
433 mTransactionCallbacksByPendingSyncId.remove(mSyncId);
434 }
435
chaviw5ef1e932020-04-21 12:55:37 -0700436 @Override
437 public boolean takeScreenshot(WindowContainerToken token, SurfaceControl outSurfaceControl) {
438 mService.mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeScreenshot()");
439 final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
440 if (wc == null) {
441 throw new RuntimeException("Invalid token in screenshot transaction");
442 }
443
444 final Rect bounds = new Rect();
445 wc.getBounds(bounds);
446 bounds.offsetTo(0, 0);
447 SurfaceControl.ScreenshotGraphicBuffer buffer = SurfaceControl.captureLayers(
448 wc.getSurfaceControl(), bounds, 1);
449
450 if (buffer == null || buffer.getGraphicBuffer() == null) {
451 return false;
452 }
453
454 SurfaceControl screenshot = mService.mWindowManager.mSurfaceControlFactory.apply(null)
455 .setName(wc.getName() + " - Organizer Screenshot")
456 .setBufferSize(bounds.width(), bounds.height())
457 .setFormat(PixelFormat.TRANSLUCENT)
458 .setParent(wc.getParentSurfaceControl())
459 .build();
460
461 Surface surface = new Surface();
462 surface.copyFrom(screenshot);
463 surface.attachAndQueueBufferWithColorSpace(buffer.getGraphicBuffer(), null);
464 surface.release();
465
466 outSurfaceControl.copyFrom(screenshot);
467 return true;
468 }
469
Wale Ogunwale568f9f412020-03-21 22:27:35 -0700470 private void enforceStackPermission(String func) {
471 mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
472 }
473}