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