blob: 4cd31806f99d091d40535c38dbbbbb36d7c8074a [file] [log] [blame]
Bryce Leeec55eb02017-12-05 20:51:27 -08001/*
2 * Copyright (C) 2017 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
Wale Ogunwale59507092018-10-29 09:00:30 -070017package com.android.server.wm;
Bryce Leeec55eb02017-12-05 20:51:27 -080018
Garfield Tanb5cc09f2018-09-28 10:06:52 -070019import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
20import static android.view.Display.INVALID_DISPLAY;
21
Louis Chang6fb1e842018-12-03 16:07:50 +080022import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
Wale Ogunwale59507092018-10-29 09:00:30 -070023import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
24import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
25import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
Garfield Tanb5cc09f2018-09-28 10:06:52 -070026
Bryce Leeec55eb02017-12-05 20:51:27 -080027import android.annotation.IntDef;
Andrii Kulian1cfcae82020-04-10 12:44:38 -070028import android.annotation.Nullable;
Bryce Leeec55eb02017-12-05 20:51:27 -080029import android.app.ActivityOptions;
30import android.content.pm.ActivityInfo.WindowLayout;
31import android.graphics.Rect;
32
33import java.lang.annotation.Retention;
34import java.lang.annotation.RetentionPolicy;
35import java.util.ArrayList;
36import java.util.List;
37
Bryce Leeec55eb02017-12-05 20:51:27 -080038/**
39 * {@link LaunchParamsController} calculates the {@link LaunchParams} by coordinating between
40 * registered {@link LaunchParamsModifier}s.
41 */
42class LaunchParamsController {
Wale Ogunwalea6191b42018-05-09 07:41:32 -070043 private final ActivityTaskManagerService mService;
Garfield Tan891146c2018-10-09 12:14:00 -070044 private final LaunchParamsPersister mPersister;
Bryce Leeec55eb02017-12-05 20:51:27 -080045 private final List<LaunchParamsModifier> mModifiers = new ArrayList<>();
46
47 // Temporary {@link LaunchParams} for internal calculations. This is kept separate from
48 // {@code mTmpCurrent} and {@code mTmpResult} to prevent clobbering values.
49 private final LaunchParams mTmpParams = new LaunchParams();
50
51 private final LaunchParams mTmpCurrent = new LaunchParams();
52 private final LaunchParams mTmpResult = new LaunchParams();
53
Garfield Tan891146c2018-10-09 12:14:00 -070054 LaunchParamsController(ActivityTaskManagerService service, LaunchParamsPersister persister) {
55 mService = service;
56 mPersister = persister;
Bryce Leeec55eb02017-12-05 20:51:27 -080057 }
58
59 /**
60 * Creates a {@link LaunchParamsController} with default registered
61 * {@link LaunchParamsModifier}s.
62 */
63 void registerDefaultModifiers(ActivityStackSupervisor supervisor) {
64 // {@link TaskLaunchParamsModifier} handles window layout preferences.
Garfield Tanb5cc09f2018-09-28 10:06:52 -070065 registerModifier(new TaskLaunchParamsModifier(supervisor));
Bryce Leeec55eb02017-12-05 20:51:27 -080066 }
67
68 /**
69 * Returns the {@link LaunchParams} calculated by the registered modifiers
Louis Changcdec0802019-11-11 11:45:07 +080070 * @param task The {@link Task} currently being positioned.
Bryce Leeec55eb02017-12-05 20:51:27 -080071 * @param layout The specified {@link WindowLayout}.
72 * @param activity The {@link ActivityRecord} currently being positioned.
73 * @param source The {@link ActivityRecord} from which activity was started from.
74 * @param options The {@link ActivityOptions} specified for the activity.
75 * @param result The resulting params.
76 */
Louis Changcdec0802019-11-11 11:45:07 +080077 void calculate(Task task, WindowLayout layout, ActivityRecord activity,
Louis Chang6fb1e842018-12-03 16:07:50 +080078 ActivityRecord source, ActivityOptions options, int phase, LaunchParams result) {
Bryce Leeec55eb02017-12-05 20:51:27 -080079 result.reset();
80
Garfield Tan891146c2018-10-09 12:14:00 -070081 if (task != null || activity != null) {
82 mPersister.getLaunchParams(task, activity, result);
83 }
84
Bryce Leeec55eb02017-12-05 20:51:27 -080085 // We start at the last registered {@link LaunchParamsModifier} as this represents
86 // The modifier closest to the product level. Moving back through the list moves closer to
87 // the platform logic.
88 for (int i = mModifiers.size() - 1; i >= 0; --i) {
89 mTmpCurrent.set(result);
90 mTmpResult.reset();
91 final LaunchParamsModifier modifier = mModifiers.get(i);
92
Louis Chang6fb1e842018-12-03 16:07:50 +080093 switch(modifier.onCalculate(task, layout, activity, source, options, phase, mTmpCurrent,
Bryce Leeec55eb02017-12-05 20:51:27 -080094 mTmpResult)) {
95 case RESULT_SKIP:
96 // Do not apply any results when we are told to skip
97 continue;
98 case RESULT_DONE:
99 // Set result and return immediately.
100 result.set(mTmpResult);
101 return;
102 case RESULT_CONTINUE:
103 // Set result and continue
104 result.set(mTmpResult);
105 break;
106 }
107 }
Louis Chang39ba54b2018-10-18 11:28:57 +0800108
109 if (activity != null && activity.requestedVrComponent != null) {
110 // Check if the Activity is a VR activity. If so, it should be launched in main display.
Andrii Kulian1cfcae82020-04-10 12:44:38 -0700111 result.mPreferredTaskDisplayArea = mService.mRootWindowContainer
112 .getDefaultTaskDisplayArea();
Louis Chang39ba54b2018-10-18 11:28:57 +0800113 } else if (mService.mVr2dDisplayId != INVALID_DISPLAY) {
114 // Get the virtual display ID from ActivityTaskManagerService. If that's set we
115 // should always use that.
Andrii Kulian1cfcae82020-04-10 12:44:38 -0700116 result.mPreferredTaskDisplayArea = mService.mRootWindowContainer
117 .getDisplayContent(mService.mVr2dDisplayId).getDefaultTaskDisplayArea();
Louis Chang39ba54b2018-10-18 11:28:57 +0800118 }
Bryce Leeec55eb02017-12-05 20:51:27 -0800119 }
120
121 /**
122 * A convenience method for laying out a task.
123 * @return {@code true} if bounds were set on the task. {@code false} otherwise.
124 */
Louis Changcdec0802019-11-11 11:45:07 +0800125 boolean layoutTask(Task task, WindowLayout layout) {
Bryce Leeec55eb02017-12-05 20:51:27 -0800126 return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/);
127 }
128
Louis Changcdec0802019-11-11 11:45:07 +0800129 boolean layoutTask(Task task, WindowLayout layout, ActivityRecord activity,
Bryce Leeec55eb02017-12-05 20:51:27 -0800130 ActivityRecord source, ActivityOptions options) {
Louis Chang6fb1e842018-12-03 16:07:50 +0800131 calculate(task, layout, activity, source, options, PHASE_BOUNDS, mTmpParams);
Bryce Leeec55eb02017-12-05 20:51:27 -0800132
133 // No changes, return.
134 if (mTmpParams.isEmpty()) {
135 return false;
136 }
137
Riddle Hsua0022cd2019-09-09 21:12:41 +0800138 mService.deferWindowLayout();
Bryce Leeec55eb02017-12-05 20:51:27 -0800139
140 try {
Andrii Kulian1cfcae82020-04-10 12:44:38 -0700141 if (mTmpParams.mPreferredTaskDisplayArea != null
142 && task.getDisplayArea() != mTmpParams.mPreferredTaskDisplayArea) {
143 mService.mRootWindowContainer.moveStackToTaskDisplayArea(task.getRootTaskId(),
144 mTmpParams.mPreferredTaskDisplayArea, true /* onTop */);
Bryce Leeec55eb02017-12-05 20:51:27 -0800145 }
146
147 if (mTmpParams.hasWindowingMode()
148 && mTmpParams.mWindowingMode != task.getStack().getWindowingMode()) {
149 task.getStack().setWindowingMode(mTmpParams.mWindowingMode);
150 }
151
Garfield Tan891146c2018-10-09 12:14:00 -0700152 if (mTmpParams.mBounds.isEmpty()) {
Bryce Leeec55eb02017-12-05 20:51:27 -0800153 return false;
154 }
Garfield Tan891146c2018-10-09 12:14:00 -0700155
156 if (task.getStack().inFreeformWindowingMode()) {
157 // Only set bounds if it's in freeform mode.
Evan Roskya4cc3a92019-06-28 13:25:01 -0700158 task.setBounds(mTmpParams.mBounds);
Garfield Tan891146c2018-10-09 12:14:00 -0700159 return true;
160 }
161
162 // Setting last non-fullscreen bounds to the bounds so next time the task enters
163 // freeform windowing mode it can be in this bounds.
164 task.setLastNonFullscreenBounds(mTmpParams.mBounds);
165 return false;
Bryce Leeec55eb02017-12-05 20:51:27 -0800166 } finally {
Riddle Hsua0022cd2019-09-09 21:12:41 +0800167 mService.continueWindowLayout();
Bryce Leeec55eb02017-12-05 20:51:27 -0800168 }
169 }
170
171 /**
172 * Adds a modifier to participate in future bounds calculation. Note that the last registered
173 * {@link LaunchParamsModifier} will be the first to calculate the bounds.
174 */
175 void registerModifier(LaunchParamsModifier modifier) {
176 if (mModifiers.contains(modifier)) {
177 return;
178 }
179
180 mModifiers.add(modifier);
181 }
182
183 /**
184 * A container for holding launch related fields.
185 */
186 static class LaunchParams {
187 /** The bounds within the parent container. */
188 final Rect mBounds = new Rect();
189
Andrii Kulian1cfcae82020-04-10 12:44:38 -0700190 /** The display area the {@link Task} would prefer to be on. */
191 @Nullable
192 TaskDisplayArea mPreferredTaskDisplayArea;
Bryce Leeec55eb02017-12-05 20:51:27 -0800193
194 /** The windowing mode to be in. */
195 int mWindowingMode;
196
197 /** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */
198 void reset() {
199 mBounds.setEmpty();
Andrii Kulian1cfcae82020-04-10 12:44:38 -0700200 mPreferredTaskDisplayArea = null;
Bryce Leeec55eb02017-12-05 20:51:27 -0800201 mWindowingMode = WINDOWING_MODE_UNDEFINED;
202 }
203
204 /** Copies the values set on the passed in {@link LaunchParams}. */
205 void set(LaunchParams params) {
206 mBounds.set(params.mBounds);
Andrii Kulian1cfcae82020-04-10 12:44:38 -0700207 mPreferredTaskDisplayArea = params.mPreferredTaskDisplayArea;
Bryce Leeec55eb02017-12-05 20:51:27 -0800208 mWindowingMode = params.mWindowingMode;
209 }
210
211 /** Returns {@code true} if no values have been explicitly set. */
212 boolean isEmpty() {
Andrii Kulian1cfcae82020-04-10 12:44:38 -0700213 return mBounds.isEmpty() && mPreferredTaskDisplayArea == null
Bryce Leeec55eb02017-12-05 20:51:27 -0800214 && mWindowingMode == WINDOWING_MODE_UNDEFINED;
215 }
216
217 boolean hasWindowingMode() {
218 return mWindowingMode != WINDOWING_MODE_UNDEFINED;
219 }
220
Andrii Kulian1cfcae82020-04-10 12:44:38 -0700221 boolean hasPreferredTaskDisplayArea() {
222 return mPreferredTaskDisplayArea != null;
Bryce Leeec55eb02017-12-05 20:51:27 -0800223 }
224
225 @Override
226 public boolean equals(Object o) {
227 if (this == o) return true;
228 if (o == null || getClass() != o.getClass()) return false;
229
230 LaunchParams that = (LaunchParams) o;
231
Andrii Kulian1cfcae82020-04-10 12:44:38 -0700232 if (mPreferredTaskDisplayArea != that.mPreferredTaskDisplayArea) return false;
Bryce Leeec55eb02017-12-05 20:51:27 -0800233 if (mWindowingMode != that.mWindowingMode) return false;
234 return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null;
235 }
236
237 @Override
238 public int hashCode() {
239 int result = mBounds != null ? mBounds.hashCode() : 0;
Andrii Kulian1cfcae82020-04-10 12:44:38 -0700240 result = 31 * result + (mPreferredTaskDisplayArea != null
241 ? mPreferredTaskDisplayArea.hashCode() : 0);
Bryce Leeec55eb02017-12-05 20:51:27 -0800242 result = 31 * result + mWindowingMode;
243 return result;
244 }
245 }
246
247 /**
248 * An interface implemented by those wanting to participate in bounds calculation.
249 */
250 interface LaunchParamsModifier {
251 @Retention(RetentionPolicy.SOURCE)
252 @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE})
253 @interface Result {}
254
Garfield Tanb5cc09f2018-09-28 10:06:52 -0700255 /** Returned when the modifier does not want to influence the bounds calculation */
Bryce Leeec55eb02017-12-05 20:51:27 -0800256 int RESULT_SKIP = 0;
Garfield Tanb5cc09f2018-09-28 10:06:52 -0700257 /**
258 * Returned when the modifier has changed the bounds and would like its results to be the
259 * final bounds applied.
260 */
Bryce Leeec55eb02017-12-05 20:51:27 -0800261 int RESULT_DONE = 1;
Garfield Tanb5cc09f2018-09-28 10:06:52 -0700262 /**
263 * Returned when the modifier has changed the bounds but is okay with other modifiers
264 * influencing the bounds.
265 */
Bryce Leeec55eb02017-12-05 20:51:27 -0800266 int RESULT_CONTINUE = 2;
267
Louis Chang6fb1e842018-12-03 16:07:50 +0800268 @Retention(RetentionPolicy.SOURCE)
269 @IntDef({PHASE_DISPLAY, PHASE_WINDOWING_MODE, PHASE_BOUNDS})
270 @interface Phase {}
271
272 /**
273 * Stops once we are done with preferred display calculation.
274 */
275 int PHASE_DISPLAY = 0;
276
277 /**
278 * Stops once we are done with windowing mode calculation.
279 */
280 int PHASE_WINDOWING_MODE = 1;
281
282 /**
283 * Stops once we are done with window bounds calculation.
284 */
285 int PHASE_BOUNDS = 2;
286
Bryce Leeec55eb02017-12-05 20:51:27 -0800287 /**
Garfield Tanb5cc09f2018-09-28 10:06:52 -0700288 * Returns the launch params that the provided activity launch params should be overridden
289 * to. {@link LaunchParamsModifier} can use this for various purposes, including: 1)
290 * Providing default bounds if the launch bounds have not been provided. 2) Repositioning
291 * the task so it doesn't get placed over an existing task. 3) Resizing the task so that its
292 * dimensions match the activity's requested orientation.
293 *
294 * @param task Can be: 1) the target task in which the source activity wants to
295 * launch the target activity; 2) a newly created task that Android
296 * gives a chance to override its launching bounds; 3) {@code null} if
297 * this is called to override an activity's launching bounds.
298 * @param layout Desired layout when activity is first launched.
299 * @param activity Activity that is being started. This can be {@code null} on
300 * re-parenting an activity to a new task (e.g. for
301 * Picture-In-Picture). Tasks being created because an activity was
302 * launched should have this be non-null.
303 * @param source the Activity that launched a new task. Could be {@code null}.
304 * @param options {@link ActivityOptions} used to start the activity with.
Louis Chang6fb1e842018-12-03 16:07:50 +0800305 * @param phase the calculation phase, see {@link LaunchParamsModifier.Phase}
Garfield Tanb5cc09f2018-09-28 10:06:52 -0700306 * @param currentParams launching params after the process of last {@link
307 * LaunchParamsModifier}.
308 * @param outParams the result params to be set.
309 * @return see {@link LaunchParamsModifier.Result}
Bryce Leeec55eb02017-12-05 20:51:27 -0800310 */
311 @Result
Louis Changcdec0802019-11-11 11:45:07 +0800312 int onCalculate(Task task, WindowLayout layout, ActivityRecord activity,
Louis Chang6fb1e842018-12-03 16:07:50 +0800313 ActivityRecord source, ActivityOptions options, @Phase int phase,
314 LaunchParams currentParams, LaunchParams outParams);
Bryce Leeec55eb02017-12-05 20:51:27 -0800315 }
316}