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