• Home
  • History
  • Annotate
  • 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