blob: ddb67b4d46286792c8d00caa9a982bb5e6c0b9f9 [file] [log] [blame]
Wale Ogunwale1666e312016-12-16 11:27:18 -08001/*
2 * Copyright (C) 2016 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
Wale Ogunwale1666e312016-12-16 11:27:18 -080019import android.content.res.Configuration;
20import android.graphics.Rect;
21import android.os.Handler;
22import android.os.Looper;
23import android.os.Message;
24import android.util.Slog;
25import android.util.SparseArray;
Adrian Roos11c25582018-02-19 18:06:36 +010026import android.view.DisplayCutout;
Winson Chungbdc646f2017-02-13 12:12:22 -080027import android.view.DisplayInfo;
28
Wale Ogunwale1666e312016-12-16 11:27:18 -080029import com.android.internal.annotations.VisibleForTesting;
30
31import java.lang.ref.WeakReference;
Wale Ogunwale1666e312016-12-16 11:27:18 -080032
33import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
34import static com.android.server.wm.WindowContainer.POSITION_TOP;
35import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
36import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
37
38/**
39 * Controller for the stack container. This is created by activity manager to link activity stacks
40 * to the stack container they use in window manager.
41 *
42 * Test class: {@link StackWindowControllerTests}
43 */
44public class StackWindowController
45 extends WindowContainerController<TaskStack, StackWindowListener> {
46
Wale Ogunwale61911492017-10-11 08:50:50 -070047 private final int mStackId;
Wale Ogunwale1666e312016-12-16 11:27:18 -080048
49 private final H mHandler;
50
Winson Chungbdc646f2017-02-13 12:12:22 -080051 // Temp bounds only used in adjustConfigurationForBounds()
52 private final Rect mTmpRect = new Rect();
53 private final Rect mTmpStableInsets = new Rect();
54 private final Rect mTmpNonDecorInsets = new Rect();
55 private final Rect mTmpDisplayBounds = new Rect();
56
Wale Ogunwale034a8ec2017-09-02 17:14:40 -070057 public StackWindowController(int stackId, StackWindowListener listener, int displayId,
58 boolean onTop, Rect outBounds) {
59 this(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
Wale Ogunwale1666e312016-12-16 11:27:18 -080060 }
61
62 @VisibleForTesting
63 public StackWindowController(int stackId, StackWindowListener listener,
Wale Ogunwale034a8ec2017-09-02 17:14:40 -070064 int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
Wale Ogunwale1666e312016-12-16 11:27:18 -080065 super(listener, service);
66 mStackId = stackId;
67 mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
68
69 synchronized (mWindowMap) {
70 final DisplayContent dc = mRoot.getDisplayContent(displayId);
71 if (dc == null) {
72 throw new IllegalArgumentException("Trying to add stackId=" + stackId
73 + " to unknown displayId=" + displayId);
74 }
75
Wale Ogunwale61911492017-10-11 08:50:50 -070076 dc.createStack(stackId, onTop, this);
Wale Ogunwale1666e312016-12-16 11:27:18 -080077 getRawBounds(outBounds);
78 }
79 }
80
81 @Override
82 public void removeContainer() {
83 synchronized (mWindowMap) {
84 if (mContainer != null) {
85 mContainer.removeIfPossible();
86 super.removeContainer();
87 }
88 }
89 }
90
Andrii Kulian51c1b672017-04-07 18:39:32 -070091 public void reparent(int displayId, Rect outStackBounds, boolean onTop) {
Wale Ogunwale1666e312016-12-16 11:27:18 -080092 synchronized (mWindowMap) {
93 if (mContainer == null) {
94 throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId
95 + " to displayId=" + displayId);
96 }
97
98 final DisplayContent targetDc = mRoot.getDisplayContent(displayId);
99 if (targetDc == null) {
100 throw new IllegalArgumentException("Trying to move stackId=" + mStackId
101 + " to unknown displayId=" + displayId);
102 }
103
Andrii Kulian51c1b672017-04-07 18:39:32 -0700104 targetDc.moveStackToDisplay(mContainer, onTop);
Wale Ogunwale1666e312016-12-16 11:27:18 -0800105 getRawBounds(outStackBounds);
106 }
107 }
108
Bryce Leef3c6a472017-11-14 14:53:06 -0800109 public void positionChildAt(TaskWindowContainerController child, int position) {
Wale Ogunwale1666e312016-12-16 11:27:18 -0800110 synchronized (mWindowMap) {
111 if (DEBUG_STACK) Slog.i(TAG_WM, "positionChildAt: positioning task=" + child
112 + " at " + position);
113 if (child.mContainer == null) {
114 if (DEBUG_STACK) Slog.i(TAG_WM,
115 "positionChildAt: could not find task=" + this);
116 return;
117 }
118 if (mContainer == null) {
119 if (DEBUG_STACK) Slog.i(TAG_WM,
120 "positionChildAt: could not find stack for task=" + mContainer);
121 return;
122 }
Bryce Leef3c6a472017-11-14 14:53:06 -0800123 child.mContainer.positionAt(position);
Wale Ogunwale1666e312016-12-16 11:27:18 -0800124 mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
125 }
126 }
127
128 public void positionChildAtTop(TaskWindowContainerController child, boolean includingParents) {
129 if (child == null) {
130 // TODO: Fix the call-points that cause this to happen.
131 return;
132 }
133
134 synchronized(mWindowMap) {
135 final Task childTask = child.mContainer;
136 if (childTask == null) {
137 Slog.e(TAG_WM, "positionChildAtTop: task=" + child + " not found");
138 return;
139 }
140 mContainer.positionChildAt(POSITION_TOP, childTask, includingParents);
141
142 if (mService.mAppTransition.isTransitionSet()) {
143 childTask.setSendingToBottom(false);
144 }
145 mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
146 }
147 }
148
Wale Ogunwale66e16852017-10-19 13:35:52 -0700149 public void positionChildAtBottom(TaskWindowContainerController child,
150 boolean includingParents) {
Wale Ogunwale1666e312016-12-16 11:27:18 -0800151 if (child == null) {
152 // TODO: Fix the call-points that cause this to happen.
153 return;
154 }
155
156 synchronized(mWindowMap) {
157 final Task childTask = child.mContainer;
158 if (childTask == null) {
159 Slog.e(TAG_WM, "positionChildAtBottom: task=" + child + " not found");
160 return;
161 }
Wale Ogunwale66e16852017-10-19 13:35:52 -0700162 mContainer.positionChildAt(POSITION_BOTTOM, childTask, includingParents);
Wale Ogunwale1666e312016-12-16 11:27:18 -0800163
164 if (mService.mAppTransition.isTransitionSet()) {
165 childTask.setSendingToBottom(true);
166 }
167 mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
168 }
169 }
170
171 /**
172 * Re-sizes a stack and its containing tasks.
173 *
174 * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
Wale Ogunwale1666e312016-12-16 11:27:18 -0800175 * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
Bryce Leef3c6a472017-11-14 14:53:06 -0800176 * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id.
Wale Ogunwale1666e312016-12-16 11:27:18 -0800177 */
Bryce Leef3c6a472017-11-14 14:53:06 -0800178 public void resize(Rect bounds, SparseArray<Rect> taskBounds,
179 SparseArray<Rect> taskTempInsetBounds) {
Wale Ogunwale1666e312016-12-16 11:27:18 -0800180 synchronized (mWindowMap) {
181 if (mContainer == null) {
182 throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
183 }
184 // We might trigger a configuration change. Save the current task bounds for freezing.
185 mContainer.prepareFreezingTaskBounds();
Bryce Leef3c6a472017-11-14 14:53:06 -0800186 if (mContainer.setBounds(bounds, taskBounds, taskTempInsetBounds)
Wale Ogunwale1666e312016-12-16 11:27:18 -0800187 && mContainer.isVisible()) {
188 mContainer.getDisplayContent().setLayoutNeeded();
189 mService.mWindowPlacerLocked.performSurfacePlacement();
190 }
Wale Ogunwale1666e312016-12-16 11:27:18 -0800191 }
192 }
193
Matthew Ngaa2b6202017-02-10 14:48:21 -0800194 /**
195 * @see TaskStack.getStackDockedModeBoundsLocked(Rect, Rect, Rect, boolean)
196 */
197 public void getStackDockedModeBounds(Rect currentTempTaskBounds, Rect outStackBounds,
198 Rect outTempTaskBounds, boolean ignoreVisibility) {
Wale Ogunwale1666e312016-12-16 11:27:18 -0800199 synchronized (mWindowMap) {
200 if (mContainer != null) {
Matthew Ngaa2b6202017-02-10 14:48:21 -0800201 mContainer.getStackDockedModeBoundsLocked(currentTempTaskBounds, outStackBounds,
202 outTempTaskBounds, ignoreVisibility);
Wale Ogunwale1666e312016-12-16 11:27:18 -0800203 return;
204 }
Matthew Ngaa2b6202017-02-10 14:48:21 -0800205 outStackBounds.setEmpty();
206 outTempTaskBounds.setEmpty();
Wale Ogunwale1666e312016-12-16 11:27:18 -0800207 }
208 }
209
210 public void prepareFreezingTaskBounds() {
211 synchronized (mWindowMap) {
212 if (mContainer == null) {
213 throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this
214 + " not found.");
215 }
216 mContainer.prepareFreezingTaskBounds();
217 }
218 }
219
Wale Ogunwale30e441d2017-11-09 08:28:45 -0800220 public void getRawBounds(Rect outBounds) {
221 synchronized (mWindowMap) {
Bryce Leef3c6a472017-11-14 14:53:06 -0800222 if (mContainer.matchParentBounds()) {
Wale Ogunwale30e441d2017-11-09 08:28:45 -0800223 outBounds.setEmpty();
224 } else {
225 mContainer.getRawBounds(outBounds);
226 }
Wale Ogunwale1666e312016-12-16 11:27:18 -0800227 }
228 }
229
230 public void getBounds(Rect outBounds) {
231 synchronized (mWindowMap) {
232 if (mContainer != null) {
233 mContainer.getBounds(outBounds);
234 return;
235 }
236 outBounds.setEmpty();
237 }
238 }
239
Matthew Ngaa2b6202017-02-10 14:48:21 -0800240 public void getBoundsForNewConfiguration(Rect outBounds) {
Wale Ogunwale1666e312016-12-16 11:27:18 -0800241 synchronized(mWindowMap) {
Matthew Ngaa2b6202017-02-10 14:48:21 -0800242 mContainer.getBoundsForNewConfiguration(outBounds);
Wale Ogunwale1666e312016-12-16 11:27:18 -0800243 }
244 }
245
Winson Chungbdc646f2017-02-13 12:12:22 -0800246 /**
247 * Adjusts the screen size in dp's for the {@param config} for the given params.
248 */
249 public void adjustConfigurationForBounds(Rect bounds, Rect insetBounds,
250 Rect nonDecorBounds, Rect stableBounds, boolean overrideWidth,
251 boolean overrideHeight, float density, Configuration config,
252 Configuration parentConfig) {
253 synchronized (mWindowMap) {
254 final TaskStack stack = mContainer;
255 final DisplayContent displayContent = stack.getDisplayContent();
256 final DisplayInfo di = displayContent.getDisplayInfo();
Adrian Roos11c25582018-02-19 18:06:36 +0100257 final DisplayCutout displayCutout = di.displayCutout;
Winson Chungbdc646f2017-02-13 12:12:22 -0800258
259 // Get the insets and display bounds
260 mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
Adrian Roos11c25582018-02-19 18:06:36 +0100261 displayCutout, mTmpStableInsets);
Winson Chungbdc646f2017-02-13 12:12:22 -0800262 mService.mPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
Adrian Roos11c25582018-02-19 18:06:36 +0100263 displayCutout, mTmpNonDecorInsets);
Winson Chungbdc646f2017-02-13 12:12:22 -0800264 mTmpDisplayBounds.set(0, 0, di.logicalWidth, di.logicalHeight);
265
266 int width;
267 int height;
Bryce Lee7566d762017-03-30 09:34:15 -0700268
Wale Ogunwale822e5122017-07-26 06:02:24 -0700269 final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
Bryce Lee7566d762017-03-30 09:34:15 -0700270
Bryce Leef3c6a472017-11-14 14:53:06 -0800271 config.windowConfiguration.setBounds(bounds);
Wale Ogunwale822e5122017-07-26 06:02:24 -0700272 config.windowConfiguration.setAppBounds(!bounds.isEmpty() ? bounds : null);
Bryce Lee7566d762017-03-30 09:34:15 -0700273 boolean intersectParentBounds = false;
274
Wale Ogunwale3382ab12017-07-27 08:55:03 -0700275 if (stack.getWindowConfiguration().tasksAreFloating()) {
Winson Chungbdc646f2017-02-13 12:12:22 -0800276 // Floating tasks should not be resized to the screen's bounds.
277
Wale Ogunwale44f036f2017-09-29 05:09:09 -0700278 if (stack.inPinnedWindowingMode()
279 && bounds.width() == mTmpDisplayBounds.width()
280 && bounds.height() == mTmpDisplayBounds.height()) {
Winson Chungbdc646f2017-02-13 12:12:22 -0800281 // If the bounds we are animating is the same as the fullscreen stack
282 // dimensions, then apply the same inset calculations that we normally do for
283 // the fullscreen stack, without intersecting it with the display bounds
284 stableBounds.inset(mTmpStableInsets);
285 nonDecorBounds.inset(mTmpNonDecorInsets);
Andrii Kulian1893a612017-05-04 14:45:09 -0700286 // Move app bounds to zero to apply intersection with parent correctly. They are
287 // used only for evaluating width and height, so it's OK to move them around.
Wale Ogunwale822e5122017-07-26 06:02:24 -0700288 config.windowConfiguration.getAppBounds().offsetTo(0, 0);
Bryce Lee7566d762017-03-30 09:34:15 -0700289 intersectParentBounds = true;
Winson Chungbdc646f2017-02-13 12:12:22 -0800290 }
291 width = (int) (stableBounds.width() / density);
292 height = (int) (stableBounds.height() / density);
293 } else {
294 // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
295 // area, i.e. the screen area without the system bars.
296 // Additionally task dimensions should not be bigger than its parents dimensions.
297 // The non decor inset are areas that could never be removed in Honeycomb. See
298 // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
299 intersectDisplayBoundsExcludeInsets(nonDecorBounds,
300 insetBounds != null ? insetBounds : bounds, mTmpNonDecorInsets,
301 mTmpDisplayBounds, overrideWidth, overrideHeight);
302 intersectDisplayBoundsExcludeInsets(stableBounds,
303 insetBounds != null ? insetBounds : bounds, mTmpStableInsets,
304 mTmpDisplayBounds, overrideWidth, overrideHeight);
305 width = Math.min((int) (stableBounds.width() / density),
306 parentConfig.screenWidthDp);
307 height = Math.min((int) (stableBounds.height() / density),
308 parentConfig.screenHeightDp);
Bryce Lee7566d762017-03-30 09:34:15 -0700309 intersectParentBounds = true;
310 }
311
Wale Ogunwale822e5122017-07-26 06:02:24 -0700312 if (intersectParentBounds && config.windowConfiguration.getAppBounds() != null) {
313 config.windowConfiguration.getAppBounds().intersect(parentAppBounds);
Winson Chungbdc646f2017-02-13 12:12:22 -0800314 }
315
316 config.screenWidthDp = width;
317 config.screenHeightDp = height;
318 config.smallestScreenWidthDp = getSmallestWidthForTaskBounds(
319 insetBounds != null ? insetBounds : bounds, density);
320 }
321 }
322
323 /**
324 * Intersects the specified {@code inOutBounds} with the display frame that excludes the stable
325 * inset areas.
326 *
327 * @param inOutBounds The inOutBounds to subtract the stable inset areas from.
328 */
329 private void intersectDisplayBoundsExcludeInsets(Rect inOutBounds, Rect inInsetBounds,
330 Rect stableInsets, Rect displayBounds, boolean overrideWidth, boolean overrideHeight) {
331 mTmpRect.set(inInsetBounds);
332 mService.intersectDisplayInsetBounds(displayBounds, stableInsets, mTmpRect);
333 int leftInset = mTmpRect.left - inInsetBounds.left;
334 int topInset = mTmpRect.top - inInsetBounds.top;
335 int rightInset = overrideWidth ? 0 : inInsetBounds.right - mTmpRect.right;
336 int bottomInset = overrideHeight ? 0 : inInsetBounds.bottom - mTmpRect.bottom;
337 inOutBounds.inset(leftInset, topInset, rightInset, bottomInset);
338 }
339
340 /**
341 * Calculates the smallest width for a task given the {@param bounds}.
342 *
343 * @return the smallest width to be used in the Configuration, in dips
344 */
345 private int getSmallestWidthForTaskBounds(Rect bounds, float density) {
346 final DisplayContent displayContent = mContainer.getDisplayContent();
347 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
348
349 if (bounds == null || (bounds.width() == displayInfo.logicalWidth &&
350 bounds.height() == displayInfo.logicalHeight)) {
351 // If the bounds are fullscreen, return the value of the fullscreen configuration
352 return displayContent.getConfiguration().smallestScreenWidthDp;
Wale Ogunwale3382ab12017-07-27 08:55:03 -0700353 } else if (mContainer.getWindowConfiguration().tasksAreFloating()) {
Winson Chungbdc646f2017-02-13 12:12:22 -0800354 // For floating tasks, calculate the smallest width from the bounds of the task
355 return (int) (Math.min(bounds.width(), bounds.height()) / density);
356 } else {
357 // Iterating across all screen orientations, and return the minimum of the task
358 // width taking into account that the bounds might change because the snap algorithm
359 // snaps to a different value
360 return displayContent.getDockedDividerController()
361 .getSmallestWidthDpForBounds(bounds);
362 }
363 }
364
Wale Ogunwale1666e312016-12-16 11:27:18 -0800365 void requestResize(Rect bounds) {
366 mHandler.obtainMessage(H.REQUEST_RESIZE, bounds).sendToTarget();
367 }
368
369 @Override
370 public String toString() {
371 return "{StackWindowController stackId=" + mStackId + "}";
372 }
373
374 private static final class H extends Handler {
375
376 static final int REQUEST_RESIZE = 0;
377
378 private final WeakReference<StackWindowController> mController;
379
380 H(WeakReference<StackWindowController> controller, Looper looper) {
381 super(looper);
382 mController = controller;
383 }
384
385 @Override
386 public void handleMessage(Message msg) {
387 final StackWindowController controller = mController.get();
388 final StackWindowListener listener = (controller != null)
389 ? controller.mListener : null;
390 if (listener == null) {
391 return;
392 }
393 switch (msg.what) {
394 case REQUEST_RESIZE:
395 listener.requestResize((Rect) msg.obj);
396 break;
397 }
398 }
399 }
400}