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