• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         private static final int ON_TASK_SNAPSHOT_INVALIDATED = 24;
145 
146         /**
147          * List of {@link TaskStackChangeListener} registered from {@link #addListener}.
148          */
149         private final List<TaskStackChangeListener> mTaskStackListeners = new ArrayList<>();
150         private final List<TaskStackChangeListener> mTmpListeners = new ArrayList<>();
151 
152         private final Handler mHandler;
153         private boolean mRegistered;
154 
Impl(Looper looper)155         private Impl(Looper looper) {
156             mHandler = new Handler(looper, this);
157         }
158 
Impl(Handler handler)159         private Impl(Handler handler) {
160             mHandler = handler;
161         }
162 
addListener(TaskStackChangeListener listener)163         public void addListener(TaskStackChangeListener listener) {
164             synchronized (mTaskStackListeners) {
165                 mTaskStackListeners.add(listener);
166             }
167             if (!mRegistered) {
168                 // Register mTaskStackListener to IActivityManager only once if needed.
169                 try {
170                     ActivityTaskManager.getService().registerTaskStackListener(this);
171                     mRegistered = true;
172                 } catch (Exception e) {
173                     Log.w(TAG, "Failed to call registerTaskStackListener", e);
174                 }
175             }
176         }
177 
removeListener(TaskStackChangeListener listener)178         public void removeListener(TaskStackChangeListener listener) {
179             boolean isEmpty;
180             synchronized (mTaskStackListeners) {
181                 mTaskStackListeners.remove(listener);
182                 isEmpty = mTaskStackListeners.isEmpty();
183             }
184             if (isEmpty && mRegistered) {
185                 // Unregister mTaskStackListener once we have no more listeners
186                 try {
187                     ActivityTaskManager.getService().unregisterTaskStackListener(this);
188                     mRegistered = false;
189                 } catch (Exception e) {
190                     Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
191                 }
192             }
193         }
194 
195         @Override
onTaskStackChanged()196         public void onTaskStackChanged() {
197             // Call the task changed callback for the non-ui thread listeners first. Copy to a set
198             // of temp listeners so that we don't lock on mTaskStackListeners while calling all the
199             // callbacks. This call is always on the same binder thread, so we can just synchronize
200             // on the copying of the listener list.
201             synchronized (mTaskStackListeners) {
202                 mTmpListeners.addAll(mTaskStackListeners);
203             }
204             for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
205                 mTmpListeners.get(i).onTaskStackChangedBackground();
206             }
207             mTmpListeners.clear();
208 
209             mHandler.removeMessages(ON_TASK_STACK_CHANGED);
210             mHandler.sendEmptyMessage(ON_TASK_STACK_CHANGED);
211         }
212 
213         @Override
onActivityPinned(String packageName, int userId, int taskId, int stackId)214         public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
215             mHandler.removeMessages(ON_ACTIVITY_PINNED);
216             mHandler.obtainMessage(ON_ACTIVITY_PINNED,
217                     new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
218         }
219 
220         @Override
onActivityUnpinned()221         public void onActivityUnpinned() {
222             mHandler.removeMessages(ON_ACTIVITY_UNPINNED);
223             mHandler.sendEmptyMessage(ON_ACTIVITY_UNPINNED);
224         }
225 
226         @Override
onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible)227         public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
228                 boolean clearedTask, boolean wasVisible) {
229             final SomeArgs args = SomeArgs.obtain();
230             args.arg1 = task;
231             args.argi1 = homeTaskVisible ? 1 : 0;
232             args.argi2 = clearedTask ? 1 : 0;
233             args.argi3 = wasVisible ? 1 : 0;
234             mHandler.removeMessages(ON_ACTIVITY_RESTART_ATTEMPT);
235             mHandler.obtainMessage(ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
236         }
237 
238         @Override
onActivityForcedResizable(String packageName, int taskId, int reason)239         public void onActivityForcedResizable(String packageName, int taskId, int reason) {
240             mHandler.obtainMessage(ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
241                     .sendToTarget();
242         }
243 
244         @Override
onActivityDismissingDockedTask()245         public void onActivityDismissingDockedTask() {
246             mHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK);
247         }
248 
249         @Override
onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo, int requestedDisplayId)250         public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo,
251                 int requestedDisplayId) {
252             mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED,
253                     requestedDisplayId,
254                     0 /* unused */,
255                     taskInfo).sendToTarget();
256         }
257 
258         @Override
onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo, int requestedDisplayId)259         public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
260                 int requestedDisplayId) {
261             mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
262                     requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
263         }
264 
265         @Override
onTaskProfileLocked(RunningTaskInfo taskInfo, int userId)266         public void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) {
267             mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, userId, 0, taskInfo).sendToTarget();
268         }
269 
270         @Override
onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)271         public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
272             mHandler.obtainMessage(ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
273         }
274 
275         @Override
onTaskSnapshotInvalidated(int taskId)276         public void onTaskSnapshotInvalidated(int taskId) {
277             mHandler.obtainMessage(ON_TASK_SNAPSHOT_INVALIDATED, taskId, 0 /* unused */)
278                     .sendToTarget();
279         }
280 
281         @Override
onTaskCreated(int taskId, ComponentName componentName)282         public void onTaskCreated(int taskId, ComponentName componentName) {
283             mHandler.obtainMessage(ON_TASK_CREATED, taskId, 0, componentName).sendToTarget();
284         }
285 
286         @Override
onTaskRemoved(int taskId)287         public void onTaskRemoved(int taskId) {
288             mHandler.obtainMessage(ON_TASK_REMOVED, taskId, 0).sendToTarget();
289         }
290 
291         @Override
onTaskMovedToFront(RunningTaskInfo taskInfo)292         public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
293             mHandler.obtainMessage(ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget();
294         }
295 
296         @Override
onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)297         public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
298             mHandler.obtainMessage(ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
299         }
300 
301         @Override
onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)302         public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
303             mHandler.obtainMessage(ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
304                     requestedOrientation).sendToTarget();
305         }
306 
307         @Override
onTaskDisplayChanged(int taskId, int newDisplayId)308         public void onTaskDisplayChanged(int taskId, int newDisplayId) {
309             mHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
310         }
311 
312         @Override
onRecentTaskListUpdated()313         public void onRecentTaskListUpdated() {
314             mHandler.obtainMessage(ON_TASK_LIST_UPDATED).sendToTarget();
315         }
316 
317         @Override
onRecentTaskListFrozenChanged(boolean frozen)318         public void onRecentTaskListFrozenChanged(boolean frozen) {
319             mHandler.obtainMessage(ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0, 0 /* unused */)
320                     .sendToTarget();
321         }
322 
323         @Override
onTaskDescriptionChanged(RunningTaskInfo taskInfo)324         public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) {
325             mHandler.obtainMessage(ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
326         }
327 
328         @Override
onActivityRotation(int displayId)329         public void onActivityRotation(int displayId) {
330             mHandler.obtainMessage(ON_ACTIVITY_ROTATION, displayId, 0 /* unused */)
331                     .sendToTarget();
332         }
333 
334         @Override
onLockTaskModeChanged(int mode)335         public void onLockTaskModeChanged(int mode) {
336             mHandler.obtainMessage(ON_LOCK_TASK_MODE_CHANGED, mode, 0 /* unused */).sendToTarget();
337         }
338 
339         @Override
handleMessage(Message msg)340         public boolean handleMessage(Message msg) {
341             synchronized (mTaskStackListeners) {
342                 switch (msg.what) {
343                     case ON_TASK_STACK_CHANGED: {
344                         Trace.beginSection("onTaskStackChanged");
345                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
346                             mTaskStackListeners.get(i).onTaskStackChanged();
347                         }
348                         Trace.endSection();
349                         break;
350                     }
351                     case ON_TASK_SNAPSHOT_CHANGED: {
352                         Trace.beginSection("onTaskSnapshotChanged");
353                         final TaskSnapshot snapshot = (TaskSnapshot) msg.obj;
354                         final ThumbnailData thumbnail = ThumbnailData.fromSnapshot(snapshot);
355                         boolean snapshotConsumed = false;
356                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
357                             boolean consumed = mTaskStackListeners.get(i).onTaskSnapshotChanged(
358                                     msg.arg1, thumbnail);
359                             snapshotConsumed |= consumed;
360                         }
361                         if (!snapshotConsumed) {
362                             thumbnail.recycleBitmap();
363                             if (snapshot.getHardwareBuffer() != null) {
364                                 snapshot.getHardwareBuffer().close();
365                             }
366                         }
367                         Trace.endSection();
368                         break;
369                     }
370                     case ON_ACTIVITY_PINNED: {
371                         final PinnedActivityInfo info = (PinnedActivityInfo) msg.obj;
372                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
373                             mTaskStackListeners.get(i).onActivityPinned(
374                                     info.mPackageName, info.mUserId, info.mTaskId,
375                                     info.mStackId);
376                         }
377                         break;
378                     }
379                     case ON_ACTIVITY_UNPINNED: {
380                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
381                             mTaskStackListeners.get(i).onActivityUnpinned();
382                         }
383                         break;
384                     }
385                     case ON_ACTIVITY_RESTART_ATTEMPT: {
386                         final SomeArgs args = (SomeArgs) msg.obj;
387                         final RunningTaskInfo task = (RunningTaskInfo) args.arg1;
388                         final boolean homeTaskVisible = args.argi1 != 0;
389                         final boolean clearedTask = args.argi2 != 0;
390                         final boolean wasVisible = args.argi3 != 0;
391                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
392                             mTaskStackListeners.get(i).onActivityRestartAttempt(task,
393                                     homeTaskVisible, clearedTask, wasVisible);
394                         }
395                         break;
396                     }
397                     case ON_ACTIVITY_FORCED_RESIZABLE: {
398                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
399                             mTaskStackListeners.get(i).onActivityForcedResizable(
400                                     (String) msg.obj, msg.arg1, msg.arg2);
401                         }
402                         break;
403                     }
404                     case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
405                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
406                             mTaskStackListeners.get(i).onActivityDismissingDockedStack();
407                         }
408                         break;
409                     }
410                     case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: {
411                         final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
412                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
413                             mTaskStackListeners.get(i)
414                                     .onActivityLaunchOnSecondaryDisplayFailed(info);
415                         }
416                         break;
417                     }
418                     case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED: {
419                         final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
420                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
421                             mTaskStackListeners.get(i)
422                                     .onActivityLaunchOnSecondaryDisplayRerouted(info);
423                         }
424                         break;
425                     }
426                     case ON_TASK_PROFILE_LOCKED: {
427                         final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
428                         final int userId = msg.arg1;
429                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
430                             mTaskStackListeners.get(i).onTaskProfileLocked(info, userId);
431                         }
432                         break;
433                     }
434                     case ON_TASK_CREATED: {
435                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
436                             mTaskStackListeners.get(i).onTaskCreated(msg.arg1,
437                                     (ComponentName) msg.obj);
438                         }
439                         break;
440                     }
441                     case ON_TASK_REMOVED: {
442                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
443                             mTaskStackListeners.get(i).onTaskRemoved(msg.arg1);
444                         }
445                         break;
446                     }
447                     case ON_TASK_MOVED_TO_FRONT: {
448                         final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
449                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
450                             mTaskStackListeners.get(i).onTaskMovedToFront(info);
451                         }
452                         break;
453                     }
454                     case ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE: {
455                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
456                             mTaskStackListeners.get(i)
457                                     .onActivityRequestedOrientationChanged(msg.arg1, msg.arg2);
458                         }
459                         break;
460                     }
461                     case ON_BACK_PRESSED_ON_TASK_ROOT: {
462                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
463                             mTaskStackListeners.get(i).onBackPressedOnTaskRoot(
464                                     (RunningTaskInfo) msg.obj);
465                         }
466                         break;
467                     }
468                     case ON_TASK_DISPLAY_CHANGED: {
469                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
470                             mTaskStackListeners.get(i).onTaskDisplayChanged(msg.arg1, msg.arg2);
471                         }
472                         break;
473                     }
474                     case ON_TASK_LIST_UPDATED: {
475                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
476                             mTaskStackListeners.get(i).onRecentTaskListUpdated();
477                         }
478                         break;
479                     }
480                     case ON_TASK_LIST_FROZEN_UNFROZEN: {
481                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
482                             mTaskStackListeners.get(i).onRecentTaskListFrozenChanged(
483                                     msg.arg1 != 0);
484                         }
485                         break;
486                     }
487                     case ON_TASK_DESCRIPTION_CHANGED: {
488                         final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
489                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
490                             mTaskStackListeners.get(i).onTaskDescriptionChanged(info);
491                         }
492                         break;
493                     }
494                     case ON_ACTIVITY_ROTATION: {
495                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
496                             mTaskStackListeners.get(i).onActivityRotation(msg.arg1);
497                         }
498                         break;
499                     }
500                     case ON_LOCK_TASK_MODE_CHANGED: {
501                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
502                             mTaskStackListeners.get(i).onLockTaskModeChanged(msg.arg1);
503                         }
504                         break;
505                     }
506                     case ON_TASK_SNAPSHOT_INVALIDATED: {
507                         Trace.beginSection("onTaskSnapshotInvalidated");
508                         final ThumbnailData thumbnail = new ThumbnailData();
509                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
510                             mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1, thumbnail);
511                         }
512                         Trace.endSection();
513                         break;
514                     }
515                 }
516             }
517             if (msg.obj instanceof SomeArgs) {
518                 ((SomeArgs) msg.obj).recycle();
519             }
520             return true;
521         }
522     }
523 
524     private static class PinnedActivityInfo {
525         final String mPackageName;
526         final int mUserId;
527         final int mTaskId;
528         final int mStackId;
529 
PinnedActivityInfo(String packageName, int userId, int taskId, int stackId)530         PinnedActivityInfo(String packageName, int userId, int taskId, int stackId) {
531             mPackageName = packageName;
532             mUserId = userId;
533             mTaskId = taskId;
534             mStackId = stackId;
535         }
536     }
537 }
538