• 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.ActivityStarter.Request;
23 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
24 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
25 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
26 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
27 
28 import android.annotation.IntDef;
29 import android.annotation.Nullable;
30 import android.app.ActivityOptions;
31 import android.content.pm.ActivityInfo.WindowLayout;
32 import android.graphics.Rect;
33 
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /**
40  * {@link LaunchParamsController} calculates the {@link LaunchParams} by coordinating between
41  * registered {@link LaunchParamsModifier}s.
42  */
43 class LaunchParamsController {
44     private final ActivityTaskManagerService mService;
45     private final LaunchParamsPersister mPersister;
46     private final List<LaunchParamsModifier> mModifiers = new ArrayList<>();
47 
48     // Temporary {@link LaunchParams} for internal calculations. This is kept separate from
49     // {@code mTmpCurrent} and {@code mTmpResult} to prevent clobbering values.
50     private final LaunchParams mTmpParams = new LaunchParams();
51 
52     private final LaunchParams mTmpCurrent = new LaunchParams();
53     private final LaunchParams mTmpResult = new LaunchParams();
54 
LaunchParamsController(ActivityTaskManagerService service, LaunchParamsPersister persister)55     LaunchParamsController(ActivityTaskManagerService service, LaunchParamsPersister persister) {
56         mService = service;
57         mPersister = persister;
58     }
59 
60     /**
61      * Creates a {@link LaunchParamsController} with default registered
62      * {@link LaunchParamsModifier}s.
63      */
registerDefaultModifiers(ActivityTaskSupervisor supervisor)64     void registerDefaultModifiers(ActivityTaskSupervisor supervisor) {
65         // {@link TaskLaunchParamsModifier} handles window layout preferences.
66         registerModifier(new TaskLaunchParamsModifier(supervisor));
67     }
68 
69     /**
70      * Returns the {@link LaunchParams} calculated by the registered modifiers
71      * @param task      The {@link Task} currently being positioned.
72      * @param layout    The specified {@link WindowLayout}.
73      * @param activity  The {@link ActivityRecord} currently being positioned.
74      * @param source    The {@link ActivityRecord} from which activity was started from.
75      * @param options   The {@link ActivityOptions} specified for the activity.
76      * @param request   The optional request from the activity starter.
77      * @param phase     The {@link LaunchParamsModifier.Phase} that the resolution should finish.
78      * @param result    The resulting params.
79      */
calculate(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options, @Nullable Request request, int phase, LaunchParams result)80     void calculate(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source,
81             ActivityOptions options, @Nullable Request request, int phase, LaunchParams result) {
82         result.reset();
83 
84         if (task != null || activity != null) {
85             mPersister.getLaunchParams(task, activity, result);
86         }
87 
88         // We start at the last registered {@link LaunchParamsModifier} as this represents
89         // The modifier closest to the product level. Moving back through the list moves closer to
90         // the platform logic.
91         for (int i = mModifiers.size() - 1; i >= 0; --i) {
92             mTmpCurrent.set(result);
93             mTmpResult.reset();
94             final LaunchParamsModifier modifier = mModifiers.get(i);
95 
96             switch(modifier.onCalculate(task, layout, activity, source, options, request, phase,
97                     mTmpCurrent, mTmpResult)) {
98                 case RESULT_SKIP:
99                     // Do not apply any results when we are told to skip
100                     continue;
101                 case RESULT_DONE:
102                     // Set result and return immediately.
103                     result.set(mTmpResult);
104                     return;
105                 case RESULT_CONTINUE:
106                     // Set result and continue
107                     result.set(mTmpResult);
108                     break;
109             }
110         }
111 
112         if (activity != null && activity.requestedVrComponent != null) {
113             // Check if the Activity is a VR activity. If so, it should be launched in main display.
114             result.mPreferredTaskDisplayArea = mService.mRootWindowContainer
115                     .getDefaultTaskDisplayArea();
116         } else if (mService.mVr2dDisplayId != INVALID_DISPLAY) {
117             // Get the virtual display ID from ActivityTaskManagerService. If that's set we
118             // should always use that.
119             result.mPreferredTaskDisplayArea = mService.mRootWindowContainer
120                     .getDisplayContent(mService.mVr2dDisplayId).getDefaultTaskDisplayArea();
121         }
122     }
123 
124     /**
125      * A convenience method for laying out a task.
126      * @return {@code true} if bounds were set on the task. {@code false} otherwise.
127      */
layoutTask(Task task, WindowLayout layout)128     boolean layoutTask(Task task, WindowLayout layout) {
129         return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/);
130     }
131 
layoutTask(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options)132     boolean layoutTask(Task task, WindowLayout layout, ActivityRecord activity,
133             ActivityRecord source, ActivityOptions options) {
134         calculate(task, layout, activity, source, options, null /* request */, PHASE_BOUNDS,
135                 mTmpParams);
136 
137         // No changes, return.
138         if (mTmpParams.isEmpty()) {
139             return false;
140         }
141 
142         mService.deferWindowLayout();
143 
144         try {
145             if (mTmpParams.mPreferredTaskDisplayArea != null
146                     && task.getDisplayArea() != mTmpParams.mPreferredTaskDisplayArea) {
147                 mService.mRootWindowContainer.moveRootTaskToTaskDisplayArea(task.getRootTaskId(),
148                         mTmpParams.mPreferredTaskDisplayArea, true /* onTop */);
149             }
150 
151             if (mTmpParams.hasWindowingMode() && task.isRootTask()
152                     && mTmpParams.mWindowingMode != task.getWindowingMode()) {
153                 final int activityType = activity != null
154                         ? activity.getActivityType() : task.getActivityType();
155                 task.setWindowingMode(task.getDisplayArea().validateWindowingMode(
156                         mTmpParams.mWindowingMode, activity, task, activityType));
157             }
158 
159             if (mTmpParams.mBounds.isEmpty()) {
160                 return false;
161             }
162 
163             if (task.getRootTask().inMultiWindowMode()) {
164                 task.setBounds(mTmpParams.mBounds);
165                 return true;
166             }
167 
168             // Setting last non-fullscreen bounds to the bounds so next time the task enters
169             // freeform windowing mode it can be in this bounds.
170             task.setLastNonFullscreenBounds(mTmpParams.mBounds);
171             return false;
172         } finally {
173             mService.continueWindowLayout();
174         }
175     }
176 
177     /**
178      * Adds a modifier to participate in future bounds calculation. Note that the last registered
179      * {@link LaunchParamsModifier} will be the first to calculate the bounds.
180      */
registerModifier(LaunchParamsModifier modifier)181     void registerModifier(LaunchParamsModifier modifier) {
182         if (mModifiers.contains(modifier)) {
183             return;
184         }
185 
186         mModifiers.add(modifier);
187     }
188 
189     /**
190      * A container for holding launch related fields.
191      */
192     static class LaunchParams {
193         /** The bounds within the parent container. */
194         final Rect mBounds = new Rect();
195 
196         /** The display area the {@link Task} would prefer to be on. */
197         @Nullable
198         TaskDisplayArea mPreferredTaskDisplayArea;
199 
200         /** The windowing mode to be in. */
201         int mWindowingMode;
202 
203         /** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */
reset()204         void reset() {
205             mBounds.setEmpty();
206             mPreferredTaskDisplayArea = null;
207             mWindowingMode = WINDOWING_MODE_UNDEFINED;
208         }
209 
210         /** Copies the values set on the passed in {@link LaunchParams}. */
set(LaunchParams params)211         void set(LaunchParams params) {
212             mBounds.set(params.mBounds);
213             mPreferredTaskDisplayArea = params.mPreferredTaskDisplayArea;
214             mWindowingMode = params.mWindowingMode;
215         }
216 
217         /** Returns {@code true} if no values have been explicitly set. */
isEmpty()218         boolean isEmpty() {
219             return mBounds.isEmpty() && mPreferredTaskDisplayArea == null
220                     && mWindowingMode == WINDOWING_MODE_UNDEFINED;
221         }
222 
hasWindowingMode()223         boolean hasWindowingMode() {
224             return mWindowingMode != WINDOWING_MODE_UNDEFINED;
225         }
226 
hasPreferredTaskDisplayArea()227         boolean hasPreferredTaskDisplayArea() {
228             return mPreferredTaskDisplayArea != null;
229         }
230 
231         @Override
equals(Object o)232         public boolean equals(Object o) {
233             if (this == o) return true;
234             if (o == null || getClass() != o.getClass()) return false;
235 
236             LaunchParams that = (LaunchParams) o;
237 
238             if (mPreferredTaskDisplayArea != that.mPreferredTaskDisplayArea) return false;
239             if (mWindowingMode != that.mWindowingMode) return false;
240             return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null;
241         }
242 
243         @Override
hashCode()244         public int hashCode() {
245             int result = mBounds != null ? mBounds.hashCode() : 0;
246             result = 31 * result + (mPreferredTaskDisplayArea != null
247                     ? mPreferredTaskDisplayArea.hashCode() : 0);
248             result = 31 * result + mWindowingMode;
249             return result;
250         }
251     }
252 
253     /**
254      * An interface implemented by those wanting to participate in bounds calculation.
255      */
256     interface LaunchParamsModifier {
257         @Retention(RetentionPolicy.SOURCE)
258         @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE})
259         @interface Result {}
260 
261         /** Returned when the modifier does not want to influence the bounds calculation */
262         int RESULT_SKIP = 0;
263         /**
264          * Returned when the modifier has changed the bounds and would like its results to be the
265          * final bounds applied.
266          */
267         int RESULT_DONE = 1;
268         /**
269          * Returned when the modifier has changed the bounds but is okay with other modifiers
270          * influencing the bounds.
271          */
272         int RESULT_CONTINUE = 2;
273 
274         @Retention(RetentionPolicy.SOURCE)
275         @IntDef({PHASE_DISPLAY, PHASE_WINDOWING_MODE, PHASE_BOUNDS})
276         @interface Phase {}
277 
278         /**
279          * Stops once we are done with preferred display calculation. The result returned from this
280          * phase is only guaranteed to have a display area which display is the launch display.
281          */
282         int PHASE_DISPLAY = 0;
283 
284         /**
285          * Stops once we are done with windowing mode calculation.
286          */
287         int PHASE_WINDOWING_MODE = 1;
288 
289         /**
290          * Stops once we are done with display area calculation.
291          */
292         int PHASE_DISPLAY_AREA = 2;
293 
294         /**
295          * Stops once we are done with window bounds calculation.
296          */
297         int PHASE_BOUNDS = 3;
298 
299         /**
300          * Returns the launch params that the provided activity launch params should be overridden
301          * to. {@link LaunchParamsModifier} can use this for various purposes, including: 1)
302          * Providing default bounds if the launch bounds have not been provided. 2) Repositioning
303          * the task so it doesn't get placed over an existing task. 3) Resizing the task so that its
304          * dimensions match the activity's requested orientation.
305          *
306          * @param task          Can be: 1) the target task in which the source activity wants to
307          *                      launch the target activity; 2) a newly created task that Android
308          *                      gives a chance to override its launching bounds; 3) {@code null} if
309          *                      this is called to override an activity's launching bounds.
310          * @param layout        Desired layout when activity is first launched.
311          * @param activity      Activity that is being started. This can be {@code null} on
312          *                      re-parenting an activity to a new task (e.g. for
313          *                      Picture-In-Picture). Tasks being created because an activity was
314          *                      launched should have this be non-null.
315          * @param source        the Activity that launched a new task. Could be {@code null}.
316          * @param options       {@link ActivityOptions} used to start the activity with.
317          * @param request       Optional data to give more context on the launch
318          * @param phase         the calculation phase, see {@link Phase}
319          * @param currentParams launching params after the process of last {@link
320          *                      LaunchParamsModifier}.
321          * @param outParams     the result params to be set.
322          * @return see {@link LaunchParamsModifier.Result}
323          */
324         @Result
onCalculate(@ullable Task task, @Nullable WindowLayout layout, @Nullable ActivityRecord activity, @Nullable ActivityRecord source, @Nullable ActivityOptions options, @Nullable Request request, @Phase int phase, LaunchParams currentParams, LaunchParams outParams)325         int onCalculate(@Nullable Task task, @Nullable WindowLayout layout,
326                 @Nullable ActivityRecord activity, @Nullable ActivityRecord source,
327                 @Nullable ActivityOptions options, @Nullable Request request,
328                 @Phase int phase, LaunchParams currentParams, LaunchParams outParams);
329     }
330 }
331