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