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 17package com.android.server.am; 18 19import android.annotation.IntDef; 20import android.app.ActivityOptions; 21import android.content.pm.ActivityInfo.WindowLayout; 22import android.graphics.Rect; 23 24import java.lang.annotation.Retention; 25import java.lang.annotation.RetentionPolicy; 26import java.util.ArrayList; 27import java.util.List; 28 29import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 30import static android.view.Display.INVALID_DISPLAY; 31 32import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; 33import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; 34import 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 */ 40class 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 51 LaunchParamsController(ActivityManagerService service) { 52 mService = service; 53 } 54 55 /** 56 * Creates a {@link LaunchParamsController} with default registered 57 * {@link LaunchParamsModifier}s. 58 */ 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 */ 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 */ 110 boolean layoutTask(TaskRecord task, WindowLayout layout) { 111 return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/); 112 } 113 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 */ 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. */ 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}. */ 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. */ 187 boolean isEmpty() { 188 return mBounds.isEmpty() && mPreferredDisplayId == INVALID_DISPLAY 189 && mWindowingMode == WINDOWING_MODE_UNDEFINED; 190 } 191 192 boolean hasWindowingMode() { 193 return mWindowingMode != WINDOWING_MODE_UNDEFINED; 194 } 195 196 boolean hasPreferredDisplay() { 197 return mPreferredDisplayId != INVALID_DISPLAY; 198 } 199 200 @Override 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 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 252 int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity, 253 ActivityRecord source, ActivityOptions options, LaunchParams currentParams, 254 LaunchParams outParams); 255 } 256} 257