blob: 1fd2c0fd19c5e16678db73be2600e8b9f7398578 [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
Evan Roskyb0e38882018-04-25 12:48:54 -070019import android.app.WindowConfiguration;
Wale Ogunwale1666e312016-12-16 11:27:18 -080020import android.content.res.Configuration;
21import android.graphics.Rect;
22import android.os.Handler;
23import android.os.Looper;
24import android.os.Message;
25import android.util.Slog;
26import android.util.SparseArray;
Adrian Roos11c25582018-02-19 18:06:36 +010027import android.view.DisplayCutout;
Winson Chungbdc646f2017-02-13 12:12:22 -080028import android.view.DisplayInfo;
29
Wale Ogunwale1666e312016-12-16 11:27:18 -080030import com.android.internal.annotations.VisibleForTesting;
31
32import java.lang.ref.WeakReference;
Wale Ogunwale1666e312016-12-16 11:27:18 -080033
34import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
35import static com.android.server.wm.WindowContainer.POSITION_TOP;
36import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
37import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
38
39/**
40 * Controller for the stack container. This is created by activity manager to link activity stacks
41 * to the stack container they use in window manager.
42 *
43 * Test class: {@link StackWindowControllerTests}
44 */
45public class StackWindowController
46 extends WindowContainerController<TaskStack, StackWindowListener> {
47
Wale Ogunwale61911492017-10-11 08:50:50 -070048 private final int mStackId;
Wale Ogunwale1666e312016-12-16 11:27:18 -080049
50 private final H mHandler;
51
Winson Chungbdc646f2017-02-13 12:12:22 -080052 // Temp bounds only used in adjustConfigurationForBounds()
53 private final Rect mTmpRect = new Rect();
54 private final Rect mTmpStableInsets = new Rect();
55 private final Rect mTmpNonDecorInsets = new Rect();
56 private final Rect mTmpDisplayBounds = new Rect();
57
Wale Ogunwale034a8ec2017-09-02 17:14:40 -070058 public StackWindowController(int stackId, StackWindowListener listener, int displayId,
59 boolean onTop, Rect outBounds) {
60 this(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
Wale Ogunwale1666e312016-12-16 11:27:18 -080061 }
62
63 @VisibleForTesting
64 public StackWindowController(int stackId, StackWindowListener listener,
Wale Ogunwale034a8ec2017-09-02 17:14:40 -070065 int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
Wale Ogunwale1666e312016-12-16 11:27:18 -080066 super(listener, service);
67 mStackId = stackId;
68 mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
69
70 synchronized (mWindowMap) {
71 final DisplayContent dc = mRoot.getDisplayContent(displayId);
72 if (dc == null) {
73 throw new IllegalArgumentException("Trying to add stackId=" + stackId
74 + " to unknown displayId=" + displayId);
75 }
76
Wale Ogunwale61911492017-10-11 08:50:50 -070077 dc.createStack(stackId, onTop, this);
Wale Ogunwale1666e312016-12-16 11:27:18 -080078 getRawBounds(outBounds);
79 }
80 }
81
82 @Override
83 public void removeContainer() {
84 synchronized (mWindowMap) {
85 if (mContainer != null) {
86 mContainer.removeIfPossible();
87 super.removeContainer();
88 }
89 }
90 }
91
Andrii Kulian51c1b672017-04-07 18:39:32 -070092 public void reparent(int displayId, Rect outStackBounds, boolean onTop) {
Wale Ogunwale1666e312016-12-16 11:27:18 -080093 synchronized (mWindowMap) {
94 if (mContainer == null) {
95 throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId
96 + " to displayId=" + displayId);
97 }
98
99 final DisplayContent targetDc = mRoot.getDisplayContent(displayId);
100 if (targetDc == null) {
101 throw new IllegalArgumentException("Trying to move stackId=" + mStackId
102 + " to unknown displayId=" + displayId);
103 }
104
Andrii Kulian51c1b672017-04-07 18:39:32 -0700105 targetDc.moveStackToDisplay(mContainer, onTop);
Wale Ogunwale1666e312016-12-16 11:27:18 -0800106 getRawBounds(outStackBounds);
107 }
108 }
109
Bryce Leef3c6a472017-11-14 14:53:06 -0800110 public void positionChildAt(TaskWindowContainerController child, int position) {
Wale Ogunwale1666e312016-12-16 11:27:18 -0800111 synchronized (mWindowMap) {
112 if (DEBUG_STACK) Slog.i(TAG_WM, "positionChildAt: positioning task=" + child
113 + " at " + position);
114 if (child.mContainer == null) {
115 if (DEBUG_STACK) Slog.i(TAG_WM,
116 "positionChildAt: could not find task=" + this);
117 return;
118 }
119 if (mContainer == null) {
120 if (DEBUG_STACK) Slog.i(TAG_WM,
121 "positionChildAt: could not find stack for task=" + mContainer);
122 return;
123 }
Bryce Leef3c6a472017-11-14 14:53:06 -0800124 child.mContainer.positionAt(position);
Wale Ogunwale1666e312016-12-16 11:27:18 -0800125 mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
126 }
127 }
128
129 public void positionChildAtTop(TaskWindowContainerController child, boolean includingParents) {
130 if (child == null) {
131 // TODO: Fix the call-points that cause this to happen.
132 return;
133 }
134
135 synchronized(mWindowMap) {
136 final Task childTask = child.mContainer;
137 if (childTask == null) {
138 Slog.e(TAG_WM, "positionChildAtTop: task=" + child + " not found");
139 return;
140 }
141 mContainer.positionChildAt(POSITION_TOP, childTask, includingParents);
142
143 if (mService.mAppTransition.isTransitionSet()) {
144 childTask.setSendingToBottom(false);
145 }
146 mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
147 }
148 }
149
Wale Ogunwale66e16852017-10-19 13:35:52 -0700150 public void positionChildAtBottom(TaskWindowContainerController child,
151 boolean includingParents) {
Wale Ogunwale1666e312016-12-16 11:27:18 -0800152 if (child == null) {
153 // TODO: Fix the call-points that cause this to happen.
154 return;
155 }
156
157 synchronized(mWindowMap) {
158 final Task childTask = child.mContainer;
159 if (childTask == null) {
160 Slog.e(TAG_WM, "positionChildAtBottom: task=" + child + " not found");
161 return;
162 }
Wale Ogunwale66e16852017-10-19 13:35:52 -0700163 mContainer.positionChildAt(POSITION_BOTTOM, childTask, includingParents);
Wale Ogunwale1666e312016-12-16 11:27:18 -0800164
165 if (mService.mAppTransition.isTransitionSet()) {
166 childTask.setSendingToBottom(true);
167 }
168 mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
169 }
170 }
171
172 /**
173 * Re-sizes a stack and its containing tasks.
174 *
175 * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
Wale Ogunwale1666e312016-12-16 11:27:18 -0800176 * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
Bryce Leef3c6a472017-11-14 14:53:06 -0800177 * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id.
Wale Ogunwale1666e312016-12-16 11:27:18 -0800178 */
Bryce Leef3c6a472017-11-14 14:53:06 -0800179 public void resize(Rect bounds, SparseArray<Rect> taskBounds,
180 SparseArray<Rect> taskTempInsetBounds) {
Wale Ogunwale1666e312016-12-16 11:27:18 -0800181 synchronized (mWindowMap) {
182 if (mContainer == null) {
183 throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
184 }
185 // We might trigger a configuration change. Save the current task bounds for freezing.
186 mContainer.prepareFreezingTaskBounds();
Bryce Leef3c6a472017-11-14 14:53:06 -0800187 if (mContainer.setBounds(bounds, taskBounds, taskTempInsetBounds)
Wale Ogunwale1666e312016-12-16 11:27:18 -0800188 && mContainer.isVisible()) {
189 mContainer.getDisplayContent().setLayoutNeeded();
190 mService.mWindowPlacerLocked.performSurfacePlacement();
191 }
Wale Ogunwale1666e312016-12-16 11:27:18 -0800192 }
193 }
194
Winson Chung15036ca2018-05-31 15:51:47 -0700195 public void onPipAnimationEndResize() {
196 synchronized (mService.mWindowMap) {
197 mContainer.onPipAnimationEndResize();
198 }
199 }
200
Matthew Ngaa2b6202017-02-10 14:48:21 -0800201 /**
202 * @see TaskStack.getStackDockedModeBoundsLocked(Rect, Rect, Rect, boolean)
203 */
204 public void getStackDockedModeBounds(Rect currentTempTaskBounds, Rect outStackBounds,
205 Rect outTempTaskBounds, boolean ignoreVisibility) {
Wale Ogunwale1666e312016-12-16 11:27:18 -0800206 synchronized (mWindowMap) {
207 if (mContainer != null) {
Matthew Ngaa2b6202017-02-10 14:48:21 -0800208 mContainer.getStackDockedModeBoundsLocked(currentTempTaskBounds, outStackBounds,
209 outTempTaskBounds, ignoreVisibility);
Wale Ogunwale1666e312016-12-16 11:27:18 -0800210 return;
211 }
Matthew Ngaa2b6202017-02-10 14:48:21 -0800212 outStackBounds.setEmpty();
213 outTempTaskBounds.setEmpty();
Wale Ogunwale1666e312016-12-16 11:27:18 -0800214 }
215 }
216
217 public void prepareFreezingTaskBounds() {
218 synchronized (mWindowMap) {
219 if (mContainer == null) {
220 throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this
221 + " not found.");
222 }
223 mContainer.prepareFreezingTaskBounds();
224 }
225 }
226
Wale Ogunwale30e441d2017-11-09 08:28:45 -0800227 public void getRawBounds(Rect outBounds) {
228 synchronized (mWindowMap) {
Bryce Leef3c6a472017-11-14 14:53:06 -0800229 if (mContainer.matchParentBounds()) {
Wale Ogunwale30e441d2017-11-09 08:28:45 -0800230 outBounds.setEmpty();
231 } else {
232 mContainer.getRawBounds(outBounds);
233 }
Wale Ogunwale1666e312016-12-16 11:27:18 -0800234 }
235 }
236
237 public void getBounds(Rect outBounds) {
238 synchronized (mWindowMap) {
239 if (mContainer != null) {
240 mContainer.getBounds(outBounds);
241 return;
242 }
243 outBounds.setEmpty();
244 }
245 }
246
Matthew Ngaa2b6202017-02-10 14:48:21 -0800247 public void getBoundsForNewConfiguration(Rect outBounds) {
Wale Ogunwale1666e312016-12-16 11:27:18 -0800248 synchronized(mWindowMap) {
Matthew Ngaa2b6202017-02-10 14:48:21 -0800249 mContainer.getBoundsForNewConfiguration(outBounds);
Wale Ogunwale1666e312016-12-16 11:27:18 -0800250 }
251 }
252
Winson Chungbdc646f2017-02-13 12:12:22 -0800253 /**
Evan Roskyb0e38882018-04-25 12:48:54 -0700254 * Adjusts the screen size in dp's for the {@param config} for the given params. The provided
255 * params represent the desired state of a configuration change. Since this utility is used
256 * before mContainer has been updated, any relevant properties (like {@param windowingMode})
257 * need to be passed in.
Winson Chungbdc646f2017-02-13 12:12:22 -0800258 */
259 public void adjustConfigurationForBounds(Rect bounds, Rect insetBounds,
260 Rect nonDecorBounds, Rect stableBounds, boolean overrideWidth,
261 boolean overrideHeight, float density, Configuration config,
Evan Roskyb0e38882018-04-25 12:48:54 -0700262 Configuration parentConfig, int windowingMode) {
Winson Chungbdc646f2017-02-13 12:12:22 -0800263 synchronized (mWindowMap) {
264 final TaskStack stack = mContainer;
265 final DisplayContent displayContent = stack.getDisplayContent();
266 final DisplayInfo di = displayContent.getDisplayInfo();
Adrian Roos11c25582018-02-19 18:06:36 +0100267 final DisplayCutout displayCutout = di.displayCutout;
Winson Chungbdc646f2017-02-13 12:12:22 -0800268
269 // Get the insets and display bounds
270 mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
Adrian Roos11c25582018-02-19 18:06:36 +0100271 displayCutout, mTmpStableInsets);
Winson Chungbdc646f2017-02-13 12:12:22 -0800272 mService.mPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
Adrian Roos11c25582018-02-19 18:06:36 +0100273 displayCutout, mTmpNonDecorInsets);
Winson Chungbdc646f2017-02-13 12:12:22 -0800274 mTmpDisplayBounds.set(0, 0, di.logicalWidth, di.logicalHeight);
275
276 int width;
277 int height;
Bryce Lee7566d762017-03-30 09:34:15 -0700278
Wale Ogunwale822e5122017-07-26 06:02:24 -0700279 final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
Bryce Lee7566d762017-03-30 09:34:15 -0700280
Bryce Leef3c6a472017-11-14 14:53:06 -0800281 config.windowConfiguration.setBounds(bounds);
Wale Ogunwale822e5122017-07-26 06:02:24 -0700282 config.windowConfiguration.setAppBounds(!bounds.isEmpty() ? bounds : null);
Bryce Lee7566d762017-03-30 09:34:15 -0700283 boolean intersectParentBounds = false;
284
Evan Roskyb0e38882018-04-25 12:48:54 -0700285 if (WindowConfiguration.isFloating(windowingMode)) {
Winson Chungbdc646f2017-02-13 12:12:22 -0800286 // Floating tasks should not be resized to the screen's bounds.
287
Evan Roskyb0e38882018-04-25 12:48:54 -0700288 if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED
Wale Ogunwale44f036f2017-09-29 05:09:09 -0700289 && bounds.width() == mTmpDisplayBounds.width()
290 && bounds.height() == mTmpDisplayBounds.height()) {
Winson Chungbdc646f2017-02-13 12:12:22 -0800291 // If the bounds we are animating is the same as the fullscreen stack
292 // dimensions, then apply the same inset calculations that we normally do for
293 // the fullscreen stack, without intersecting it with the display bounds
294 stableBounds.inset(mTmpStableInsets);
295 nonDecorBounds.inset(mTmpNonDecorInsets);
Andrii Kulian1893a612017-05-04 14:45:09 -0700296 // Move app bounds to zero to apply intersection with parent correctly. They are
297 // used only for evaluating width and height, so it's OK to move them around.
Wale Ogunwale822e5122017-07-26 06:02:24 -0700298 config.windowConfiguration.getAppBounds().offsetTo(0, 0);
Bryce Lee7566d762017-03-30 09:34:15 -0700299 intersectParentBounds = true;
Winson Chungbdc646f2017-02-13 12:12:22 -0800300 }
301 width = (int) (stableBounds.width() / density);
302 height = (int) (stableBounds.height() / density);
303 } else {
304 // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
305 // area, i.e. the screen area without the system bars.
306 // Additionally task dimensions should not be bigger than its parents dimensions.
307 // The non decor inset are areas that could never be removed in Honeycomb. See
308 // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
309 intersectDisplayBoundsExcludeInsets(nonDecorBounds,
310 insetBounds != null ? insetBounds : bounds, mTmpNonDecorInsets,
311 mTmpDisplayBounds, overrideWidth, overrideHeight);
312 intersectDisplayBoundsExcludeInsets(stableBounds,
313 insetBounds != null ? insetBounds : bounds, mTmpStableInsets,
314 mTmpDisplayBounds, overrideWidth, overrideHeight);
315 width = Math.min((int) (stableBounds.width() / density),
316 parentConfig.screenWidthDp);
317 height = Math.min((int) (stableBounds.height() / density),
318 parentConfig.screenHeightDp);
Bryce Lee7566d762017-03-30 09:34:15 -0700319 intersectParentBounds = true;
320 }
321
Wale Ogunwale822e5122017-07-26 06:02:24 -0700322 if (intersectParentBounds && config.windowConfiguration.getAppBounds() != null) {
323 config.windowConfiguration.getAppBounds().intersect(parentAppBounds);
Winson Chungbdc646f2017-02-13 12:12:22 -0800324 }
325
326 config.screenWidthDp = width;
327 config.screenHeightDp = height;
328 config.smallestScreenWidthDp = getSmallestWidthForTaskBounds(
Evan Roskyb0e38882018-04-25 12:48:54 -0700329 insetBounds != null ? insetBounds : bounds, density, windowingMode);
Winson Chungbdc646f2017-02-13 12:12:22 -0800330 }
331 }
332
333 /**
334 * Intersects the specified {@code inOutBounds} with the display frame that excludes the stable
335 * inset areas.
336 *
337 * @param inOutBounds The inOutBounds to subtract the stable inset areas from.
338 */
339 private void intersectDisplayBoundsExcludeInsets(Rect inOutBounds, Rect inInsetBounds,
340 Rect stableInsets, Rect displayBounds, boolean overrideWidth, boolean overrideHeight) {
341 mTmpRect.set(inInsetBounds);
342 mService.intersectDisplayInsetBounds(displayBounds, stableInsets, mTmpRect);
343 int leftInset = mTmpRect.left - inInsetBounds.left;
344 int topInset = mTmpRect.top - inInsetBounds.top;
345 int rightInset = overrideWidth ? 0 : inInsetBounds.right - mTmpRect.right;
346 int bottomInset = overrideHeight ? 0 : inInsetBounds.bottom - mTmpRect.bottom;
347 inOutBounds.inset(leftInset, topInset, rightInset, bottomInset);
348 }
349
350 /**
Evan Roskyb0e38882018-04-25 12:48:54 -0700351 * Calculates the smallest width for a task given the target {@param bounds} and
352 * {@param windowingMode}. Avoid using values from mContainer since they can be out-of-date.
Winson Chungbdc646f2017-02-13 12:12:22 -0800353 *
354 * @return the smallest width to be used in the Configuration, in dips
355 */
Evan Roskyb0e38882018-04-25 12:48:54 -0700356 private int getSmallestWidthForTaskBounds(Rect bounds, float density, int windowingMode) {
Winson Chungbdc646f2017-02-13 12:12:22 -0800357 final DisplayContent displayContent = mContainer.getDisplayContent();
358 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
359
360 if (bounds == null || (bounds.width() == displayInfo.logicalWidth &&
361 bounds.height() == displayInfo.logicalHeight)) {
362 // If the bounds are fullscreen, return the value of the fullscreen configuration
363 return displayContent.getConfiguration().smallestScreenWidthDp;
Evan Roskyb0e38882018-04-25 12:48:54 -0700364 } else if (WindowConfiguration.isFloating(windowingMode)) {
Winson Chungbdc646f2017-02-13 12:12:22 -0800365 // For floating tasks, calculate the smallest width from the bounds of the task
366 return (int) (Math.min(bounds.width(), bounds.height()) / density);
367 } else {
368 // Iterating across all screen orientations, and return the minimum of the task
369 // width taking into account that the bounds might change because the snap algorithm
370 // snaps to a different value
371 return displayContent.getDockedDividerController()
372 .getSmallestWidthDpForBounds(bounds);
373 }
374 }
375
Wale Ogunwale1666e312016-12-16 11:27:18 -0800376 void requestResize(Rect bounds) {
377 mHandler.obtainMessage(H.REQUEST_RESIZE, bounds).sendToTarget();
378 }
379
380 @Override
381 public String toString() {
382 return "{StackWindowController stackId=" + mStackId + "}";
383 }
384
385 private static final class H extends Handler {
386
387 static final int REQUEST_RESIZE = 0;
388
389 private final WeakReference<StackWindowController> mController;
390
391 H(WeakReference<StackWindowController> controller, Looper looper) {
392 super(looper);
393 mController = controller;
394 }
395
396 @Override
397 public void handleMessage(Message msg) {
398 final StackWindowController controller = mController.get();
399 final StackWindowListener listener = (controller != null)
400 ? controller.mListener : null;
401 if (listener == null) {
402 return;
403 }
404 switch (msg.what) {
405 case REQUEST_RESIZE:
406 listener.requestResize((Rect) msg.obj);
407 break;
408 }
409 }
410 }
411}