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