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.am; 18 19 import android.annotation.IntDef; 20 import android.app.ActivityOptions; 21 import android.content.pm.ActivityInfo.WindowLayout; 22 import android.graphics.Rect; 23 24 import java.lang.annotation.Retention; 25 import java.lang.annotation.RetentionPolicy; 26 import java.util.ArrayList; 27 import java.util.List; 28 29 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 30 import static android.view.Display.INVALID_DISPLAY; 31 32 import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; 33 import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; 34 import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; 35 36 /** 37 * {@link LaunchParamsController} calculates the {@link LaunchParams} by coordinating between 38 * registered {@link LaunchParamsModifier}s. 39 */ 40 class LaunchParamsController { 41 private final ActivityManagerService mService; 42 private final List<LaunchParamsModifier> mModifiers = new ArrayList<>(); 43 44 // Temporary {@link LaunchParams} for internal calculations. This is kept separate from 45 // {@code mTmpCurrent} and {@code mTmpResult} to prevent clobbering values. 46 private final LaunchParams mTmpParams = new LaunchParams(); 47 48 private final LaunchParams mTmpCurrent = new LaunchParams(); 49 private final LaunchParams mTmpResult = new LaunchParams(); 50 LaunchParamsController(ActivityManagerService service)51 LaunchParamsController(ActivityManagerService service) { 52 mService = service; 53 } 54 55 /** 56 * Creates a {@link LaunchParamsController} with default registered 57 * {@link LaunchParamsModifier}s. 58 */ registerDefaultModifiers(ActivityStackSupervisor supervisor)59 void registerDefaultModifiers(ActivityStackSupervisor supervisor) { 60 // {@link TaskLaunchParamsModifier} handles window layout preferences. 61 registerModifier(new TaskLaunchParamsModifier()); 62 63 // {@link ActivityLaunchParamsModifier} is the most specific modifier and thus should be 64 // registered last (applied first) out of the defaults. 65 registerModifier(new ActivityLaunchParamsModifier(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, LaunchParams result)77 void calculate(TaskRecord task, WindowLayout layout, ActivityRecord activity, 78 ActivityRecord source, ActivityOptions options, LaunchParams result) { 79 result.reset(); 80 81 // We start at the last registered {@link LaunchParamsModifier} as this represents 82 // The modifier closest to the product level. Moving back through the list moves closer to 83 // the platform logic. 84 for (int i = mModifiers.size() - 1; i >= 0; --i) { 85 mTmpCurrent.set(result); 86 mTmpResult.reset(); 87 final LaunchParamsModifier modifier = mModifiers.get(i); 88 89 switch(modifier.onCalculate(task, layout, activity, source, options, mTmpCurrent, 90 mTmpResult)) { 91 case RESULT_SKIP: 92 // Do not apply any results when we are told to skip 93 continue; 94 case RESULT_DONE: 95 // Set result and return immediately. 96 result.set(mTmpResult); 97 return; 98 case RESULT_CONTINUE: 99 // Set result and continue 100 result.set(mTmpResult); 101 break; 102 } 103 } 104 } 105 106 /** 107 * A convenience method for laying out a task. 108 * @return {@code true} if bounds were set on the task. {@code false} otherwise. 109 */ layoutTask(TaskRecord task, WindowLayout layout)110 boolean layoutTask(TaskRecord task, WindowLayout layout) { 111 return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/); 112 } 113 layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options)114 boolean layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity, 115 ActivityRecord source, ActivityOptions options) { 116 calculate(task, layout, activity, source, options, mTmpParams); 117 118 // No changes, return. 119 if (mTmpParams.isEmpty()) { 120 return false; 121 } 122 123 mService.mWindowManager.deferSurfaceLayout(); 124 125 try { 126 if (mTmpParams.hasPreferredDisplay() 127 && mTmpParams.mPreferredDisplayId != task.getStack().getDisplay().mDisplayId) { 128 mService.moveStackToDisplay(task.getStackId(), mTmpParams.mPreferredDisplayId); 129 } 130 131 if (mTmpParams.hasWindowingMode() 132 && mTmpParams.mWindowingMode != task.getStack().getWindowingMode()) { 133 task.getStack().setWindowingMode(mTmpParams.mWindowingMode); 134 } 135 136 if (!mTmpParams.mBounds.isEmpty()) { 137 task.updateOverrideConfiguration(mTmpParams.mBounds); 138 return true; 139 } else { 140 return false; 141 } 142 } finally { 143 mService.mWindowManager.continueSurfaceLayout(); 144 } 145 } 146 147 /** 148 * Adds a modifier to participate in future bounds calculation. Note that the last registered 149 * {@link LaunchParamsModifier} will be the first to calculate the bounds. 150 */ registerModifier(LaunchParamsModifier modifier)151 void registerModifier(LaunchParamsModifier modifier) { 152 if (mModifiers.contains(modifier)) { 153 return; 154 } 155 156 mModifiers.add(modifier); 157 } 158 159 /** 160 * A container for holding launch related fields. 161 */ 162 static class LaunchParams { 163 /** The bounds within the parent container. */ 164 final Rect mBounds = new Rect(); 165 166 /** The id of the display the {@link TaskRecord} would prefer to be on. */ 167 int mPreferredDisplayId; 168 169 /** The windowing mode to be in. */ 170 int mWindowingMode; 171 172 /** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */ reset()173 void reset() { 174 mBounds.setEmpty(); 175 mPreferredDisplayId = INVALID_DISPLAY; 176 mWindowingMode = WINDOWING_MODE_UNDEFINED; 177 } 178 179 /** Copies the values set on the passed in {@link LaunchParams}. */ set(LaunchParams params)180 void set(LaunchParams params) { 181 mBounds.set(params.mBounds); 182 mPreferredDisplayId = params.mPreferredDisplayId; 183 mWindowingMode = params.mWindowingMode; 184 } 185 186 /** Returns {@code true} if no values have been explicitly set. */ isEmpty()187 boolean isEmpty() { 188 return mBounds.isEmpty() && mPreferredDisplayId == INVALID_DISPLAY 189 && mWindowingMode == WINDOWING_MODE_UNDEFINED; 190 } 191 hasWindowingMode()192 boolean hasWindowingMode() { 193 return mWindowingMode != WINDOWING_MODE_UNDEFINED; 194 } 195 hasPreferredDisplay()196 boolean hasPreferredDisplay() { 197 return mPreferredDisplayId != INVALID_DISPLAY; 198 } 199 200 @Override equals(Object o)201 public boolean equals(Object o) { 202 if (this == o) return true; 203 if (o == null || getClass() != o.getClass()) return false; 204 205 LaunchParams that = (LaunchParams) o; 206 207 if (mPreferredDisplayId != that.mPreferredDisplayId) return false; 208 if (mWindowingMode != that.mWindowingMode) return false; 209 return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null; 210 } 211 212 @Override hashCode()213 public int hashCode() { 214 int result = mBounds != null ? mBounds.hashCode() : 0; 215 result = 31 * result + mPreferredDisplayId; 216 result = 31 * result + mWindowingMode; 217 return result; 218 } 219 } 220 221 /** 222 * An interface implemented by those wanting to participate in bounds calculation. 223 */ 224 interface LaunchParamsModifier { 225 @Retention(RetentionPolicy.SOURCE) 226 @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE}) 227 @interface Result {} 228 229 // Returned when the modifier does not want to influence the bounds calculation 230 int RESULT_SKIP = 0; 231 // Returned when the modifier has changed the bounds and would like its results to be the 232 // final bounds applied. 233 int RESULT_DONE = 1; 234 // Returned when the modifier has changed the bounds but is okay with other modifiers 235 // influencing the bounds. 236 int RESULT_CONTINUE = 2; 237 238 /** 239 * Called when asked to calculate {@link LaunchParams}. 240 * @param task The {@link TaskRecord} currently being positioned. 241 * @param layout The specified {@link WindowLayout}. 242 * @param activity The {@link ActivityRecord} currently being positioned. 243 * @param source The {@link ActivityRecord} activity was started from. 244 * @param options The {@link ActivityOptions} specified for the activity. 245 * @param currentParams The current {@link LaunchParams}. This can differ from the initial 246 * params as it represents the modified params up to this point. 247 * @param outParams The resulting {@link LaunchParams} after all calculations. 248 * @return A {@link Result} representing the result of the 249 * {@link LaunchParams} calculation. 250 */ 251 @Result onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options, LaunchParams currentParams, LaunchParams outParams)252 int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity, 253 ActivityRecord source, ActivityOptions options, LaunchParams currentParams, 254 LaunchParams outParams); 255 } 256 } 257