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