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 27 import android.annotation.IntDef; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.app.ActivityManager.RunningTaskInfo; 31 import android.app.TaskInfo; 32 import android.content.Context; 33 import android.content.LocusId; 34 import android.graphics.Rect; 35 import android.os.Binder; 36 import android.os.IBinder; 37 import android.util.ArrayMap; 38 import android.util.ArraySet; 39 import android.util.Log; 40 import android.util.SparseArray; 41 import android.view.SurfaceControl; 42 import android.window.ITaskOrganizerController; 43 import android.window.StartingWindowInfo; 44 import android.window.TaskAppearedInfo; 45 import android.window.TaskOrganizer; 46 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.protolog.common.ProtoLog; 49 import com.android.wm.shell.common.ScreenshotUtils; 50 import com.android.wm.shell.common.ShellExecutor; 51 import com.android.wm.shell.sizecompatui.SizeCompatUIController; 52 import com.android.wm.shell.startingsurface.StartingWindowController; 53 54 import java.io.PrintWriter; 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.List; 58 import java.util.Objects; 59 import java.util.function.Consumer; 60 61 /** 62 * Unified task organizer for all components in the shell. 63 * TODO(b/167582004): may consider consolidating this class and TaskOrganizer 64 */ 65 public class ShellTaskOrganizer extends TaskOrganizer implements 66 SizeCompatUIController.SizeCompatUICallback { 67 68 // Intentionally using negative numbers here so the positive numbers can be used 69 // for task id specific listeners that will be added later. 70 public static final int TASK_LISTENER_TYPE_UNDEFINED = -1; 71 public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2; 72 public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3; 73 public static final int TASK_LISTENER_TYPE_PIP = -4; 74 75 @IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = { 76 TASK_LISTENER_TYPE_UNDEFINED, 77 TASK_LISTENER_TYPE_FULLSCREEN, 78 TASK_LISTENER_TYPE_MULTI_WINDOW, 79 TASK_LISTENER_TYPE_PIP, 80 }) 81 public @interface TaskListenerType {} 82 83 private static final String TAG = "ShellTaskOrganizer"; 84 85 /** 86 * Callbacks for when the tasks change in the system. 87 */ 88 public interface TaskListener { onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash)89 default void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {} onTaskInfoChanged(RunningTaskInfo taskInfo)90 default void onTaskInfoChanged(RunningTaskInfo taskInfo) {} onTaskVanished(RunningTaskInfo taskInfo)91 default void onTaskVanished(RunningTaskInfo taskInfo) {} onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)92 default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {} 93 /** Whether this task listener supports size compat UI. */ supportSizeCompatUI()94 default boolean supportSizeCompatUI() { 95 // All TaskListeners should support size compat except PIP. 96 return true; 97 } 98 /** Attaches the a child window surface to the task surface. */ attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b)99 default void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { 100 throw new IllegalStateException( 101 "This task listener doesn't support child surface attachment."); 102 } dump(@onNull PrintWriter pw, String prefix)103 default void dump(@NonNull PrintWriter pw, String prefix) {}; 104 } 105 106 /** 107 * Callbacks for events on a task with a locus id. 108 */ 109 public interface LocusIdListener { 110 /** 111 * Notifies when a task with a locusId becomes visible, when a visible task's locusId 112 * changes, or if a previously visible task with a locusId becomes invisible. 113 */ onVisibilityChanged(int taskId, LocusId locus, boolean visible)114 void onVisibilityChanged(int taskId, LocusId locus, boolean visible); 115 } 116 117 /** 118 * Keys map from either a task id or {@link TaskListenerType}. 119 * @see #addListenerForTaskId 120 * @see #addListenerForType 121 */ 122 private final SparseArray<TaskListener> mTaskListeners = new SparseArray<>(); 123 124 // Keeps track of all the tasks reported to this organizer (changes in windowing mode will 125 // require us to report to both old and new listeners) 126 private final SparseArray<TaskAppearedInfo> mTasks = new SparseArray<>(); 127 128 /** @see #setPendingLaunchCookieListener */ 129 private final ArrayMap<IBinder, TaskListener> mLaunchCookieToListener = new ArrayMap<>(); 130 131 // Keeps track of taskId's with visible locusIds. Used to notify any {@link LocusIdListener}s 132 // that might be set. 133 private final SparseArray<LocusId> mVisibleTasksWithLocusId = new SparseArray<>(); 134 135 /** @see #addLocusIdListener */ 136 private final ArraySet<LocusIdListener> mLocusIdListeners = new ArraySet<>(); 137 138 private final Object mLock = new Object(); 139 private StartingWindowController mStartingWindow; 140 141 /** 142 * In charge of showing size compat UI. Can be {@code null} if device doesn't support size 143 * compat. 144 */ 145 @Nullable 146 private final SizeCompatUIController mSizeCompatUI; 147 ShellTaskOrganizer(ShellExecutor mainExecutor, Context context)148 public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) { 149 this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */); 150 } 151 ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable SizeCompatUIController sizeCompatUI)152 public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable 153 SizeCompatUIController sizeCompatUI) { 154 this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI); 155 } 156 157 @VisibleForTesting ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor, Context context, @Nullable SizeCompatUIController sizeCompatUI)158 ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor, 159 Context context, @Nullable SizeCompatUIController sizeCompatUI) { 160 super(taskOrganizerController, mainExecutor); 161 mSizeCompatUI = sizeCompatUI; 162 if (sizeCompatUI != null) { 163 sizeCompatUI.setSizeCompatUICallback(this); 164 } 165 } 166 167 @Override registerOrganizer()168 public List<TaskAppearedInfo> registerOrganizer() { 169 synchronized (mLock) { 170 ProtoLog.v(WM_SHELL_TASK_ORG, "Registering organizer"); 171 final List<TaskAppearedInfo> taskInfos = super.registerOrganizer(); 172 for (int i = 0; i < taskInfos.size(); i++) { 173 final TaskAppearedInfo info = taskInfos.get(i); 174 ProtoLog.v(WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s", 175 info.getTaskInfo().taskId, info.getTaskInfo().baseIntent); 176 onTaskAppeared(info); 177 } 178 return taskInfos; 179 } 180 } 181 createRootTask(int displayId, int windowingMode, TaskListener listener)182 public void createRootTask(int displayId, int windowingMode, TaskListener listener) { 183 ProtoLog.v(WM_SHELL_TASK_ORG, "createRootTask() displayId=%d winMode=%d listener=%s", 184 displayId, windowingMode, listener.toString()); 185 final IBinder cookie = new Binder(); 186 setPendingLaunchCookieListener(cookie, listener); 187 super.createRootTask(displayId, windowingMode, cookie); 188 } 189 190 /** 191 * @hide 192 */ initStartingWindow(StartingWindowController startingWindow)193 public void initStartingWindow(StartingWindowController startingWindow) { 194 mStartingWindow = startingWindow; 195 } 196 197 /** 198 * Adds a listener for a specific task id. 199 */ addListenerForTaskId(TaskListener listener, int taskId)200 public void addListenerForTaskId(TaskListener listener, int taskId) { 201 synchronized (mLock) { 202 ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForTaskId taskId=%s", taskId); 203 if (mTaskListeners.get(taskId) != null) { 204 throw new IllegalArgumentException( 205 "Listener for taskId=" + taskId + " already exists"); 206 } 207 208 final TaskAppearedInfo info = mTasks.get(taskId); 209 if (info == null) { 210 throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId); 211 } 212 213 final TaskListener oldListener = getTaskListener(info.getTaskInfo()); 214 mTaskListeners.put(taskId, listener); 215 updateTaskListenerIfNeeded(info.getTaskInfo(), info.getLeash(), oldListener, listener); 216 } 217 } 218 219 /** 220 * Adds a listener for tasks with given types. 221 */ addListenerForType(TaskListener listener, @TaskListenerType int... listenerTypes)222 public void addListenerForType(TaskListener listener, @TaskListenerType int... listenerTypes) { 223 synchronized (mLock) { 224 ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForType types=%s listener=%s", 225 Arrays.toString(listenerTypes), listener); 226 for (int listenerType : listenerTypes) { 227 if (mTaskListeners.get(listenerType) != null) { 228 throw new IllegalArgumentException("Listener for listenerType=" + listenerType 229 + " already exists"); 230 } 231 mTaskListeners.put(listenerType, listener); 232 233 // Notify the listener of all existing tasks with the given type. 234 for (int i = mTasks.size() - 1; i >= 0; --i) { 235 final TaskAppearedInfo data = mTasks.valueAt(i); 236 final TaskListener taskListener = getTaskListener(data.getTaskInfo()); 237 if (taskListener != listener) continue; 238 listener.onTaskAppeared(data.getTaskInfo(), data.getLeash()); 239 } 240 } 241 } 242 } 243 244 /** 245 * Removes a registered listener. 246 */ removeListener(TaskListener listener)247 public void removeListener(TaskListener listener) { 248 synchronized (mLock) { 249 ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener); 250 final int index = mTaskListeners.indexOfValue(listener); 251 if (index == -1) { 252 Log.w(TAG, "No registered listener found"); 253 return; 254 } 255 256 // Collect tasks associated with the listener we are about to remove. 257 final ArrayList<TaskAppearedInfo> tasks = new ArrayList<>(); 258 for (int i = mTasks.size() - 1; i >= 0; --i) { 259 final TaskAppearedInfo data = mTasks.valueAt(i); 260 final TaskListener taskListener = getTaskListener(data.getTaskInfo()); 261 if (taskListener != listener) continue; 262 tasks.add(data); 263 } 264 265 // Remove listener 266 mTaskListeners.removeAt(index); 267 268 // Associate tasks with new listeners if needed. 269 for (int i = tasks.size() - 1; i >= 0; --i) { 270 final TaskAppearedInfo data = tasks.get(i); 271 updateTaskListenerIfNeeded(data.getTaskInfo(), data.getLeash(), 272 null /* oldListener already removed*/, getTaskListener(data.getTaskInfo())); 273 } 274 } 275 } 276 277 /** 278 * Associated a listener to a pending launch cookie so we can route the task later once it 279 * appears. 280 */ setPendingLaunchCookieListener(IBinder cookie, TaskListener listener)281 public void setPendingLaunchCookieListener(IBinder cookie, TaskListener listener) { 282 synchronized (mLock) { 283 mLaunchCookieToListener.put(cookie, listener); 284 } 285 } 286 287 /** 288 * Adds a listener to be notified for {@link LocusId} visibility changes. 289 */ addLocusIdListener(LocusIdListener listener)290 public void addLocusIdListener(LocusIdListener listener) { 291 synchronized (mLock) { 292 mLocusIdListeners.add(listener); 293 for (int i = 0; i < mVisibleTasksWithLocusId.size(); i++) { 294 listener.onVisibilityChanged(mVisibleTasksWithLocusId.keyAt(i), 295 mVisibleTasksWithLocusId.valueAt(i), true /* visible */); 296 } 297 } 298 } 299 300 /** 301 * Removes listener. 302 */ removeLocusIdListener(LocusIdListener listener)303 public void removeLocusIdListener(LocusIdListener listener) { 304 synchronized (mLock) { 305 mLocusIdListeners.remove(listener); 306 } 307 } 308 309 @Override addStartingWindow(StartingWindowInfo info, IBinder appToken)310 public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { 311 if (mStartingWindow != null) { 312 mStartingWindow.addStartingWindow(info, appToken); 313 } 314 } 315 316 @Override removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, boolean playRevealAnimation)317 public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, 318 boolean playRevealAnimation) { 319 if (mStartingWindow != null) { 320 mStartingWindow.removeStartingWindow(taskId, leash, frame, playRevealAnimation); 321 } 322 } 323 324 @Override copySplashScreenView(int taskId)325 public void copySplashScreenView(int taskId) { 326 if (mStartingWindow != null) { 327 mStartingWindow.copySplashScreenView(taskId); 328 } 329 } 330 331 @Override onAppSplashScreenViewRemoved(int taskId)332 public void onAppSplashScreenViewRemoved(int taskId) { 333 if (mStartingWindow != null) { 334 mStartingWindow.onAppSplashScreenViewRemoved(taskId); 335 } 336 } 337 338 @Override onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash)339 public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { 340 synchronized (mLock) { 341 onTaskAppeared(new TaskAppearedInfo(taskInfo, leash)); 342 } 343 } 344 onTaskAppeared(TaskAppearedInfo info)345 private void onTaskAppeared(TaskAppearedInfo info) { 346 final int taskId = info.getTaskInfo().taskId; 347 mTasks.put(taskId, info); 348 final TaskListener listener = 349 getTaskListener(info.getTaskInfo(), true /*removeLaunchCookieIfNeeded*/); 350 ProtoLog.v(WM_SHELL_TASK_ORG, "Task appeared taskId=%d listener=%s", taskId, listener); 351 if (listener != null) { 352 listener.onTaskAppeared(info.getTaskInfo(), info.getLeash()); 353 } 354 notifyLocusVisibilityIfNeeded(info.getTaskInfo()); 355 notifySizeCompatUI(info.getTaskInfo(), listener); 356 } 357 358 /** 359 * Take a screenshot of a task. 360 */ screenshotTask(RunningTaskInfo taskInfo, Rect crop, Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer)361 public void screenshotTask(RunningTaskInfo taskInfo, Rect crop, 362 Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) { 363 final TaskAppearedInfo info = mTasks.get(taskInfo.taskId); 364 if (info == null) { 365 return; 366 } 367 ScreenshotUtils.captureLayer(info.getLeash(), crop, consumer); 368 } 369 370 371 @Override onTaskInfoChanged(RunningTaskInfo taskInfo)372 public void onTaskInfoChanged(RunningTaskInfo taskInfo) { 373 synchronized (mLock) { 374 ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId); 375 final TaskAppearedInfo data = mTasks.get(taskInfo.taskId); 376 final TaskListener oldListener = getTaskListener(data.getTaskInfo()); 377 final TaskListener newListener = getTaskListener(taskInfo); 378 mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash())); 379 final boolean updated = updateTaskListenerIfNeeded( 380 taskInfo, data.getLeash(), oldListener, newListener); 381 if (!updated && newListener != null) { 382 newListener.onTaskInfoChanged(taskInfo); 383 } 384 notifyLocusVisibilityIfNeeded(taskInfo); 385 if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) { 386 // Notify the size compat UI if the listener or task info changed. 387 notifySizeCompatUI(taskInfo, newListener); 388 } 389 } 390 } 391 392 @Override onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)393 public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { 394 synchronized (mLock) { 395 ProtoLog.v(WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", taskInfo.taskId); 396 final TaskListener listener = getTaskListener(taskInfo); 397 if (listener != null) { 398 listener.onBackPressedOnTaskRoot(taskInfo); 399 } 400 } 401 } 402 403 @Override onTaskVanished(RunningTaskInfo taskInfo)404 public void onTaskVanished(RunningTaskInfo taskInfo) { 405 synchronized (mLock) { 406 ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId); 407 final int taskId = taskInfo.taskId; 408 final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo()); 409 mTasks.remove(taskId); 410 if (listener != null) { 411 listener.onTaskVanished(taskInfo); 412 } 413 notifyLocusVisibilityIfNeeded(taskInfo); 414 // Pass null for listener to remove the size compat UI on this task if there is any. 415 notifySizeCompatUI(taskInfo, null /* taskListener */); 416 } 417 } 418 419 /** Gets running task by taskId. Returns {@code null} if no such task observed. */ 420 @Nullable getRunningTaskInfo(int taskId)421 public RunningTaskInfo getRunningTaskInfo(int taskId) { 422 synchronized (mLock) { 423 final TaskAppearedInfo info = mTasks.get(taskId); 424 return info != null ? info.getTaskInfo() : null; 425 } 426 } 427 428 /** Helper to set int metadata on the Surface corresponding to the task id. */ setSurfaceMetadata(int taskId, int key, int value)429 public void setSurfaceMetadata(int taskId, int key, int value) { 430 synchronized (mLock) { 431 final TaskAppearedInfo info = mTasks.get(taskId); 432 if (info == null || info.getLeash() == null) { 433 return; 434 } 435 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 436 t.setMetadata(info.getLeash(), key, value); 437 t.apply(); 438 } 439 } 440 updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash, TaskListener oldListener, TaskListener newListener)441 private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash, 442 TaskListener oldListener, TaskListener newListener) { 443 if (oldListener == newListener) return false; 444 // TODO: We currently send vanished/appeared as the task moves between types, but 445 // we should consider adding a different mode-changed callback 446 if (oldListener != null) { 447 oldListener.onTaskVanished(taskInfo); 448 } 449 if (newListener != null) { 450 newListener.onTaskAppeared(taskInfo, leash); 451 } 452 return true; 453 } 454 notifyLocusVisibilityIfNeeded(TaskInfo taskInfo)455 private void notifyLocusVisibilityIfNeeded(TaskInfo taskInfo) { 456 final int taskId = taskInfo.taskId; 457 final LocusId prevLocus = mVisibleTasksWithLocusId.get(taskId); 458 final boolean sameLocus = Objects.equals(prevLocus, taskInfo.mTopActivityLocusId); 459 if (prevLocus == null) { 460 // New visible locus 461 if (taskInfo.mTopActivityLocusId != null && taskInfo.isVisible) { 462 mVisibleTasksWithLocusId.put(taskId, taskInfo.mTopActivityLocusId); 463 notifyLocusIdChange(taskId, taskInfo.mTopActivityLocusId, true /* visible */); 464 } 465 } else if (sameLocus && !taskInfo.isVisible) { 466 // Hidden locus 467 mVisibleTasksWithLocusId.remove(taskId); 468 notifyLocusIdChange(taskId, taskInfo.mTopActivityLocusId, false /* visible */); 469 } else if (!sameLocus) { 470 // Changed locus 471 if (taskInfo.isVisible) { 472 mVisibleTasksWithLocusId.put(taskId, taskInfo.mTopActivityLocusId); 473 notifyLocusIdChange(taskId, prevLocus, false /* visible */); 474 notifyLocusIdChange(taskId, taskInfo.mTopActivityLocusId, true /* visible */); 475 } else { 476 mVisibleTasksWithLocusId.remove(taskInfo.taskId); 477 notifyLocusIdChange(taskId, prevLocus, false /* visible */); 478 } 479 } 480 } 481 notifyLocusIdChange(int taskId, LocusId locus, boolean visible)482 private void notifyLocusIdChange(int taskId, LocusId locus, boolean visible) { 483 for (int i = 0; i < mLocusIdListeners.size(); i++) { 484 mLocusIdListeners.valueAt(i).onVisibilityChanged(taskId, locus, visible); 485 } 486 } 487 488 @Override onSizeCompatRestartButtonClicked(int taskId)489 public void onSizeCompatRestartButtonClicked(int taskId) { 490 final TaskAppearedInfo info; 491 synchronized (mLock) { 492 info = mTasks.get(taskId); 493 } 494 if (info != null) { 495 restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token); 496 } 497 } 498 499 /** 500 * Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task 501 * to update the UI accordingly. 502 * 503 * @param taskInfo the new Task info 504 * @param taskListener listener to handle the Task Surface placement. {@code null} if task is 505 * vanished. 506 */ notifySizeCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener)507 private void notifySizeCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) { 508 if (mSizeCompatUI == null) { 509 return; 510 } 511 512 // The task is vanished or doesn't support size compat UI, notify to remove size compat UI 513 // on this Task if there is any. 514 if (taskListener == null || !taskListener.supportSizeCompatUI() 515 || !taskInfo.topActivityInSizeCompat || !taskInfo.isVisible) { 516 mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, 517 null /* taskConfig */, null /* taskListener */); 518 return; 519 } 520 521 mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, 522 taskInfo.configuration, taskListener); 523 } 524 getTaskListener(RunningTaskInfo runningTaskInfo)525 private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) { 526 return getTaskListener(runningTaskInfo, false /*removeLaunchCookieIfNeeded*/); 527 } 528 getTaskListener(RunningTaskInfo runningTaskInfo, boolean removeLaunchCookieIfNeeded)529 private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo, 530 boolean removeLaunchCookieIfNeeded) { 531 532 final int taskId = runningTaskInfo.taskId; 533 TaskListener listener; 534 535 // First priority goes to listener that might be pending for this task. 536 final ArrayList<IBinder> launchCookies = runningTaskInfo.launchCookies; 537 for (int i = launchCookies.size() - 1; i >= 0; --i) { 538 final IBinder cookie = launchCookies.get(i); 539 listener = mLaunchCookieToListener.get(cookie); 540 if (listener == null) continue; 541 542 if (removeLaunchCookieIfNeeded) { 543 // Remove the cookie and add the listener. 544 mLaunchCookieToListener.remove(cookie); 545 mTaskListeners.put(taskId, listener); 546 } 547 return listener; 548 } 549 550 // Next priority goes to taskId specific listeners. 551 listener = mTaskListeners.get(taskId); 552 if (listener != null) return listener; 553 554 // Next priority goes to the listener listening to its parent. 555 if (runningTaskInfo.hasParentTask()) { 556 listener = mTaskListeners.get(runningTaskInfo.parentTaskId); 557 if (listener != null) return listener; 558 } 559 560 // Next we try type specific listeners. 561 final int taskListenerType = taskInfoToTaskListenerType(runningTaskInfo); 562 return mTaskListeners.get(taskListenerType); 563 } 564 565 @VisibleForTesting taskInfoToTaskListenerType(RunningTaskInfo runningTaskInfo)566 static @TaskListenerType int taskInfoToTaskListenerType(RunningTaskInfo runningTaskInfo) { 567 switch (runningTaskInfo.getWindowingMode()) { 568 case WINDOWING_MODE_FULLSCREEN: 569 return TASK_LISTENER_TYPE_FULLSCREEN; 570 case WINDOWING_MODE_MULTI_WINDOW: 571 return TASK_LISTENER_TYPE_MULTI_WINDOW; 572 case WINDOWING_MODE_PINNED: 573 return TASK_LISTENER_TYPE_PIP; 574 case WINDOWING_MODE_FREEFORM: 575 case WINDOWING_MODE_UNDEFINED: 576 default: 577 return TASK_LISTENER_TYPE_UNDEFINED; 578 } 579 } 580 taskListenerTypeToString(@askListenerType int type)581 public static String taskListenerTypeToString(@TaskListenerType int type) { 582 switch (type) { 583 case TASK_LISTENER_TYPE_FULLSCREEN: 584 return "TASK_LISTENER_TYPE_FULLSCREEN"; 585 case TASK_LISTENER_TYPE_MULTI_WINDOW: 586 return "TASK_LISTENER_TYPE_MULTI_WINDOW"; 587 case TASK_LISTENER_TYPE_PIP: 588 return "TASK_LISTENER_TYPE_PIP"; 589 case TASK_LISTENER_TYPE_UNDEFINED: 590 return "TASK_LISTENER_TYPE_UNDEFINED"; 591 default: 592 return "taskId#" + type; 593 } 594 } 595 dump(@onNull PrintWriter pw, String prefix)596 public void dump(@NonNull PrintWriter pw, String prefix) { 597 synchronized (mLock) { 598 final String innerPrefix = prefix + " "; 599 final String childPrefix = innerPrefix + " "; 600 pw.println(prefix + TAG); 601 pw.println(innerPrefix + mTaskListeners.size() + " Listeners"); 602 for (int i = mTaskListeners.size() - 1; i >= 0; --i) { 603 final int key = mTaskListeners.keyAt(i); 604 final TaskListener listener = mTaskListeners.valueAt(i); 605 pw.println(innerPrefix + "#" + i + " " + taskListenerTypeToString(key)); 606 listener.dump(pw, childPrefix); 607 } 608 609 pw.println(); 610 pw.println(innerPrefix + mTasks.size() + " Tasks"); 611 for (int i = mTasks.size() - 1; i >= 0; --i) { 612 final int key = mTasks.keyAt(i); 613 final TaskAppearedInfo info = mTasks.valueAt(i); 614 final TaskListener listener = getTaskListener(info.getTaskInfo()); 615 pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener); 616 } 617 618 pw.println(); 619 pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies"); 620 for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) { 621 final IBinder key = mLaunchCookieToListener.keyAt(i); 622 final TaskListener listener = mLaunchCookieToListener.valueAt(i); 623 pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener); 624 } 625 } 626 } 627 } 628