1 /*
2  * Copyright (C) 2017 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.systemui.shared.system;
18 
19 import android.annotation.NonNull;
20 import android.app.ActivityManager.RunningTaskInfo;
21 import android.app.ActivityTaskManager;
22 import android.app.TaskStackListener;
23 import android.content.ComponentName;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.os.Trace;
28 import android.util.Log;
29 import android.window.TaskSnapshot;
30 
31 import androidx.annotation.VisibleForTesting;
32 
33 import com.android.internal.os.SomeArgs;
34 import com.android.systemui.shared.recents.model.ThumbnailData;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /**
40  * Tracks all the task stack listeners
41  */
42 public class TaskStackChangeListeners {
43 
44     private static final String TAG = TaskStackChangeListeners.class.getSimpleName();
45     private static final TaskStackChangeListeners INSTANCE = new TaskStackChangeListeners();
46 
47     private final Impl mImpl;
48 
49     /**
50      * Proxies calls to the given handler callback synchronously for testing purposes.
51      */
52     private static class TestSyncHandler extends Handler {
53         private Handler.Callback mCb;
54 
TestSyncHandler()55         public TestSyncHandler() {
56             super(Looper.getMainLooper());
57         }
58 
setCallback(Handler.Callback cb)59         public void setCallback(Handler.Callback cb) {
60             mCb = cb;
61         }
62 
63         @Override
sendMessageAtTime(@onNull Message msg, long uptimeMillis)64         public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
65             return mCb.handleMessage(msg);
66         }
67     }
68 
TaskStackChangeListeners()69     private TaskStackChangeListeners() {
70         mImpl = new Impl(Looper.getMainLooper());
71     }
72 
TaskStackChangeListeners(Handler h)73     private TaskStackChangeListeners(Handler h) {
74         mImpl = new Impl(h);
75     }
76 
getInstance()77     public static TaskStackChangeListeners getInstance() {
78         return INSTANCE;
79     }
80 
81     /**
82      * Returns an instance of the listeners that can be called upon synchronously for testsing
83      * purposes.
84      */
85     @VisibleForTesting
getTestInstance()86     public static TaskStackChangeListeners getTestInstance() {
87         TestSyncHandler h = new TestSyncHandler();
88         TaskStackChangeListeners l = new TaskStackChangeListeners(h);
89         h.setCallback(l.mImpl);
90         return l;
91     }
92 
93     /**
94      * Registers a task stack listener with the system.
95      * This should be called on the main thread.
96      */
registerTaskStackListener(TaskStackChangeListener listener)97     public void registerTaskStackListener(TaskStackChangeListener listener) {
98         synchronized (mImpl) {
99             mImpl.addListener(listener);
100         }
101     }
102 
103     /**
104      * Unregisters a task stack listener with the system.
105      * This should be called on the main thread.
106      */
unregisterTaskStackListener(TaskStackChangeListener listener)107     public void unregisterTaskStackListener(TaskStackChangeListener listener) {
108         synchronized (mImpl) {
109             mImpl.removeListener(listener);
110         }
111     }
112 
113     /**
114      * Returns an instance of the listener to call upon from tests.
115      */
116     @VisibleForTesting
getListenerImpl()117     public TaskStackListener getListenerImpl() {
118         return mImpl;
119     }
120 
121     private class Impl extends TaskStackListener implements Handler.Callback {
122 
123         private static final int ON_TASK_STACK_CHANGED = 1;
124         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
125         private static final int ON_ACTIVITY_PINNED = 3;
126         private static final int ON_ACTIVITY_RESTART_ATTEMPT = 4;
127         private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
128         private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
129         private static final int ON_TASK_PROFILE_LOCKED = 8;
130         private static final int ON_ACTIVITY_UNPINNED = 10;
131         private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11;
132         private static final int ON_TASK_CREATED = 12;
133         private static final int ON_TASK_REMOVED = 13;
134         private static final int ON_TASK_MOVED_TO_FRONT = 14;
135         private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 15;
136         private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16;
137         private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 17;
138         private static final int ON_TASK_DISPLAY_CHANGED = 18;
139         private static final int ON_TASK_LIST_UPDATED = 19;
140         private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 20;
141         private static final int ON_TASK_DESCRIPTION_CHANGED = 21;
142         private static final int ON_ACTIVITY_ROTATION = 22;
143         private static final int ON_LOCK_TASK_MODE_CHANGED = 23;
144 
145         /**
146          * List of {@link TaskStackChangeListener} registered from {@link #addListener}.
147          */
148         private final List<TaskStackChangeListener> mTaskStackListeners = new ArrayList<>();
149         private final List<TaskStackChangeListener> mTmpListeners = new ArrayList<>();
150 
151         private final Handler mHandler;
152         private boolean mRegistered;
153 
Impl(Looper looper)154         private Impl(Looper looper) {
155             mHandler = new Handler(looper, this);
156         }
157 
Impl(Handler handler)158         private Impl(Handler handler) {
159             mHandler = handler;
160         }
161 
addListener(TaskStackChangeListener listener)162         public void addListener(TaskStackChangeListener listener) {
163             synchronized (mTaskStackListeners) {
164                 mTaskStackListeners.add(listener);
165             }
166             if (!mRegistered) {
167                 // Register mTaskStackListener to IActivityManager only once if needed.
168                 try {
169                     ActivityTaskManager.getService().registerTaskStackListener(this);
170                     mRegistered = true;
171                 } catch (Exception e) {
172                     Log.w(TAG, "Failed to call registerTaskStackListener", e);
173                 }
174             }
175         }
176 
removeListener(TaskStackChangeListener listener)177         public void removeListener(TaskStackChangeListener listener) {
178             boolean isEmpty;
179             synchronized (mTaskStackListeners) {
180                 mTaskStackListeners.remove(listener);
181                 isEmpty = mTaskStackListeners.isEmpty();
182             }
183             if (isEmpty && mRegistered) {
184                 // Unregister mTaskStackListener once we have no more listeners
185                 try {
186                     ActivityTaskManager.getService().unregisterTaskStackListener(this);
187                     mRegistered = false;
188                 } catch (Exception e) {
189                     Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
190                 }
191             }
192         }
193 
194         @Override
onTaskStackChanged()195         public void onTaskStackChanged() {
196             // Call the task changed callback for the non-ui thread listeners first. Copy to a set
197             // of temp listeners so that we don't lock on mTaskStackListeners while calling all the
198             // callbacks. This call is always on the same binder thread, so we can just synchronize
199             // on the copying of the listener list.
200             synchronized (mTaskStackListeners) {
201                 mTmpListeners.addAll(mTaskStackListeners);
202             }
203             for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
204                 mTmpListeners.get(i).onTaskStackChangedBackground();
205             }
206             mTmpListeners.clear();
207 
208             mHandler.removeMessages(ON_TASK_STACK_CHANGED);
209             mHandler.sendEmptyMessage(ON_TASK_STACK_CHANGED);
210         }
211 
212         @Override
onActivityPinned(String packageName, int userId, int taskId, int stackId)213         public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
214             mHandler.removeMessages(ON_ACTIVITY_PINNED);
215             mHandler.obtainMessage(ON_ACTIVITY_PINNED,
216                     new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
217         }
218 
219         @Override
onActivityUnpinned()220         public void onActivityUnpinned() {
221             mHandler.removeMessages(ON_ACTIVITY_UNPINNED);
222             mHandler.sendEmptyMessage(ON_ACTIVITY_UNPINNED);
223         }
224 
225         @Override
onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible)226         public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
227                 boolean clearedTask, boolean wasVisible) {
228             final SomeArgs args = SomeArgs.obtain();
229             args.arg1 = task;
230             args.argi1 = homeTaskVisible ? 1 : 0;
231             args.argi2 = clearedTask ? 1 : 0;
232             args.argi3 = wasVisible ? 1 : 0;
233             mHandler.removeMessages(ON_ACTIVITY_RESTART_ATTEMPT);
234             mHandler.obtainMessage(ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
235         }
236 
237         @Override
onActivityForcedResizable(String packageName, int taskId, int reason)238         public void onActivityForcedResizable(String packageName, int taskId, int reason) {
239             mHandler.obtainMessage(ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
240                     .sendToTarget();
241         }
242 
243         @Override
onActivityDismissingDockedTask()244         public void onActivityDismissingDockedTask() {
245             mHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK);
246         }
247 
248         @Override
onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo, int requestedDisplayId)249         public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo,
250                 int requestedDisplayId) {
251             mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED,
252                     requestedDisplayId,
253                     0 /* unused */,
254                     taskInfo).sendToTarget();
255         }
256 
257         @Override
onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo, int requestedDisplayId)258         public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
259                 int requestedDisplayId) {
260             mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
261                     requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
262         }
263 
264         @Override
onTaskProfileLocked(RunningTaskInfo taskInfo, int userId)265         public void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) {
266             mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, userId, 0, taskInfo).sendToTarget();
267         }
268 
269         @Override
onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)270         public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
271             mHandler.obtainMessage(ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
272         }
273 
274         @Override
onTaskCreated(int taskId, ComponentName componentName)275         public void onTaskCreated(int taskId, ComponentName componentName) {
276             mHandler.obtainMessage(ON_TASK_CREATED, taskId, 0, componentName).sendToTarget();
277         }
278 
279         @Override
onTaskRemoved(int taskId)280         public void onTaskRemoved(int taskId) {
281             mHandler.obtainMessage(ON_TASK_REMOVED, taskId, 0).sendToTarget();
282         }
283 
284         @Override
onTaskMovedToFront(RunningTaskInfo taskInfo)285         public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
286             mHandler.obtainMessage(ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget();
287         }
288 
289         @Override
onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)290         public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
291             mHandler.obtainMessage(ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
292         }
293 
294         @Override
onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)295         public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
296             mHandler.obtainMessage(ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
297                     requestedOrientation).sendToTarget();
298         }
299 
300         @Override
onTaskDisplayChanged(int taskId, int newDisplayId)301         public void onTaskDisplayChanged(int taskId, int newDisplayId) {
302             mHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
303         }
304 
305         @Override
onRecentTaskListUpdated()306         public void onRecentTaskListUpdated() {
307             mHandler.obtainMessage(ON_TASK_LIST_UPDATED).sendToTarget();
308         }
309 
310         @Override
onRecentTaskListFrozenChanged(boolean frozen)311         public void onRecentTaskListFrozenChanged(boolean frozen) {
312             mHandler.obtainMessage(ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0, 0 /* unused */)
313                     .sendToTarget();
314         }
315 
316         @Override
onTaskDescriptionChanged(RunningTaskInfo taskInfo)317         public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) {
318             mHandler.obtainMessage(ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
319         }
320 
321         @Override
onActivityRotation(int displayId)322         public void onActivityRotation(int displayId) {
323             mHandler.obtainMessage(ON_ACTIVITY_ROTATION, displayId, 0 /* unused */)
324                     .sendToTarget();
325         }
326 
327         @Override
onLockTaskModeChanged(int mode)328         public void onLockTaskModeChanged(int mode) {
329             mHandler.obtainMessage(ON_LOCK_TASK_MODE_CHANGED, mode, 0 /* unused */).sendToTarget();
330         }
331 
332         @Override
handleMessage(Message msg)333         public boolean handleMessage(Message msg) {
334             synchronized (mTaskStackListeners) {
335                 switch (msg.what) {
336                     case ON_TASK_STACK_CHANGED: {
337                         Trace.beginSection("onTaskStackChanged");
338                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
339                             mTaskStackListeners.get(i).onTaskStackChanged();
340                         }
341                         Trace.endSection();
342                         break;
343                     }
344                     case ON_TASK_SNAPSHOT_CHANGED: {
345                         Trace.beginSection("onTaskSnapshotChanged");
346                         final TaskSnapshot snapshot = (TaskSnapshot) msg.obj;
347                         final ThumbnailData thumbnail = new ThumbnailData(snapshot);
348                         boolean snapshotConsumed = false;
349                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
350                             boolean consumed = mTaskStackListeners.get(i).onTaskSnapshotChanged(
351                                     msg.arg1, thumbnail);
352                             snapshotConsumed |= consumed;
353                         }
354                         if (!snapshotConsumed) {
355                             thumbnail.recycleBitmap();
356                             if (snapshot.getHardwareBuffer() != null) {
357                                 snapshot.getHardwareBuffer().close();
358                             }
359                         }
360                         Trace.endSection();
361                         break;
362                     }
363                     case ON_ACTIVITY_PINNED: {
364                         final PinnedActivityInfo info = (PinnedActivityInfo) msg.obj;
365                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
366                             mTaskStackListeners.get(i).onActivityPinned(
367                                     info.mPackageName, info.mUserId, info.mTaskId,
368                                     info.mStackId);
369                         }
370                         break;
371                     }
372                     case ON_ACTIVITY_UNPINNED: {
373                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
374                             mTaskStackListeners.get(i).onActivityUnpinned();
375                         }
376                         break;
377                     }
378                     case ON_ACTIVITY_RESTART_ATTEMPT: {
379                         final SomeArgs args = (SomeArgs) msg.obj;
380                         final RunningTaskInfo task = (RunningTaskInfo) args.arg1;
381                         final boolean homeTaskVisible = args.argi1 != 0;
382                         final boolean clearedTask = args.argi2 != 0;
383                         final boolean wasVisible = args.argi3 != 0;
384                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
385                             mTaskStackListeners.get(i).onActivityRestartAttempt(task,
386                                     homeTaskVisible, clearedTask, wasVisible);
387                         }
388                         break;
389                     }
390                     case ON_ACTIVITY_FORCED_RESIZABLE: {
391                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
392                             mTaskStackListeners.get(i).onActivityForcedResizable(
393                                     (String) msg.obj, msg.arg1, msg.arg2);
394                         }
395                         break;
396                     }
397                     case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
398                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
399                             mTaskStackListeners.get(i).onActivityDismissingDockedStack();
400                         }
401                         break;
402                     }
403                     case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: {
404                         final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
405                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
406                             mTaskStackListeners.get(i)
407                                     .onActivityLaunchOnSecondaryDisplayFailed(info);
408                         }
409                         break;
410                     }
411                     case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED: {
412                         final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
413                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
414                             mTaskStackListeners.get(i)
415                                     .onActivityLaunchOnSecondaryDisplayRerouted(info);
416                         }
417                         break;
418                     }
419                     case ON_TASK_PROFILE_LOCKED: {
420                         final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
421                         final int userId = msg.arg1;
422                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
423                             mTaskStackListeners.get(i).onTaskProfileLocked(info, userId);
424                         }
425                         break;
426                     }
427                     case ON_TASK_CREATED: {
428                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
429                             mTaskStackListeners.get(i).onTaskCreated(msg.arg1,
430                                     (ComponentName) msg.obj);
431                         }
432                         break;
433                     }
434                     case ON_TASK_REMOVED: {
435                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
436                             mTaskStackListeners.get(i).onTaskRemoved(msg.arg1);
437                         }
438                         break;
439                     }
440                     case ON_TASK_MOVED_TO_FRONT: {
441                         final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
442                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
443                             mTaskStackListeners.get(i).onTaskMovedToFront(info);
444                         }
445                         break;
446                     }
447                     case ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE: {
448                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
449                             mTaskStackListeners.get(i)
450                                     .onActivityRequestedOrientationChanged(msg.arg1, msg.arg2);
451                         }
452                         break;
453                     }
454                     case ON_BACK_PRESSED_ON_TASK_ROOT: {
455                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
456                             mTaskStackListeners.get(i).onBackPressedOnTaskRoot(
457                                     (RunningTaskInfo) msg.obj);
458                         }
459                         break;
460                     }
461                     case ON_TASK_DISPLAY_CHANGED: {
462                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
463                             mTaskStackListeners.get(i).onTaskDisplayChanged(msg.arg1, msg.arg2);
464                         }
465                         break;
466                     }
467                     case ON_TASK_LIST_UPDATED: {
468                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
469                             mTaskStackListeners.get(i).onRecentTaskListUpdated();
470                         }
471                         break;
472                     }
473                     case ON_TASK_LIST_FROZEN_UNFROZEN: {
474                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
475                             mTaskStackListeners.get(i).onRecentTaskListFrozenChanged(
476                                     msg.arg1 != 0);
477                         }
478                         break;
479                     }
480                     case ON_TASK_DESCRIPTION_CHANGED: {
481                         final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
482                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
483                             mTaskStackListeners.get(i).onTaskDescriptionChanged(info);
484                         }
485                         break;
486                     }
487                     case ON_ACTIVITY_ROTATION: {
488                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
489                             mTaskStackListeners.get(i).onActivityRotation(msg.arg1);
490                         }
491                         break;
492                     }
493                     case ON_LOCK_TASK_MODE_CHANGED: {
494                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
495                             mTaskStackListeners.get(i).onLockTaskModeChanged(msg.arg1);
496                         }
497                         break;
498                     }
499                 }
500             }
501             if (msg.obj instanceof SomeArgs) {
502                 ((SomeArgs) msg.obj).recycle();
503             }
504             return true;
505         }
506     }
507 
508     private static class PinnedActivityInfo {
509         final String mPackageName;
510         final int mUserId;
511         final int mTaskId;
512         final int mStackId;
513 
PinnedActivityInfo(String packageName, int userId, int taskId, int stackId)514         PinnedActivityInfo(String packageName, int userId, int taskId, int stackId) {
515             mPackageName = packageName;
516             mUserId = userId;
517             mTaskId = taskId;
518             mStackId = stackId;
519         }
520     }
521 }
522