• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server.wm;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
20 import static android.view.Display.INVALID_DISPLAY;
21 
22 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
23 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
24 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
25 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
26 
27 import android.annotation.IntDef;
28 import android.annotation.Nullable;
29 import android.app.ActivityOptions;
30 import android.content.pm.ActivityInfo.WindowLayout;
31 import android.graphics.Rect;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.ArrayList;
36 import java.util.List;
37 
38 /**
39  * {@link LaunchParamsController} calculates the {@link LaunchParams} by coordinating between
40  * registered {@link LaunchParamsModifier}s.
41  */
42 class LaunchParamsController {
43     private final ActivityTaskManagerService mService;
44     private final LaunchParamsPersister mPersister;
45     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 
LaunchParamsController(ActivityTaskManagerService service, LaunchParamsPersister persister)54     LaunchParamsController(ActivityTaskManagerService service, LaunchParamsPersister persister) {
55         mService = service;
56         mPersister = persister;
57     }
58 
59     /**
60      * Creates a {@link LaunchParamsController} with default registered
61      * {@link LaunchParamsModifier}s.
62      */
registerDefaultModifiers(ActivityStackSupervisor supervisor)63     void registerDefaultModifiers(ActivityStackSupervisor supervisor) {
64         // {@link TaskLaunchParamsModifier} handles window layout preferences.
65         registerModifier(new TaskLaunchParamsModifier(supervisor));
66     }
67 
68     /**
69      * Returns the {@link LaunchParams} calculated by the registered modifiers
70      * @param task      The {@link Task} 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      */
calculate(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options, int phase, LaunchParams result)77     void calculate(Task task, WindowLayout layout, ActivityRecord activity,
78                    ActivityRecord source, ActivityOptions options, int phase, LaunchParams result) {
79         result.reset();
80 
81         if (task != null || activity != null) {
82             mPersister.getLaunchParams(task, activity, result);
83         }
84 
85         // 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 
93             switch(modifier.onCalculate(task, layout, activity, source, options, phase, mTmpCurrent,
94                     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         }
108 
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.
111             result.mPreferredTaskDisplayArea = mService.mRootWindowContainer
112                     .getDefaultTaskDisplayArea();
113         } else if (mService.mVr2dDisplayId != INVALID_DISPLAY) {
114             // Get the virtual display ID from ActivityTaskManagerService. If that's set we
115             // should always use that.
116             result.mPreferredTaskDisplayArea = mService.mRootWindowContainer
117                     .getDisplayContent(mService.mVr2dDisplayId).getDefaultTaskDisplayArea();
118         }
119     }
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      */
layoutTask(Task task, WindowLayout layout)125     boolean layoutTask(Task task, WindowLayout layout) {
126         return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/);
127     }
128 
layoutTask(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options)129     boolean layoutTask(Task task, WindowLayout layout, ActivityRecord activity,
130             ActivityRecord source, ActivityOptions options) {
131         calculate(task, layout, activity, source, options, PHASE_BOUNDS, mTmpParams);
132 
133         // No changes, return.
134         if (mTmpParams.isEmpty()) {
135             return false;
136         }
137 
138         mService.deferWindowLayout();
139 
140         try {
141             if (mTmpParams.mPreferredTaskDisplayArea != null
142                     && task.getDisplayArea() != mTmpParams.mPreferredTaskDisplayArea) {
143                 mService.mRootWindowContainer.moveStackToTaskDisplayArea(task.getRootTaskId(),
144                         mTmpParams.mPreferredTaskDisplayArea, true /* onTop */);
145             }
146 
147             if (mTmpParams.hasWindowingMode()
148                     && mTmpParams.mWindowingMode != task.getStack().getWindowingMode()) {
149                 final int activityType = activity != null
150                         ? activity.getActivityType() : task.getActivityType();
151                 task.getStack().setWindowingMode(task.getDisplayArea().validateWindowingMode(
152                         mTmpParams.mWindowingMode, activity, task, activityType));
153             }
154 
155             if (mTmpParams.mBounds.isEmpty()) {
156                 return false;
157             }
158 
159             if (task.getStack().inFreeformWindowingMode()) {
160                 // Only set bounds if it's in freeform mode.
161                 task.setBounds(mTmpParams.mBounds);
162                 return true;
163             }
164 
165             // Setting last non-fullscreen bounds to the bounds so next time the task enters
166             // freeform windowing mode it can be in this bounds.
167             task.setLastNonFullscreenBounds(mTmpParams.mBounds);
168             return false;
169         } finally {
170             mService.continueWindowLayout();
171         }
172     }
173 
174     /**
175      * Adds a modifier to participate in future bounds calculation. Note that the last registered
176      * {@link LaunchParamsModifier} will be the first to calculate the bounds.
177      */
registerModifier(LaunchParamsModifier modifier)178     void registerModifier(LaunchParamsModifier modifier) {
179         if (mModifiers.contains(modifier)) {
180             return;
181         }
182 
183         mModifiers.add(modifier);
184     }
185 
186     /**
187      * A container for holding launch related fields.
188      */
189     static class LaunchParams {
190         /** The bounds within the parent container. */
191         final Rect mBounds = new Rect();
192 
193         /** The display area the {@link Task} would prefer to be on. */
194         @Nullable
195         TaskDisplayArea mPreferredTaskDisplayArea;
196 
197         /** The windowing mode to be in. */
198         int mWindowingMode;
199 
200         /** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */
reset()201         void reset() {
202             mBounds.setEmpty();
203             mPreferredTaskDisplayArea = null;
204             mWindowingMode = WINDOWING_MODE_UNDEFINED;
205         }
206 
207         /** Copies the values set on the passed in {@link LaunchParams}. */
set(LaunchParams params)208         void set(LaunchParams params) {
209             mBounds.set(params.mBounds);
210             mPreferredTaskDisplayArea = params.mPreferredTaskDisplayArea;
211             mWindowingMode = params.mWindowingMode;
212         }
213 
214         /** Returns {@code true} if no values have been explicitly set. */
isEmpty()215         boolean isEmpty() {
216             return mBounds.isEmpty() && mPreferredTaskDisplayArea == null
217                     && mWindowingMode == WINDOWING_MODE_UNDEFINED;
218         }
219 
hasWindowingMode()220         boolean hasWindowingMode() {
221             return mWindowingMode != WINDOWING_MODE_UNDEFINED;
222         }
223 
hasPreferredTaskDisplayArea()224         boolean hasPreferredTaskDisplayArea() {
225             return mPreferredTaskDisplayArea != null;
226         }
227 
228         @Override
equals(Object o)229         public boolean equals(Object o) {
230             if (this == o) return true;
231             if (o == null || getClass() != o.getClass()) return false;
232 
233             LaunchParams that = (LaunchParams) o;
234 
235             if (mPreferredTaskDisplayArea != that.mPreferredTaskDisplayArea) return false;
236             if (mWindowingMode != that.mWindowingMode) return false;
237             return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null;
238         }
239 
240         @Override
hashCode()241         public int hashCode() {
242             int result = mBounds != null ? mBounds.hashCode() : 0;
243             result = 31 * result + (mPreferredTaskDisplayArea != null
244                     ? mPreferredTaskDisplayArea.hashCode() : 0);
245             result = 31 * result + mWindowingMode;
246             return result;
247         }
248     }
249 
250     /**
251      * An interface implemented by those wanting to participate in bounds calculation.
252      */
253     interface LaunchParamsModifier {
254         @Retention(RetentionPolicy.SOURCE)
255         @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE})
256         @interface Result {}
257 
258         /** Returned when the modifier does not want to influence the bounds calculation */
259         int RESULT_SKIP = 0;
260         /**
261          * Returned when the modifier has changed the bounds and would like its results to be the
262          * final bounds applied.
263          */
264         int RESULT_DONE = 1;
265         /**
266          * Returned when the modifier has changed the bounds but is okay with other modifiers
267          * influencing the bounds.
268          */
269         int RESULT_CONTINUE = 2;
270 
271         @Retention(RetentionPolicy.SOURCE)
272         @IntDef({PHASE_DISPLAY, PHASE_WINDOWING_MODE, PHASE_BOUNDS})
273         @interface Phase {}
274 
275         /**
276          * Stops once we are done with preferred display calculation.
277          */
278         int PHASE_DISPLAY = 0;
279 
280         /**
281          * Stops once we are done with windowing mode calculation.
282          */
283         int PHASE_WINDOWING_MODE = 1;
284 
285         /**
286          * Stops once we are done with window bounds calculation.
287          */
288         int PHASE_BOUNDS = 2;
289 
290         /**
291          * Returns the launch params that the provided activity launch params should be overridden
292          * to. {@link LaunchParamsModifier} can use this for various purposes, including: 1)
293          * Providing default bounds if the launch bounds have not been provided. 2) Repositioning
294          * the task so it doesn't get placed over an existing task. 3) Resizing the task so that its
295          * dimensions match the activity's requested orientation.
296          *
297          * @param task          Can be: 1) the target task in which the source activity wants to
298          *                      launch the target activity; 2) a newly created task that Android
299          *                      gives a chance to override its launching bounds; 3) {@code null} if
300          *                      this is called to override an activity's launching bounds.
301          * @param layout        Desired layout when activity is first launched.
302          * @param activity      Activity that is being started. This can be {@code null} on
303          *                      re-parenting an activity to a new task (e.g. for
304          *                      Picture-In-Picture). Tasks being created because an activity was
305          *                      launched should have this be non-null.
306          * @param source        the Activity that launched a new task. Could be {@code null}.
307          * @param options       {@link ActivityOptions} used to start the activity with.
308          * @param phase         the calculation phase, see {@link LaunchParamsModifier.Phase}
309          * @param currentParams launching params after the process of last {@link
310          *                      LaunchParamsModifier}.
311          * @param outParams     the result params to be set.
312          * @return see {@link LaunchParamsModifier.Result}
313          */
314         @Result
onCalculate(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options, @Phase int phase, LaunchParams currentParams, LaunchParams outParams)315         int onCalculate(Task task, WindowLayout layout, ActivityRecord activity,
316                 ActivityRecord source, ActivityOptions options, @Phase int phase,
317                 LaunchParams currentParams, LaunchParams outParams);
318     }
319 }
320