Wale Ogunwale | 65ebd95 | 2018-04-25 15:41:44 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (C) 2018 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 static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; |
| 20 | import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; |
| 21 | import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; |
| 22 | import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; |
| 23 | import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; |
| 24 | import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; |
| 25 | import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; |
| 26 | import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK; |
| 27 | import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK; |
| 28 | import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; |
| 29 | import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; |
| 30 | import static com.android.server.am.ActivityManagerService.ANIMATE; |
| 31 | import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME; |
| 32 | import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; |
| 33 | |
| 34 | import android.app.ActivityManager; |
| 35 | import android.app.IActivityTaskManager; |
| 36 | import android.app.WindowConfiguration; |
| 37 | import android.content.Context; |
| 38 | import android.graphics.Rect; |
| 39 | import android.os.Binder; |
| 40 | import android.util.Slog; |
| 41 | |
| 42 | import com.android.server.SystemService; |
| 43 | |
| 44 | import java.util.ArrayList; |
| 45 | import java.util.List; |
| 46 | |
| 47 | /** |
| 48 | * System service for managing activities and their containers (task, stacks, displays,... ). |
| 49 | * |
| 50 | * {@hide} |
| 51 | */ |
| 52 | public class ActivityTaskManagerService extends IActivityTaskManager.Stub { |
| 53 | private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_AM; |
| 54 | private static final String TAG_STACK = TAG + POSTFIX_STACK; |
| 55 | |
| 56 | private Context mContext; |
| 57 | private ActivityManagerService mAm; |
| 58 | /* Global service lock used by the package the owns this service. */ |
| 59 | Object mGlobalLock; |
| 60 | private ActivityStackSupervisor mStackSupervisor; |
| 61 | |
| 62 | ActivityTaskManagerService(Context context) { |
| 63 | mContext = context; |
| 64 | } |
| 65 | |
| 66 | // TODO: Will be converted to WM lock once transition is complete. |
| 67 | void setActivityManagerService(ActivityManagerService am) { |
| 68 | mAm = am; |
| 69 | mGlobalLock = mAm; |
| 70 | mStackSupervisor = mAm.mStackSupervisor; |
| 71 | } |
| 72 | |
| 73 | public static final class Lifecycle extends SystemService { |
| 74 | private final ActivityTaskManagerService mService; |
| 75 | |
| 76 | public Lifecycle(Context context) { |
| 77 | super(context); |
| 78 | mService = new ActivityTaskManagerService(context); |
| 79 | } |
| 80 | |
| 81 | @Override |
| 82 | public void onStart() { |
| 83 | publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService); |
| 84 | } |
| 85 | |
| 86 | public ActivityTaskManagerService getService() { |
| 87 | return mService; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | @Override |
| 92 | public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) { |
| 93 | if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { |
| 94 | setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, |
| 95 | toTop, ANIMATE, null /* initialBounds */, true /* showRecents */); |
| 96 | return; |
| 97 | } |
| 98 | mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()"); |
| 99 | synchronized (mGlobalLock) { |
| 100 | final long ident = Binder.clearCallingIdentity(); |
| 101 | try { |
| 102 | final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); |
| 103 | if (task == null) { |
| 104 | Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId); |
| 105 | return; |
| 106 | } |
| 107 | |
| 108 | if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId |
| 109 | + " to windowingMode=" + windowingMode + " toTop=" + toTop); |
| 110 | |
| 111 | if (!task.isActivityTypeStandardOrUndefined()) { |
| 112 | throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move" |
| 113 | + " non-standard task " + taskId + " to windowing mode=" |
| 114 | + windowingMode); |
| 115 | } |
| 116 | |
| 117 | final ActivityStack stack = task.getStack(); |
| 118 | if (toTop) { |
| 119 | stack.moveToFront("setTaskWindowingMode", task); |
| 120 | } |
| 121 | stack.setWindowingMode(windowingMode); |
| 122 | } finally { |
| 123 | Binder.restoreCallingIdentity(ident); |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | @Override |
| 129 | public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) { |
| 130 | return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED); |
| 131 | } |
| 132 | |
| 133 | @Override |
| 134 | public List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, |
| 135 | @WindowConfiguration.ActivityType int ignoreActivityType, |
| 136 | @WindowConfiguration.WindowingMode int ignoreWindowingMode) { |
| 137 | final int callingUid = Binder.getCallingUid(); |
| 138 | ArrayList<ActivityManager.RunningTaskInfo> list = new ArrayList<>(); |
| 139 | |
| 140 | synchronized (mGlobalLock) { |
| 141 | if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum); |
| 142 | |
| 143 | final boolean allowed = mAm.isGetTasksAllowed("getTasks", Binder.getCallingPid(), |
| 144 | callingUid); |
| 145 | mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType, |
| 146 | ignoreWindowingMode, callingUid, allowed); |
| 147 | } |
| 148 | |
| 149 | return list; |
| 150 | } |
| 151 | |
| 152 | @Override |
| 153 | public void moveTaskToStack(int taskId, int stackId, boolean toTop) { |
| 154 | mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()"); |
| 155 | synchronized (mGlobalLock) { |
| 156 | final long ident = Binder.clearCallingIdentity(); |
| 157 | try { |
| 158 | final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); |
| 159 | if (task == null) { |
| 160 | Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId); |
| 161 | return; |
| 162 | } |
| 163 | |
| 164 | if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId |
| 165 | + " to stackId=" + stackId + " toTop=" + toTop); |
| 166 | |
| 167 | final ActivityStack stack = mStackSupervisor.getStack(stackId); |
| 168 | if (stack == null) { |
| 169 | throw new IllegalStateException( |
| 170 | "moveTaskToStack: No stack for stackId=" + stackId); |
| 171 | } |
| 172 | if (!stack.isActivityTypeStandardOrUndefined()) { |
| 173 | throw new IllegalArgumentException("moveTaskToStack: Attempt to move task " |
| 174 | + taskId + " to stack " + stackId); |
| 175 | } |
| 176 | if (stack.inSplitScreenPrimaryWindowingMode()) { |
| 177 | mAm.mWindowManager.setDockedStackCreateState( |
| 178 | SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */); |
| 179 | } |
| 180 | task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, |
| 181 | "moveTaskToStack"); |
| 182 | } finally { |
| 183 | Binder.restoreCallingIdentity(ident); |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | @Override |
| 189 | public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode, |
| 190 | boolean preserveWindows, boolean animate, int animationDuration) { |
| 191 | mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()"); |
| 192 | |
| 193 | final long ident = Binder.clearCallingIdentity(); |
| 194 | try { |
| 195 | synchronized (mGlobalLock) { |
| 196 | if (animate) { |
| 197 | final PinnedActivityStack stack = mStackSupervisor.getStack(stackId); |
| 198 | if (stack == null) { |
| 199 | Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); |
| 200 | return; |
| 201 | } |
| 202 | if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { |
| 203 | throw new IllegalArgumentException("Stack: " + stackId |
| 204 | + " doesn't support animated resize."); |
| 205 | } |
| 206 | stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds, |
| 207 | animationDuration, false /* fromFullscreen */); |
| 208 | } else { |
| 209 | final ActivityStack stack = mStackSupervisor.getStack(stackId); |
| 210 | if (stack == null) { |
| 211 | Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); |
| 212 | return; |
| 213 | } |
| 214 | mStackSupervisor.resizeStackLocked(stack, destBounds, |
| 215 | null /* tempTaskBounds */, null /* tempTaskInsetBounds */, |
| 216 | preserveWindows, allowResizeInDockedMode, !DEFER_RESUME); |
| 217 | } |
| 218 | } |
| 219 | } finally { |
| 220 | Binder.restoreCallingIdentity(ident); |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | /** |
| 225 | * Moves the specified task to the primary-split-screen stack. |
| 226 | * |
| 227 | * @param taskId Id of task to move. |
| 228 | * @param createMode The mode the primary split screen stack should be created in if it doesn't |
| 229 | * exist already. See |
| 230 | * {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT} |
| 231 | * and |
| 232 | * {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT} |
| 233 | * @param toTop If the task and stack should be moved to the top. |
| 234 | * @param animate Whether we should play an animation for the moving the task. |
| 235 | * @param initialBounds If the primary stack gets created, it will use these bounds for the |
| 236 | * stack. Pass {@code null} to use default bounds. |
| 237 | * @param showRecents If the recents activity should be shown on the other side of the task |
| 238 | * going into split-screen mode. |
| 239 | */ |
| 240 | @Override |
| 241 | public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, |
| 242 | boolean toTop, boolean animate, Rect initialBounds, boolean showRecents) { |
| 243 | mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, |
| 244 | "setTaskWindowingModeSplitScreenPrimary()"); |
| 245 | synchronized (mGlobalLock) { |
| 246 | final long ident = Binder.clearCallingIdentity(); |
| 247 | try { |
| 248 | final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); |
| 249 | if (task == null) { |
| 250 | Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId); |
| 251 | return false; |
| 252 | } |
| 253 | if (DEBUG_STACK) Slog.d(TAG_STACK, |
| 254 | "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId |
| 255 | + " to createMode=" + createMode + " toTop=" + toTop); |
| 256 | if (!task.isActivityTypeStandardOrUndefined()) { |
| 257 | throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move" |
| 258 | + " non-standard task " + taskId + " to split-screen windowing mode"); |
| 259 | } |
| 260 | |
| 261 | mAm.mWindowManager.setDockedStackCreateState(createMode, initialBounds); |
| 262 | final int windowingMode = task.getWindowingMode(); |
| 263 | final ActivityStack stack = task.getStack(); |
| 264 | if (toTop) { |
| 265 | stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task); |
| 266 | } |
| 267 | stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents, |
| 268 | false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */); |
| 269 | return windowingMode != task.getWindowingMode(); |
| 270 | } finally { |
| 271 | Binder.restoreCallingIdentity(ident); |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | /** |
| 277 | * Removes stacks in the input windowing modes from the system if they are of activity type |
| 278 | * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED |
| 279 | */ |
| 280 | @Override |
| 281 | public void removeStacksInWindowingModes(int[] windowingModes) { |
| 282 | mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, |
| 283 | "removeStacksInWindowingModes()"); |
| 284 | |
| 285 | synchronized (mGlobalLock) { |
| 286 | final long ident = Binder.clearCallingIdentity(); |
| 287 | try { |
| 288 | mStackSupervisor.removeStacksInWindowingModes(windowingModes); |
| 289 | } finally { |
| 290 | Binder.restoreCallingIdentity(ident); |
| 291 | } |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | @Override |
| 296 | public void removeStacksWithActivityTypes(int[] activityTypes) { |
| 297 | mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, |
| 298 | "removeStacksWithActivityTypes()"); |
| 299 | |
| 300 | synchronized (mGlobalLock) { |
| 301 | final long ident = Binder.clearCallingIdentity(); |
| 302 | try { |
| 303 | mStackSupervisor.removeStacksWithActivityTypes(activityTypes); |
| 304 | } finally { |
| 305 | Binder.restoreCallingIdentity(ident); |
| 306 | } |
| 307 | } |
| 308 | } |
| 309 | } |