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