1 /* 2 * Copyright (C) 2020 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.wm.shell; 18 19 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 24 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 25 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 26 27 import static com.android.wm.shell.compatui.impl.CompatUIEventsKt.SIZE_COMPAT_RESTART_BUTTON_APPEARED; 28 import static com.android.wm.shell.compatui.impl.CompatUIEventsKt.SIZE_COMPAT_RESTART_BUTTON_CLICKED; 29 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; 30 import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; 31 32 import android.annotation.IntDef; 33 import android.annotation.NonNull; 34 import android.annotation.Nullable; 35 import android.app.ActivityManager.RunningTaskInfo; 36 import android.app.TaskInfo; 37 import android.app.WindowConfiguration; 38 import android.content.LocusId; 39 import android.content.pm.ActivityInfo; 40 import android.graphics.Rect; 41 import android.os.Binder; 42 import android.os.IBinder; 43 import android.util.ArrayMap; 44 import android.util.ArraySet; 45 import android.util.Log; 46 import android.util.SparseArray; 47 import android.view.SurfaceControl; 48 import android.window.ITaskOrganizerController; 49 import android.window.StartingWindowInfo; 50 import android.window.StartingWindowRemovalInfo; 51 import android.window.TaskAppearedInfo; 52 import android.window.TaskOrganizer; 53 54 import com.android.internal.annotations.VisibleForTesting; 55 import com.android.internal.protolog.ProtoLog; 56 import com.android.internal.util.FrameworkStatsLog; 57 import com.android.wm.shell.common.ShellExecutor; 58 import com.android.wm.shell.compatui.CompatUIController; 59 import com.android.wm.shell.compatui.api.CompatUIHandler; 60 import com.android.wm.shell.compatui.api.CompatUIInfo; 61 import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonAppeared; 62 import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonClicked; 63 import com.android.wm.shell.recents.RecentTasksController; 64 import com.android.wm.shell.startingsurface.StartingWindowController; 65 import com.android.wm.shell.sysui.ShellCommandHandler; 66 import com.android.wm.shell.sysui.ShellInit; 67 import com.android.wm.shell.unfold.UnfoldAnimationController; 68 69 import java.io.PrintWriter; 70 import java.util.ArrayList; 71 import java.util.Arrays; 72 import java.util.List; 73 import java.util.Objects; 74 import java.util.Optional; 75 76 /** 77 * Unified task organizer for all components in the shell. 78 * TODO(b/167582004): may consider consolidating this class and TaskOrganizer 79 */ 80 public class ShellTaskOrganizer extends TaskOrganizer { 81 private static final String TAG = "ShellTaskOrganizer"; 82 83 // Intentionally using negative numbers here so the positive numbers can be used 84 // for task id specific listeners that will be added later. 85 public static final int TASK_LISTENER_TYPE_UNDEFINED = -1; 86 public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2; 87 public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3; 88 public static final int TASK_LISTENER_TYPE_PIP = -4; 89 public static final int TASK_LISTENER_TYPE_FREEFORM = -5; 90 91 @IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = { 92 TASK_LISTENER_TYPE_UNDEFINED, 93 TASK_LISTENER_TYPE_FULLSCREEN, 94 TASK_LISTENER_TYPE_MULTI_WINDOW, 95 TASK_LISTENER_TYPE_PIP, 96 TASK_LISTENER_TYPE_FREEFORM, 97 }) 98 public @interface TaskListenerType {} 99 100 /** 101 * Callbacks for when the tasks change in the system. 102 */ 103 public interface TaskListener { onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash)104 default void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {} onTaskInfoChanged(RunningTaskInfo taskInfo)105 default void onTaskInfoChanged(RunningTaskInfo taskInfo) {} onTaskVanished(RunningTaskInfo taskInfo)106 default void onTaskVanished(RunningTaskInfo taskInfo) {} onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)107 default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {} 108 /** Whether this task listener supports compat UI. */ supportCompatUI()109 default boolean supportCompatUI() { 110 // All TaskListeners should support compat UI except PIP and StageCoordinator. 111 return true; 112 } 113 /** Attaches a child window surface to the task surface. */ attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b)114 default void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { 115 throw new IllegalStateException( 116 "This task listener doesn't support child surface attachment."); 117 } 118 /** Reparents a child window surface to the task surface. */ reparentChildSurfaceToTask(int taskId, SurfaceControl sc, SurfaceControl.Transaction t)119 default void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, 120 SurfaceControl.Transaction t) { 121 throw new IllegalStateException( 122 "This task listener doesn't support child surface reparent."); 123 } dump(@onNull PrintWriter pw, String prefix)124 default void dump(@NonNull PrintWriter pw, String prefix) {}; 125 } 126 127 /** 128 * Limited scope callback to notify when a task is removed from the system. This signal is 129 * not synchronized with anything (or any transition), and should not be used in cases where 130 * that is necessary. 131 */ 132 public interface TaskVanishedListener { onTaskVanished(RunningTaskInfo taskInfo)133 default void onTaskVanished(RunningTaskInfo taskInfo) {} 134 } 135 136 /** 137 * Callbacks for events on a task with a locus id. 138 */ 139 public interface LocusIdListener { 140 /** 141 * Notifies when a task with a locusId becomes visible, when a visible task's locusId 142 * changes, or if a previously visible task with a locusId becomes invisible. 143 */ onVisibilityChanged(int taskId, LocusId locus, boolean visible)144 void onVisibilityChanged(int taskId, LocusId locus, boolean visible); 145 } 146 147 /** 148 * Callbacks for events in which the focus has changed. 149 */ 150 public interface FocusListener { 151 /** 152 * Notifies when the task which is focused has changed. 153 */ onFocusTaskChanged(RunningTaskInfo taskInfo)154 void onFocusTaskChanged(RunningTaskInfo taskInfo); 155 } 156 157 /** 158 * Keys map from either a task id or {@link TaskListenerType}. 159 * @see #addListenerForTaskId 160 * @see #addListenerForType 161 */ 162 private final SparseArray<TaskListener> mTaskListeners = new SparseArray<>(); 163 164 // Keeps track of all the tasks reported to this organizer (changes in windowing mode will 165 // require us to report to both old and new listeners) 166 private final SparseArray<TaskAppearedInfo> mTasks = new SparseArray<>(); 167 168 /** @see #setPendingLaunchCookieListener */ 169 private final ArrayMap<IBinder, TaskListener> mLaunchCookieToListener = new ArrayMap<>(); 170 171 // Keeps track of taskId's with visible locusIds. Used to notify any {@link LocusIdListener}s 172 // that might be set. 173 private final SparseArray<LocusId> mVisibleTasksWithLocusId = new SparseArray<>(); 174 175 /** @see #addLocusIdListener */ 176 private final ArraySet<LocusIdListener> mLocusIdListeners = new ArraySet<>(); 177 178 private final ArraySet<FocusListener> mFocusListeners = new ArraySet<>(); 179 180 // Listeners that should be notified when a task is removed 181 private final ArraySet<TaskVanishedListener> mTaskVanishedListeners = new ArraySet<>(); 182 183 private final Object mLock = new Object(); 184 private StartingWindowController mStartingWindow; 185 186 /** Overlay surface for home root task */ 187 private final SurfaceControl mHomeTaskOverlayContainer = new SurfaceControl.Builder() 188 .setName("home_task_overlay_container") 189 .setContainerLayer() 190 .setHidden(false) 191 .setCallsite("ShellTaskOrganizer.mHomeTaskOverlayContainer") 192 .build(); 193 194 /** 195 * In charge of showing compat UI. Can be {@code null} if the device doesn't support size 196 * compat or if this isn't the main {@link ShellTaskOrganizer}. 197 * 198 * <p>NOTE: only the main {@link ShellTaskOrganizer} should have a {@link CompatUIHandler}, 199 * Subclasses should be initialized with a {@code null} {@link CompatUIHandler}. 200 */ 201 @Nullable 202 private final CompatUIHandler mCompatUI; 203 204 @NonNull 205 private final ShellCommandHandler mShellCommandHandler; 206 207 @Nullable 208 private final Optional<RecentTasksController> mRecentTasks; 209 210 @Nullable 211 private final UnfoldAnimationController mUnfoldAnimationController; 212 213 @Nullable 214 private RunningTaskInfo mLastFocusedTaskInfo; 215 ShellTaskOrganizer(ShellExecutor mainExecutor)216 public ShellTaskOrganizer(ShellExecutor mainExecutor) { 217 this(null /* shellInit */, null /* shellCommandHandler */, 218 null /* taskOrganizerController */, null /* compatUI */, 219 Optional.empty() /* unfoldAnimationController */, 220 Optional.empty() /* recentTasksController */, 221 mainExecutor); 222 } 223 ShellTaskOrganizer(ShellInit shellInit, ShellCommandHandler shellCommandHandler, @Nullable CompatUIHandler compatUI, Optional<UnfoldAnimationController> unfoldAnimationController, Optional<RecentTasksController> recentTasks, ShellExecutor mainExecutor)224 public ShellTaskOrganizer(ShellInit shellInit, 225 ShellCommandHandler shellCommandHandler, 226 @Nullable CompatUIHandler compatUI, 227 Optional<UnfoldAnimationController> unfoldAnimationController, 228 Optional<RecentTasksController> recentTasks, 229 ShellExecutor mainExecutor) { 230 this(shellInit, shellCommandHandler, null /* taskOrganizerController */, compatUI, 231 unfoldAnimationController, recentTasks, mainExecutor); 232 } 233 234 @VisibleForTesting ShellTaskOrganizer(ShellInit shellInit, ShellCommandHandler shellCommandHandler, ITaskOrganizerController taskOrganizerController, @Nullable CompatUIHandler compatUI, Optional<UnfoldAnimationController> unfoldAnimationController, Optional<RecentTasksController> recentTasks, ShellExecutor mainExecutor)235 protected ShellTaskOrganizer(ShellInit shellInit, 236 ShellCommandHandler shellCommandHandler, 237 ITaskOrganizerController taskOrganizerController, 238 @Nullable CompatUIHandler compatUI, 239 Optional<UnfoldAnimationController> unfoldAnimationController, 240 Optional<RecentTasksController> recentTasks, 241 ShellExecutor mainExecutor) { 242 super(taskOrganizerController, mainExecutor); 243 mShellCommandHandler = shellCommandHandler; 244 mCompatUI = compatUI; 245 mRecentTasks = recentTasks; 246 mUnfoldAnimationController = unfoldAnimationController.orElse(null); 247 if (shellInit != null) { 248 shellInit.addInitCallback(this::onInit, this); 249 } 250 } 251 onInit()252 private void onInit() { 253 mShellCommandHandler.addDumpCallback(this::dump, this); 254 if (mCompatUI != null) { 255 mCompatUI.setCallback(compatUIEvent -> { 256 switch(compatUIEvent.getEventId()) { 257 case SIZE_COMPAT_RESTART_BUTTON_APPEARED: 258 onSizeCompatRestartButtonAppeared(compatUIEvent.asType()); 259 break; 260 case SIZE_COMPAT_RESTART_BUTTON_CLICKED: 261 onSizeCompatRestartButtonClicked(compatUIEvent.asType()); 262 break; 263 default: 264 265 } 266 }); 267 } 268 registerOrganizer(); 269 } 270 271 @Override registerOrganizer()272 public List<TaskAppearedInfo> registerOrganizer() { 273 synchronized (mLock) { 274 ProtoLog.v(WM_SHELL_TASK_ORG, "Registering organizer"); 275 final List<TaskAppearedInfo> taskInfos = super.registerOrganizer(); 276 for (int i = 0; i < taskInfos.size(); i++) { 277 final TaskAppearedInfo info = taskInfos.get(i); 278 ProtoLog.v(WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s", 279 info.getTaskInfo().taskId, info.getTaskInfo().baseIntent); 280 onTaskAppeared(info); 281 } 282 return taskInfos; 283 } 284 } 285 286 @Override unregisterOrganizer()287 public void unregisterOrganizer() { 288 super.unregisterOrganizer(); 289 if (mStartingWindow != null) { 290 mStartingWindow.clearAllWindows(); 291 } 292 } 293 294 /** 295 * Creates a persistent root task in WM for a particular windowing-mode. 296 * @param displayId The display to create the root task on. 297 * @param windowingMode Windowing mode to put the root task in. 298 * @param listener The listener to get the created task callback. 299 */ createRootTask(int displayId, int windowingMode, TaskListener listener)300 public void createRootTask(int displayId, int windowingMode, TaskListener listener) { 301 createRootTask(displayId, windowingMode, listener, false /* removeWithTaskOrganizer */); 302 } 303 304 /** 305 * Creates a persistent root task in WM for a particular windowing-mode. 306 * @param displayId The display to create the root task on. 307 * @param windowingMode Windowing mode to put the root task in. 308 * @param listener The listener to get the created task callback. 309 * @param removeWithTaskOrganizer True if this task should be removed when organizer destroyed. 310 */ createRootTask(int displayId, int windowingMode, TaskListener listener, boolean removeWithTaskOrganizer)311 public void createRootTask(int displayId, int windowingMode, TaskListener listener, 312 boolean removeWithTaskOrganizer) { 313 ProtoLog.v(WM_SHELL_TASK_ORG, "createRootTask() displayId=%d winMode=%d listener=%s" , 314 displayId, windowingMode, listener.toString()); 315 final IBinder cookie = new Binder(); 316 setPendingLaunchCookieListener(cookie, listener); 317 super.createRootTask(displayId, windowingMode, cookie, removeWithTaskOrganizer); 318 } 319 320 /** 321 * @hide 322 */ initStartingWindow(StartingWindowController startingWindow)323 public void initStartingWindow(StartingWindowController startingWindow) { 324 mStartingWindow = startingWindow; 325 } 326 327 /** 328 * Adds a listener for a specific task id. 329 */ addListenerForTaskId(TaskListener listener, int taskId)330 public void addListenerForTaskId(TaskListener listener, int taskId) { 331 synchronized (mLock) { 332 ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForTaskId taskId=%s", taskId); 333 if (mTaskListeners.get(taskId) != null) { 334 throw new IllegalArgumentException( 335 "Listener for taskId=" + taskId + " already exists"); 336 } 337 338 final TaskAppearedInfo info = mTasks.get(taskId); 339 if (info == null) { 340 throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId); 341 } 342 343 final TaskListener oldListener = getTaskListener(info.getTaskInfo()); 344 mTaskListeners.put(taskId, listener); 345 updateTaskListenerIfNeeded(info.getTaskInfo(), info.getLeash(), oldListener, listener); 346 } 347 } 348 349 /** 350 * Adds a listener for tasks with given types. 351 */ addListenerForType(TaskListener listener, @TaskListenerType int... listenerTypes)352 public void addListenerForType(TaskListener listener, @TaskListenerType int... listenerTypes) { 353 synchronized (mLock) { 354 ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForType types=%s listener=%s", 355 Arrays.toString(listenerTypes), listener); 356 for (int listenerType : listenerTypes) { 357 if (mTaskListeners.get(listenerType) != null) { 358 throw new IllegalArgumentException("Listener for listenerType=" + listenerType 359 + " already exists"); 360 } 361 mTaskListeners.put(listenerType, listener); 362 } 363 364 // Notify the listener of all existing tasks with the given type. 365 for (int i = mTasks.size() - 1; i >= 0; --i) { 366 final TaskAppearedInfo data = mTasks.valueAt(i); 367 final TaskListener taskListener = getTaskListener(data.getTaskInfo()); 368 if (taskListener != listener) continue; 369 listener.onTaskAppeared(data.getTaskInfo(), data.getLeash()); 370 } 371 } 372 } 373 374 /** 375 * Removes a registered listener. 376 */ removeListener(TaskListener listener)377 public void removeListener(TaskListener listener) { 378 synchronized (mLock) { 379 ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener); 380 final int index = mTaskListeners.indexOfValue(listener); 381 if (index == -1) { 382 Log.w(TAG, "No registered listener found"); 383 return; 384 } 385 386 // Collect tasks associated with the listener we are about to remove. 387 final ArrayList<TaskAppearedInfo> tasks = new ArrayList<>(); 388 for (int i = mTasks.size() - 1; i >= 0; --i) { 389 final TaskAppearedInfo data = mTasks.valueAt(i); 390 final TaskListener taskListener = getTaskListener(data.getTaskInfo()); 391 if (taskListener != listener) continue; 392 tasks.add(data); 393 } 394 395 // Remove listener, there can be the multiple occurrences, so search the whole list. 396 for (int i = mTaskListeners.size() - 1; i >= 0; --i) { 397 if (mTaskListeners.valueAt(i) == listener) { 398 mTaskListeners.removeAt(i); 399 } 400 } 401 402 // Associate tasks with new listeners if needed. 403 for (int i = tasks.size() - 1; i >= 0; --i) { 404 final TaskAppearedInfo data = tasks.get(i); 405 updateTaskListenerIfNeeded(data.getTaskInfo(), data.getLeash(), 406 null /* oldListener already removed*/, getTaskListener(data.getTaskInfo())); 407 } 408 } 409 } 410 411 /** 412 * Associated a listener to a pending launch cookie so we can route the task later once it 413 * appears. 414 */ setPendingLaunchCookieListener(IBinder cookie, TaskListener listener)415 public void setPendingLaunchCookieListener(IBinder cookie, TaskListener listener) { 416 synchronized (mLock) { 417 mLaunchCookieToListener.put(cookie, listener); 418 } 419 } 420 421 /** 422 * Adds a listener to be notified for {@link LocusId} visibility changes. 423 */ addLocusIdListener(LocusIdListener listener)424 public void addLocusIdListener(LocusIdListener listener) { 425 synchronized (mLock) { 426 mLocusIdListeners.add(listener); 427 for (int i = 0; i < mVisibleTasksWithLocusId.size(); i++) { 428 listener.onVisibilityChanged(mVisibleTasksWithLocusId.keyAt(i), 429 mVisibleTasksWithLocusId.valueAt(i), true /* visible */); 430 } 431 } 432 } 433 434 /** 435 * Removes a locus id listener. 436 */ removeLocusIdListener(LocusIdListener listener)437 public void removeLocusIdListener(LocusIdListener listener) { 438 synchronized (mLock) { 439 mLocusIdListeners.remove(listener); 440 } 441 } 442 443 /** 444 * Adds a listener to be notified for task focus changes. 445 */ addFocusListener(FocusListener listener)446 public void addFocusListener(FocusListener listener) { 447 synchronized (mLock) { 448 mFocusListeners.add(listener); 449 if (mLastFocusedTaskInfo != null) { 450 listener.onFocusTaskChanged(mLastFocusedTaskInfo); 451 } 452 } 453 } 454 455 /** 456 * Removes a focus listener. 457 */ removeFocusListener(FocusListener listener)458 public void removeFocusListener(FocusListener listener) { 459 synchronized (mLock) { 460 mFocusListeners.remove(listener); 461 } 462 } 463 464 /** 465 * Adds a listener to be notified when a task vanishes. 466 */ addTaskVanishedListener(TaskVanishedListener listener)467 public void addTaskVanishedListener(TaskVanishedListener listener) { 468 synchronized (mLock) { 469 mTaskVanishedListeners.add(listener); 470 } 471 } 472 473 /** 474 * Removes a task-vanished listener. 475 */ removeTaskVanishedListener(TaskVanishedListener listener)476 public void removeTaskVanishedListener(TaskVanishedListener listener) { 477 synchronized (mLock) { 478 mTaskVanishedListeners.remove(listener); 479 } 480 } 481 482 /** 483 * Returns a surface which can be used to attach overlays to the home root task 484 */ 485 @NonNull getHomeTaskOverlayContainer()486 public SurfaceControl getHomeTaskOverlayContainer() { 487 return mHomeTaskOverlayContainer; 488 } 489 490 /** 491 * Returns the home task surface, not for wide use. 492 */ 493 @Nullable getHomeTaskSurface()494 public SurfaceControl getHomeTaskSurface() { 495 for (int i = 0; i < mTasks.size(); i++) { 496 final TaskAppearedInfo info = mTasks.valueAt(i); 497 if (info.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) { 498 return info.getLeash(); 499 } 500 } 501 return null; 502 } 503 504 @Override addStartingWindow(StartingWindowInfo info)505 public void addStartingWindow(StartingWindowInfo info) { 506 if (mStartingWindow != null) { 507 mStartingWindow.addStartingWindow(info); 508 } 509 } 510 511 @Override removeStartingWindow(StartingWindowRemovalInfo removalInfo)512 public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { 513 if (mStartingWindow != null) { 514 mStartingWindow.removeStartingWindow(removalInfo); 515 } 516 } 517 518 @Override copySplashScreenView(int taskId)519 public void copySplashScreenView(int taskId) { 520 if (mStartingWindow != null) { 521 mStartingWindow.copySplashScreenView(taskId); 522 } 523 } 524 525 @Override onAppSplashScreenViewRemoved(int taskId)526 public void onAppSplashScreenViewRemoved(int taskId) { 527 if (mStartingWindow != null) { 528 mStartingWindow.onAppSplashScreenViewRemoved(taskId); 529 } 530 } 531 532 @Override onImeDrawnOnTask(int taskId)533 public void onImeDrawnOnTask(int taskId) { 534 if (mStartingWindow != null) { 535 mStartingWindow.onImeDrawnOnTask(taskId); 536 } 537 } 538 539 @Override onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash)540 public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { 541 if (leash != null) { 542 leash.setUnreleasedWarningCallSite("ShellTaskOrganizer.onTaskAppeared"); 543 } 544 synchronized (mLock) { 545 onTaskAppeared(new TaskAppearedInfo(taskInfo, leash)); 546 } 547 } 548 onTaskAppeared(TaskAppearedInfo info)549 private void onTaskAppeared(TaskAppearedInfo info) { 550 final int taskId = info.getTaskInfo().taskId; 551 mTasks.put(taskId, info); 552 final TaskListener listener = 553 getTaskListener(info.getTaskInfo(), true /*removeLaunchCookieIfNeeded*/); 554 ProtoLog.v(WM_SHELL_TASK_ORG, "Task appeared taskId=%d listener=%s", taskId, listener); 555 if (listener != null) { 556 listener.onTaskAppeared(info.getTaskInfo(), info.getLeash()); 557 } 558 if (mUnfoldAnimationController != null) { 559 mUnfoldAnimationController.onTaskAppeared(info.getTaskInfo(), info.getLeash()); 560 } 561 562 if (info.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) { 563 ProtoLog.v(WM_SHELL_TASK_ORG, "Adding overlay to home task"); 564 final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 565 t.setLayer(mHomeTaskOverlayContainer, Integer.MAX_VALUE); 566 t.reparent(mHomeTaskOverlayContainer, info.getLeash()); 567 t.apply(); 568 } 569 570 notifyLocusVisibilityIfNeeded(info.getTaskInfo()); 571 notifyCompatUI(info.getTaskInfo(), listener); 572 mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskAdded(info.getTaskInfo())); 573 } 574 575 @Override onTaskInfoChanged(RunningTaskInfo taskInfo)576 public void onTaskInfoChanged(RunningTaskInfo taskInfo) { 577 synchronized (mLock) { 578 ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId); 579 580 if (mUnfoldAnimationController != null) { 581 mUnfoldAnimationController.onTaskInfoChanged(taskInfo); 582 } 583 584 final TaskAppearedInfo data = mTasks.get(taskInfo.taskId); 585 final TaskListener oldListener = getTaskListener(data.getTaskInfo()); 586 final TaskListener newListener = getTaskListener(taskInfo); 587 mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash())); 588 final boolean updated = updateTaskListenerIfNeeded( 589 taskInfo, data.getLeash(), oldListener, newListener); 590 if (!updated && newListener != null) { 591 newListener.onTaskInfoChanged(taskInfo); 592 } 593 notifyLocusVisibilityIfNeeded(taskInfo); 594 if (updated || !taskInfo.equalsForCompatUi(data.getTaskInfo())) { 595 // Notify the compat UI if the listener or task info changed. 596 notifyCompatUI(taskInfo, newListener); 597 } 598 final boolean windowModeChanged = 599 data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode(); 600 if (windowModeChanged 601 || hasFreeformConfigurationChanged(data.getTaskInfo(), taskInfo)) { 602 mRecentTasks.ifPresent(recentTasks -> 603 recentTasks.onTaskRunningInfoChanged(taskInfo)); 604 } 605 // TODO (b/207687679): Remove check for HOME once bug is fixed 606 final boolean isFocusedOrHome = taskInfo.isFocused 607 || (taskInfo.topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME 608 && taskInfo.isVisible); 609 final boolean focusTaskChanged = (mLastFocusedTaskInfo == null 610 || mLastFocusedTaskInfo.taskId != taskInfo.taskId 611 || mLastFocusedTaskInfo.getWindowingMode() != taskInfo.getWindowingMode()) 612 && isFocusedOrHome; 613 if (focusTaskChanged) { 614 for (int i = 0; i < mFocusListeners.size(); i++) { 615 mFocusListeners.valueAt(i).onFocusTaskChanged(taskInfo); 616 } 617 mLastFocusedTaskInfo = taskInfo; 618 } 619 } 620 } 621 hasFreeformConfigurationChanged(RunningTaskInfo oldTaskInfo, RunningTaskInfo newTaskInfo)622 private boolean hasFreeformConfigurationChanged(RunningTaskInfo oldTaskInfo, 623 RunningTaskInfo newTaskInfo) { 624 if (newTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) { 625 return false; 626 } 627 return oldTaskInfo.isVisible != newTaskInfo.isVisible 628 || !oldTaskInfo.positionInParent.equals(newTaskInfo.positionInParent) 629 || !Objects.equals(oldTaskInfo.configuration.windowConfiguration.getAppBounds(), 630 newTaskInfo.configuration.windowConfiguration.getAppBounds()); 631 } 632 633 @Override onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)634 public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { 635 synchronized (mLock) { 636 ProtoLog.v(WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", taskInfo.taskId); 637 final TaskListener listener = getTaskListener(taskInfo); 638 if (listener != null) { 639 listener.onBackPressedOnTaskRoot(taskInfo); 640 } 641 } 642 } 643 644 @Override onTaskVanished(RunningTaskInfo taskInfo)645 public void onTaskVanished(RunningTaskInfo taskInfo) { 646 synchronized (mLock) { 647 ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId); 648 if (mUnfoldAnimationController != null) { 649 mUnfoldAnimationController.onTaskVanished(taskInfo); 650 } 651 652 final int taskId = taskInfo.taskId; 653 final TaskAppearedInfo appearedInfo = mTasks.get(taskId); 654 final TaskListener listener = getTaskListener(appearedInfo.getTaskInfo()); 655 mTasks.remove(taskId); 656 if (listener != null) { 657 listener.onTaskVanished(taskInfo); 658 } 659 notifyLocusVisibilityIfNeeded(taskInfo); 660 // Pass null for listener to remove the compat UI on this task if there is any. 661 notifyCompatUI(taskInfo, null /* taskListener */); 662 // Notify the recent tasks that a task has been removed 663 mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo)); 664 if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) { 665 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 666 t.reparent(mHomeTaskOverlayContainer, null); 667 t.apply(); 668 ProtoLog.v(WM_SHELL_TASK_ORG, "Removing overlay surface"); 669 } 670 for (TaskVanishedListener l : mTaskVanishedListeners) { 671 l.onTaskVanished(taskInfo); 672 } 673 674 if (!ENABLE_SHELL_TRANSITIONS && (appearedInfo.getLeash() != null)) { 675 // Preemptively clean up the leash only if shell transitions are not enabled 676 appearedInfo.getLeash().release(); 677 } 678 } 679 } 680 681 /** 682 * Return list of {@link RunningTaskInfo}s for the given display. 683 * 684 * @return filtered list of tasks or empty list 685 */ getRunningTasks(int displayId)686 public ArrayList<RunningTaskInfo> getRunningTasks(int displayId) { 687 ArrayList<RunningTaskInfo> result = new ArrayList<>(); 688 for (int i = 0; i < mTasks.size(); i++) { 689 RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo(); 690 if (taskInfo.displayId == displayId) { 691 result.add(taskInfo); 692 } 693 } 694 return result; 695 } 696 697 /** Return list of {@link RunningTaskInfo}s on all the displays. */ getRunningTasks()698 public ArrayList<RunningTaskInfo> getRunningTasks() { 699 ArrayList<RunningTaskInfo> result = new ArrayList<>(); 700 for (int i = 0; i < mTasks.size(); i++) { 701 result.add(mTasks.valueAt(i).getTaskInfo()); 702 } 703 return result; 704 } 705 706 /** Gets running task by taskId. Returns {@code null} if no such task observed. */ 707 @Nullable getRunningTaskInfo(int taskId)708 public RunningTaskInfo getRunningTaskInfo(int taskId) { 709 synchronized (mLock) { 710 final TaskAppearedInfo info = mTasks.get(taskId); 711 return info != null ? info.getTaskInfo() : null; 712 } 713 } 714 715 /** 716 * Shows/hides the given task surface. Not for general use as changing the task visibility may 717 * conflict with other Transitions. This is currently ONLY used to temporarily hide a task 718 * while a drag is in session. 719 */ setTaskSurfaceVisibility(int taskId, boolean visible)720 public void setTaskSurfaceVisibility(int taskId, boolean visible) { 721 synchronized (mLock) { 722 final TaskAppearedInfo info = mTasks.get(taskId); 723 if (info != null) { 724 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 725 t.setVisibility(info.getLeash(), visible); 726 t.apply(); 727 } 728 } 729 } 730 updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash, TaskListener oldListener, TaskListener newListener)731 private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash, 732 TaskListener oldListener, TaskListener newListener) { 733 if (oldListener == newListener) return false; 734 // TODO: We currently send vanished/appeared as the task moves between types, but 735 // we should consider adding a different mode-changed callback 736 if (oldListener != null) { 737 oldListener.onTaskVanished(taskInfo); 738 } 739 if (newListener != null) { 740 newListener.onTaskAppeared(taskInfo, leash); 741 } 742 return true; 743 } 744 notifyLocusVisibilityIfNeeded(TaskInfo taskInfo)745 private void notifyLocusVisibilityIfNeeded(TaskInfo taskInfo) { 746 final int taskId = taskInfo.taskId; 747 final LocusId prevLocus = mVisibleTasksWithLocusId.get(taskId); 748 final boolean sameLocus = Objects.equals(prevLocus, taskInfo.mTopActivityLocusId); 749 if (prevLocus == null) { 750 // New visible locus 751 if (taskInfo.mTopActivityLocusId != null && taskInfo.isVisible) { 752 mVisibleTasksWithLocusId.put(taskId, taskInfo.mTopActivityLocusId); 753 notifyLocusIdChange(taskId, taskInfo.mTopActivityLocusId, true /* visible */); 754 } 755 } else if (sameLocus && !taskInfo.isVisible) { 756 // Hidden locus 757 mVisibleTasksWithLocusId.remove(taskId); 758 notifyLocusIdChange(taskId, taskInfo.mTopActivityLocusId, false /* visible */); 759 } else if (!sameLocus) { 760 // Changed locus 761 if (taskInfo.isVisible) { 762 mVisibleTasksWithLocusId.put(taskId, taskInfo.mTopActivityLocusId); 763 notifyLocusIdChange(taskId, prevLocus, false /* visible */); 764 notifyLocusIdChange(taskId, taskInfo.mTopActivityLocusId, true /* visible */); 765 } else { 766 mVisibleTasksWithLocusId.remove(taskInfo.taskId); 767 notifyLocusIdChange(taskId, prevLocus, false /* visible */); 768 } 769 } 770 } 771 notifyLocusIdChange(int taskId, LocusId locus, boolean visible)772 private void notifyLocusIdChange(int taskId, LocusId locus, boolean visible) { 773 for (int i = 0; i < mLocusIdListeners.size(); i++) { 774 mLocusIdListeners.valueAt(i).onVisibilityChanged(taskId, locus, visible); 775 } 776 } 777 778 /** Reparents a child window surface to the task surface. */ reparentChildSurfaceToTask(int taskId, SurfaceControl sc, SurfaceControl.Transaction t)779 public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, 780 SurfaceControl.Transaction t) { 781 final TaskListener taskListener; 782 synchronized (mLock) { 783 taskListener = mTasks.contains(taskId) 784 ? getTaskListener(mTasks.get(taskId).getTaskInfo()) 785 : null; 786 } 787 if (taskListener == null) { 788 ProtoLog.w(WM_SHELL_TASK_ORG, "Failed to find Task to reparent surface taskId=%d", 789 taskId); 790 return; 791 } 792 taskListener.reparentChildSurfaceToTask(taskId, sc, t); 793 } 794 795 @VisibleForTesting onSizeCompatRestartButtonAppeared(@onNull SizeCompatRestartButtonAppeared compatUIEvent)796 void onSizeCompatRestartButtonAppeared(@NonNull SizeCompatRestartButtonAppeared compatUIEvent) { 797 final int taskId = compatUIEvent.getTaskId(); 798 final TaskAppearedInfo info; 799 synchronized (mLock) { 800 info = mTasks.get(taskId); 801 } 802 if (info == null) { 803 return; 804 } 805 logSizeCompatRestartButtonEventReported(info, 806 FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED__EVENT__APPEARED); 807 } 808 809 @VisibleForTesting onSizeCompatRestartButtonClicked(@onNull SizeCompatRestartButtonClicked compatUIEvent)810 void onSizeCompatRestartButtonClicked(@NonNull SizeCompatRestartButtonClicked compatUIEvent) { 811 final int taskId = compatUIEvent.getTaskId(); 812 final TaskAppearedInfo info; 813 synchronized (mLock) { 814 info = mTasks.get(taskId); 815 } 816 if (info == null) { 817 return; 818 } 819 logSizeCompatRestartButtonEventReported(info, 820 FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED__EVENT__CLICKED); 821 restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token); 822 } 823 logSizeCompatRestartButtonEventReported(@onNull TaskAppearedInfo info, int event)824 private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info, 825 int event) { 826 ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo; 827 if (topActivityInfo == null) { 828 return; 829 } 830 FrameworkStatsLog.write(FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED, 831 topActivityInfo.applicationInfo.uid, event); 832 } 833 834 /** 835 * Notifies {@link CompatUIController} about the compat info changed on the give Task 836 * to update the UI accordingly. 837 * 838 * @param taskInfo the new Task info 839 * @param taskListener listener to handle the Task Surface placement. {@code null} if task is 840 * vanished. 841 */ notifyCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener)842 private void notifyCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) { 843 if (mCompatUI == null) { 844 return; 845 } 846 847 // The task is vanished or doesn't support compat UI, notify to remove compat UI 848 // on this Task if there is any. 849 if (taskListener == null || !taskListener.supportCompatUI() 850 || !taskInfo.appCompatTaskInfo.hasCompatUI() || !taskInfo.isVisible) { 851 mCompatUI.onCompatInfoChanged(new CompatUIInfo(taskInfo, null /* taskListener */)); 852 return; 853 } 854 mCompatUI.onCompatInfoChanged(new CompatUIInfo(taskInfo, taskListener)); 855 } 856 getTaskListener(RunningTaskInfo runningTaskInfo)857 private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) { 858 return getTaskListener(runningTaskInfo, false /*removeLaunchCookieIfNeeded*/); 859 } 860 getTaskListener(RunningTaskInfo runningTaskInfo, boolean removeLaunchCookieIfNeeded)861 private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo, 862 boolean removeLaunchCookieIfNeeded) { 863 864 final int taskId = runningTaskInfo.taskId; 865 TaskListener listener; 866 867 // First priority goes to listener that might be pending for this task. 868 final ArrayList<IBinder> launchCookies = runningTaskInfo.launchCookies; 869 for (int i = launchCookies.size() - 1; i >= 0; --i) { 870 final IBinder cookie = launchCookies.get(i); 871 listener = mLaunchCookieToListener.get(cookie); 872 if (listener == null) continue; 873 874 if (removeLaunchCookieIfNeeded) { 875 // Remove the cookie and add the listener. 876 mLaunchCookieToListener.remove(cookie); 877 mTaskListeners.put(taskId, listener); 878 } 879 return listener; 880 } 881 882 // Next priority goes to taskId specific listeners. 883 listener = mTaskListeners.get(taskId); 884 if (listener != null) return listener; 885 886 // Next priority goes to the listener listening to its parent. 887 if (runningTaskInfo.hasParentTask()) { 888 listener = mTaskListeners.get(runningTaskInfo.parentTaskId); 889 if (listener != null) return listener; 890 } 891 892 // Next we try type specific listeners. 893 final int taskListenerType = taskInfoToTaskListenerType(runningTaskInfo); 894 return mTaskListeners.get(taskListenerType); 895 } 896 897 @VisibleForTesting taskInfoToTaskListenerType(RunningTaskInfo runningTaskInfo)898 static @TaskListenerType int taskInfoToTaskListenerType(RunningTaskInfo runningTaskInfo) { 899 switch (runningTaskInfo.getWindowingMode()) { 900 case WINDOWING_MODE_FULLSCREEN: 901 return TASK_LISTENER_TYPE_FULLSCREEN; 902 case WINDOWING_MODE_MULTI_WINDOW: 903 return TASK_LISTENER_TYPE_MULTI_WINDOW; 904 case WINDOWING_MODE_PINNED: 905 return TASK_LISTENER_TYPE_PIP; 906 case WINDOWING_MODE_FREEFORM: 907 return TASK_LISTENER_TYPE_FREEFORM; 908 case WINDOWING_MODE_UNDEFINED: 909 default: 910 return TASK_LISTENER_TYPE_UNDEFINED; 911 } 912 } 913 taskListenerTypeToString(@askListenerType int type)914 public static String taskListenerTypeToString(@TaskListenerType int type) { 915 switch (type) { 916 case TASK_LISTENER_TYPE_FULLSCREEN: 917 return "TASK_LISTENER_TYPE_FULLSCREEN"; 918 case TASK_LISTENER_TYPE_MULTI_WINDOW: 919 return "TASK_LISTENER_TYPE_MULTI_WINDOW"; 920 case TASK_LISTENER_TYPE_PIP: 921 return "TASK_LISTENER_TYPE_PIP"; 922 case TASK_LISTENER_TYPE_FREEFORM: 923 return "TASK_LISTENER_TYPE_FREEFORM"; 924 case TASK_LISTENER_TYPE_UNDEFINED: 925 return "TASK_LISTENER_TYPE_UNDEFINED"; 926 default: 927 return "taskId#" + type; 928 } 929 } 930 dump(@onNull PrintWriter pw, String prefix)931 public void dump(@NonNull PrintWriter pw, String prefix) { 932 synchronized (mLock) { 933 final String innerPrefix = prefix + " "; 934 final String childPrefix = innerPrefix + " "; 935 pw.println(prefix + TAG); 936 pw.println(innerPrefix + mTaskListeners.size() + " Listeners"); 937 for (int i = mTaskListeners.size() - 1; i >= 0; --i) { 938 final int key = mTaskListeners.keyAt(i); 939 final TaskListener listener = mTaskListeners.valueAt(i); 940 pw.println(innerPrefix + "#" + i + " " + taskListenerTypeToString(key)); 941 listener.dump(pw, childPrefix); 942 } 943 944 pw.println(); 945 pw.println(innerPrefix + mTasks.size() + " Tasks"); 946 for (int i = mTasks.size() - 1; i >= 0; --i) { 947 final int key = mTasks.keyAt(i); 948 final TaskAppearedInfo info = mTasks.valueAt(i); 949 final TaskListener listener = getTaskListener(info.getTaskInfo()); 950 final int windowingMode = info.getTaskInfo().getWindowingMode(); 951 String pkg = ""; 952 if (info.getTaskInfo().baseActivity != null) { 953 pkg = info.getTaskInfo().baseActivity.getPackageName(); 954 } 955 Rect bounds = info.getTaskInfo().getConfiguration().windowConfiguration.getBounds(); 956 boolean running = info.getTaskInfo().isRunning; 957 boolean visible = info.getTaskInfo().isVisible; 958 boolean focused = info.getTaskInfo().isFocused; 959 pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener 960 + " wmMode=" + windowingMode + " pkg=" + pkg + " bounds=" + bounds 961 + " running=" + running + " visible=" + visible + " focused=" + focused); 962 } 963 964 pw.println(); 965 pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies"); 966 for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) { 967 final IBinder key = mLaunchCookieToListener.keyAt(i); 968 final TaskListener listener = mLaunchCookieToListener.valueAt(i); 969 pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener); 970 } 971 972 } 973 } 974 } 975