blob: 7ab7f987d3016b809170f425b9147f3300a61073 [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
17package com.android.server.am;
18
19import android.annotation.IntDef;
20import android.app.ActivityOptions;
21import android.content.pm.ActivityInfo.WindowLayout;
22import android.graphics.Rect;
23
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.util.ArrayList;
27import java.util.List;
28
29import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
30import static android.view.Display.INVALID_DISPLAY;
31
32import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
33import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
34import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
35
36/**
37 * {@link LaunchParamsController} calculates the {@link LaunchParams} by coordinating between
38 * registered {@link LaunchParamsModifier}s.
39 */
40class LaunchParamsController {
41 private final ActivityManagerService mService;
42 private final List<LaunchParamsModifier> mModifiers = new ArrayList<>();
43
44 // Temporary {@link LaunchParams} for internal calculations. This is kept separate from
45 // {@code mTmpCurrent} and {@code mTmpResult} to prevent clobbering values.
46 private final LaunchParams mTmpParams = new LaunchParams();
47
48 private final LaunchParams mTmpCurrent = new LaunchParams();
49 private final LaunchParams mTmpResult = new LaunchParams();
50
51 LaunchParamsController(ActivityManagerService service) {
52 mService = service;
53 }
54
55 /**
56 * Creates a {@link LaunchParamsController} with default registered
57 * {@link LaunchParamsModifier}s.
58 */
59 void registerDefaultModifiers(ActivityStackSupervisor supervisor) {
60 // {@link TaskLaunchParamsModifier} handles window layout preferences.
61 registerModifier(new TaskLaunchParamsModifier());
62
63 // {@link ActivityLaunchParamsModifier} is the most specific modifier and thus should be
64 // registered last (applied first) out of the defaults.
65 registerModifier(new ActivityLaunchParamsModifier(supervisor));
66 }
67
68 /**
69 * Returns the {@link LaunchParams} calculated by the registered modifiers
70 * @param task The {@link TaskRecord} currently being positioned.
71 * @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 */
77 void calculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
78 ActivityRecord source, ActivityOptions options, LaunchParams result) {
79 result.reset();
80
81 // We start at the last registered {@link LaunchParamsModifier} as this represents
82 // The modifier closest to the product level. Moving back through the list moves closer to
83 // the platform logic.
84 for (int i = mModifiers.size() - 1; i >= 0; --i) {
85 mTmpCurrent.set(result);
86 mTmpResult.reset();
87 final LaunchParamsModifier modifier = mModifiers.get(i);
88
89 switch(modifier.onCalculate(task, layout, activity, source, options, mTmpCurrent,
90 mTmpResult)) {
91 case RESULT_SKIP:
92 // Do not apply any results when we are told to skip
93 continue;
94 case RESULT_DONE:
95 // Set result and return immediately.
96 result.set(mTmpResult);
97 return;
98 case RESULT_CONTINUE:
99 // Set result and continue
100 result.set(mTmpResult);
101 break;
102 }
103 }
104 }
105
106 /**
107 * A convenience method for laying out a task.
108 * @return {@code true} if bounds were set on the task. {@code false} otherwise.
109 */
110 boolean layoutTask(TaskRecord task, WindowLayout layout) {
111 return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/);
112 }
113
114 boolean layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity,
115 ActivityRecord source, ActivityOptions options) {
116 calculate(task, layout, activity, source, options, mTmpParams);
117
118 // No changes, return.
119 if (mTmpParams.isEmpty()) {
120 return false;
121 }
122
123 mService.mWindowManager.deferSurfaceLayout();
124
125 try {
126 if (mTmpParams.hasPreferredDisplay()
127 && mTmpParams.mPreferredDisplayId != task.getStack().getDisplay().mDisplayId) {
128 mService.moveStackToDisplay(task.getStackId(), mTmpParams.mPreferredDisplayId);
129 }
130
131 if (mTmpParams.hasWindowingMode()
132 && mTmpParams.mWindowingMode != task.getStack().getWindowingMode()) {
133 task.getStack().setWindowingMode(mTmpParams.mWindowingMode);
134 }
135
136 if (!mTmpParams.mBounds.isEmpty()) {
137 task.updateOverrideConfiguration(mTmpParams.mBounds);
138 return true;
139 } else {
140 return false;
141 }
142 } finally {
143 mService.mWindowManager.continueSurfaceLayout();
144 }
145 }
146
147 /**
148 * Adds a modifier to participate in future bounds calculation. Note that the last registered
149 * {@link LaunchParamsModifier} will be the first to calculate the bounds.
150 */
151 void registerModifier(LaunchParamsModifier modifier) {
152 if (mModifiers.contains(modifier)) {
153 return;
154 }
155
156 mModifiers.add(modifier);
157 }
158
159 /**
160 * A container for holding launch related fields.
161 */
162 static class LaunchParams {
163 /** The bounds within the parent container. */
164 final Rect mBounds = new Rect();
165
166 /** The id of the display the {@link TaskRecord} would prefer to be on. */
167 int mPreferredDisplayId;
168
169 /** The windowing mode to be in. */
170 int mWindowingMode;
171
172 /** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */
173 void reset() {
174 mBounds.setEmpty();
175 mPreferredDisplayId = INVALID_DISPLAY;
176 mWindowingMode = WINDOWING_MODE_UNDEFINED;
177 }
178
179 /** Copies the values set on the passed in {@link LaunchParams}. */
180 void set(LaunchParams params) {
181 mBounds.set(params.mBounds);
182 mPreferredDisplayId = params.mPreferredDisplayId;
183 mWindowingMode = params.mWindowingMode;
184 }
185
186 /** Returns {@code true} if no values have been explicitly set. */
187 boolean isEmpty() {
188 return mBounds.isEmpty() && mPreferredDisplayId == INVALID_DISPLAY
189 && mWindowingMode == WINDOWING_MODE_UNDEFINED;
190 }
191
192 boolean hasWindowingMode() {
193 return mWindowingMode != WINDOWING_MODE_UNDEFINED;
194 }
195
196 boolean hasPreferredDisplay() {
197 return mPreferredDisplayId != INVALID_DISPLAY;
198 }
199
200 @Override
201 public boolean equals(Object o) {
202 if (this == o) return true;
203 if (o == null || getClass() != o.getClass()) return false;
204
205 LaunchParams that = (LaunchParams) o;
206
207 if (mPreferredDisplayId != that.mPreferredDisplayId) return false;
208 if (mWindowingMode != that.mWindowingMode) return false;
209 return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null;
210 }
211
212 @Override
213 public int hashCode() {
214 int result = mBounds != null ? mBounds.hashCode() : 0;
215 result = 31 * result + mPreferredDisplayId;
216 result = 31 * result + mWindowingMode;
217 return result;
218 }
219 }
220
221 /**
222 * An interface implemented by those wanting to participate in bounds calculation.
223 */
224 interface LaunchParamsModifier {
225 @Retention(RetentionPolicy.SOURCE)
226 @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE})
227 @interface Result {}
228
229 // Returned when the modifier does not want to influence the bounds calculation
230 int RESULT_SKIP = 0;
231 // Returned when the modifier has changed the bounds and would like its results to be the
232 // final bounds applied.
233 int RESULT_DONE = 1;
234 // Returned when the modifier has changed the bounds but is okay with other modifiers
235 // influencing the bounds.
236 int RESULT_CONTINUE = 2;
237
238 /**
239 * Called when asked to calculate {@link LaunchParams}.
240 * @param task The {@link TaskRecord} currently being positioned.
241 * @param layout The specified {@link WindowLayout}.
242 * @param activity The {@link ActivityRecord} currently being positioned.
243 * @param source The {@link ActivityRecord} activity was started from.
244 * @param options The {@link ActivityOptions} specified for the activity.
245 * @param currentParams The current {@link LaunchParams}. This can differ from the initial
246 * params as it represents the modified params up to this point.
247 * @param outParams The resulting {@link LaunchParams} after all calculations.
248 * @return A {@link Result} representing the result of the
249 * {@link LaunchParams} calculation.
250 */
251 @Result
252 int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
253 ActivityRecord source, ActivityOptions options, LaunchParams currentParams,
254 LaunchParams outParams);
255 }
256}