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.DEFAULT_DISPLAY; 21 import static android.view.Display.INVALID_DISPLAY; 22 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.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 TaskRecord} 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(TaskRecord task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options, int phase, LaunchParams result)77 void calculate(TaskRecord 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.mPreferredDisplayId = DEFAULT_DISPLAY; 112 } else if (mService.mVr2dDisplayId != INVALID_DISPLAY) { 113 // Get the virtual display ID from ActivityTaskManagerService. If that's set we 114 // should always use that. 115 result.mPreferredDisplayId = mService.mVr2dDisplayId; 116 } 117 } 118 119 /** 120 * A convenience method for laying out a task. 121 * @return {@code true} if bounds were set on the task. {@code false} otherwise. 122 */ layoutTask(TaskRecord task, WindowLayout layout)123 boolean layoutTask(TaskRecord task, WindowLayout layout) { 124 return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/); 125 } 126 layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options)127 boolean layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity, 128 ActivityRecord source, ActivityOptions options) { 129 calculate(task, layout, activity, source, options, PHASE_BOUNDS, mTmpParams); 130 131 // No changes, return. 132 if (mTmpParams.isEmpty()) { 133 return false; 134 } 135 136 mService.mWindowManager.deferSurfaceLayout(); 137 138 try { 139 if (mTmpParams.hasPreferredDisplay() 140 && mTmpParams.mPreferredDisplayId != task.getStack().getDisplay().mDisplayId) { 141 mService.moveStackToDisplay(task.getStackId(), mTmpParams.mPreferredDisplayId); 142 } 143 144 if (mTmpParams.hasWindowingMode() 145 && mTmpParams.mWindowingMode != task.getStack().getWindowingMode()) { 146 task.getStack().setWindowingMode(mTmpParams.mWindowingMode); 147 } 148 149 if (mTmpParams.mBounds.isEmpty()) { 150 return false; 151 } 152 153 if (task.getStack().inFreeformWindowingMode()) { 154 // Only set bounds if it's in freeform mode. 155 task.updateOverrideConfiguration(mTmpParams.mBounds); 156 return true; 157 } 158 159 // Setting last non-fullscreen bounds to the bounds so next time the task enters 160 // freeform windowing mode it can be in this bounds. 161 task.setLastNonFullscreenBounds(mTmpParams.mBounds); 162 return false; 163 } finally { 164 mService.mWindowManager.continueSurfaceLayout(); 165 } 166 } 167 168 /** 169 * Adds a modifier to participate in future bounds calculation. Note that the last registered 170 * {@link LaunchParamsModifier} will be the first to calculate the bounds. 171 */ registerModifier(LaunchParamsModifier modifier)172 void registerModifier(LaunchParamsModifier modifier) { 173 if (mModifiers.contains(modifier)) { 174 return; 175 } 176 177 mModifiers.add(modifier); 178 } 179 180 /** 181 * A container for holding launch related fields. 182 */ 183 static class LaunchParams { 184 /** The bounds within the parent container. */ 185 final Rect mBounds = new Rect(); 186 187 /** The id of the display the {@link TaskRecord} would prefer to be on. */ 188 int mPreferredDisplayId; 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 mPreferredDisplayId = INVALID_DISPLAY; 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 mPreferredDisplayId = params.mPreferredDisplayId; 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() && mPreferredDisplayId == INVALID_DISPLAY 210 && mWindowingMode == WINDOWING_MODE_UNDEFINED; 211 } 212 hasWindowingMode()213 boolean hasWindowingMode() { 214 return mWindowingMode != WINDOWING_MODE_UNDEFINED; 215 } 216 hasPreferredDisplay()217 boolean hasPreferredDisplay() { 218 return mPreferredDisplayId != INVALID_DISPLAY; 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 (mPreferredDisplayId != that.mPreferredDisplayId) 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 + mPreferredDisplayId; 237 result = 31 * result + mWindowingMode; 238 return result; 239 } 240 } 241 242 /** 243 * An interface implemented by those wanting to participate in bounds calculation. 244 */ 245 interface LaunchParamsModifier { 246 @Retention(RetentionPolicy.SOURCE) 247 @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE}) 248 @interface Result {} 249 250 /** Returned when the modifier does not want to influence the bounds calculation */ 251 int RESULT_SKIP = 0; 252 /** 253 * Returned when the modifier has changed the bounds and would like its results to be the 254 * final bounds applied. 255 */ 256 int RESULT_DONE = 1; 257 /** 258 * Returned when the modifier has changed the bounds but is okay with other modifiers 259 * influencing the bounds. 260 */ 261 int RESULT_CONTINUE = 2; 262 263 @Retention(RetentionPolicy.SOURCE) 264 @IntDef({PHASE_DISPLAY, PHASE_WINDOWING_MODE, PHASE_BOUNDS}) 265 @interface Phase {} 266 267 /** 268 * Stops once we are done with preferred display calculation. 269 */ 270 int PHASE_DISPLAY = 0; 271 272 /** 273 * Stops once we are done with windowing mode calculation. 274 */ 275 int PHASE_WINDOWING_MODE = 1; 276 277 /** 278 * Stops once we are done with window bounds calculation. 279 */ 280 int PHASE_BOUNDS = 2; 281 282 /** 283 * Returns the launch params that the provided activity launch params should be overridden 284 * to. {@link LaunchParamsModifier} can use this for various purposes, including: 1) 285 * Providing default bounds if the launch bounds have not been provided. 2) Repositioning 286 * the task so it doesn't get placed over an existing task. 3) Resizing the task so that its 287 * dimensions match the activity's requested orientation. 288 * 289 * @param task Can be: 1) the target task in which the source activity wants to 290 * launch the target activity; 2) a newly created task that Android 291 * gives a chance to override its launching bounds; 3) {@code null} if 292 * this is called to override an activity's launching bounds. 293 * @param layout Desired layout when activity is first launched. 294 * @param activity Activity that is being started. This can be {@code null} on 295 * re-parenting an activity to a new task (e.g. for 296 * Picture-In-Picture). Tasks being created because an activity was 297 * launched should have this be non-null. 298 * @param source the Activity that launched a new task. Could be {@code null}. 299 * @param options {@link ActivityOptions} used to start the activity with. 300 * @param phase the calculation phase, see {@link LaunchParamsModifier.Phase} 301 * @param currentParams launching params after the process of last {@link 302 * LaunchParamsModifier}. 303 * @param outParams the result params to be set. 304 * @return see {@link LaunchParamsModifier.Result} 305 */ 306 @Result onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options, @Phase int phase, LaunchParams currentParams, LaunchParams outParams)307 int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity, 308 ActivityRecord source, ActivityOptions options, @Phase int phase, 309 LaunchParams currentParams, LaunchParams outParams); 310 } 311 } 312