1/*
2 * Copyright (C) 2016 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
17package com.android.server.am;
18
19import android.app.ActivityManager.TaskSnapshot;
20import android.app.ITaskStackListener;
21import android.app.ActivityManager.TaskDescription;
22import android.content.ComponentName;
23import android.os.Binder;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.Message;
27import android.os.RemoteCallbackList;
28import android.os.RemoteException;
29
30import java.util.ArrayList;
31
32class TaskChangeNotificationController {
33    private static final int LOG_STACK_STATE_MSG = 1;
34    private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2;
35    private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3;
36    private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
37    private static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5;
38    private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6;
39    private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7;
40    private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8;
41    private static final int NOTIFY_TASK_REMOVED_LISTENERS_MSG = 9;
42    private static final int NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG = 10;
43    private static final int NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG = 11;
44    private static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12;
45    private static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
46    private static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14;
47    private static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15;
48    private static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16;
49    private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17;
50    private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18;
51
52    // Delay in notifying task stack change listeners (in millis)
53    private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
54
55    private final ActivityManagerService mService;
56    private final ActivityStackSupervisor mStackSupervisor;
57    private final Handler mHandler;
58
59    // Task stack change listeners in a remote process.
60    private final RemoteCallbackList<ITaskStackListener> mRemoteTaskStackListeners =
61            new RemoteCallbackList<>();
62
63    /*
64     * Task stack change listeners in a local process. Tracked separately so that they can be
65     * called on the same thread.
66     */
67    private final ArrayList<ITaskStackListener> mLocalTaskStackListeners = new ArrayList<>();
68
69    private final TaskStackConsumer mNotifyTaskStackChanged = (l, m) -> {
70        l.onTaskStackChanged();
71    };
72
73    private final TaskStackConsumer mNotifyTaskCreated = (l, m) -> {
74        l.onTaskCreated(m.arg1, (ComponentName) m.obj);
75    };
76
77    private final TaskStackConsumer mNotifyTaskRemoved = (l, m) -> {
78        l.onTaskRemoved(m.arg1);
79    };
80
81    private final TaskStackConsumer mNotifyTaskMovedToFront = (l, m) -> {
82        l.onTaskMovedToFront(m.arg1);
83    };
84
85    private final TaskStackConsumer mNotifyTaskDescriptionChanged = (l, m) -> {
86        l.onTaskDescriptionChanged(m.arg1, (TaskDescription) m.obj);
87    };
88
89    private final TaskStackConsumer mNotifyActivityRequestedOrientationChanged = (l, m) -> {
90        l.onActivityRequestedOrientationChanged(m.arg1, m.arg2);
91    };
92
93    private final TaskStackConsumer mNotifyTaskRemovalStarted = (l, m) -> {
94        l.onTaskRemovalStarted(m.arg1);
95    };
96
97    private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> {
98        l.onActivityPinned((String) m.obj /* packageName */, m.sendingUid /* userId */,
99                m.arg1 /* taskId */, m.arg2 /* stackId */);
100    };
101
102    private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> {
103        l.onActivityUnpinned();
104    };
105
106    private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> {
107        l.onPinnedActivityRestartAttempt(m.arg1 != 0);
108    };
109
110    private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> {
111        l.onPinnedStackAnimationStarted();
112    };
113
114    private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> {
115        l.onPinnedStackAnimationEnded();
116    };
117
118    private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> {
119        l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2);
120    };
121
122    private final TaskStackConsumer mNotifyActivityDismissingDockedStack = (l, m) -> {
123        l.onActivityDismissingDockedStack();
124    };
125
126    private final TaskStackConsumer mNotifyActivityLaunchOnSecondaryDisplayFailed = (l, m) -> {
127        l.onActivityLaunchOnSecondaryDisplayFailed();
128    };
129
130    private final TaskStackConsumer mNotifyTaskProfileLocked = (l, m) -> {
131        l.onTaskProfileLocked(m.arg1, m.arg2);
132    };
133
134    private final TaskStackConsumer mNotifyTaskSnapshotChanged = (l, m) -> {
135        l.onTaskSnapshotChanged(m.arg1, (TaskSnapshot) m.obj);
136    };
137
138    @FunctionalInterface
139    public interface TaskStackConsumer {
140        void accept(ITaskStackListener t, Message m) throws RemoteException;
141    }
142
143    private class MainHandler extends Handler {
144        public MainHandler(Looper looper) {
145            super(looper);
146        }
147
148        @Override
149        public void handleMessage(Message msg) {
150            switch (msg.what) {
151                case LOG_STACK_STATE_MSG: {
152                    synchronized (mService) {
153                        mStackSupervisor.logStackState();
154                    }
155                    break;
156                }
157                case NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG:
158                    forAllRemoteListeners(mNotifyTaskStackChanged, msg);
159                    break;
160                case NOTIFY_TASK_ADDED_LISTENERS_MSG:
161                    forAllRemoteListeners(mNotifyTaskCreated, msg);
162                    break;
163                case NOTIFY_TASK_REMOVED_LISTENERS_MSG:
164                    forAllRemoteListeners(mNotifyTaskRemoved, msg);
165                    break;
166                case NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG:
167                    forAllRemoteListeners(mNotifyTaskMovedToFront, msg);
168                    break;
169                case NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG:
170                    forAllRemoteListeners(mNotifyTaskDescriptionChanged, msg);
171                    break;
172                case NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS:
173                    forAllRemoteListeners(mNotifyActivityRequestedOrientationChanged, msg);
174                    break;
175                case NOTIFY_TASK_REMOVAL_STARTED_LISTENERS:
176                    forAllRemoteListeners(mNotifyTaskRemovalStarted, msg);
177                    break;
178                case NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG:
179                    forAllRemoteListeners(mNotifyActivityPinned, msg);
180                    break;
181                case NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG:
182                    forAllRemoteListeners(mNotifyActivityUnpinned, msg);
183                    break;
184                case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG:
185                    forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg);
186                    break;
187                case NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG:
188                    forAllRemoteListeners(mNotifyPinnedStackAnimationStarted, msg);
189                    break;
190                case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG:
191                    forAllRemoteListeners(mNotifyPinnedStackAnimationEnded, msg);
192                    break;
193                case NOTIFY_FORCED_RESIZABLE_MSG:
194                    forAllRemoteListeners(mNotifyActivityForcedResizable, msg);
195                    break;
196                case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG:
197                    forAllRemoteListeners(mNotifyActivityDismissingDockedStack, msg);
198                    break;
199                case NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG:
200                    forAllRemoteListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg);
201                    break;
202                case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG:
203                    forAllRemoteListeners(mNotifyTaskProfileLocked, msg);
204                    break;
205                case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG:
206                    forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg);
207                    break;
208            }
209        }
210    }
211
212    public TaskChangeNotificationController(ActivityManagerService service,
213            ActivityStackSupervisor stackSupervisor, Handler handler) {
214        mService = service;
215        mStackSupervisor = stackSupervisor;
216        mHandler = new MainHandler(handler.getLooper());
217    }
218
219    public void registerTaskStackListener(ITaskStackListener listener) {
220        synchronized (mService) {
221            if (listener != null) {
222                if (Binder.getCallingPid() == android.os.Process.myPid()) {
223                    if (!mLocalTaskStackListeners.contains(listener)) {
224                        mLocalTaskStackListeners.add(listener);
225                    }
226                } else {
227                    mRemoteTaskStackListeners.register(listener);
228                }
229            }
230        }
231    }
232
233    public void unregisterTaskStackListener(ITaskStackListener listener) {
234        synchronized (mService) {
235            if (listener != null) {
236                if (Binder.getCallingPid() == android.os.Process.myPid()) {
237                    mLocalTaskStackListeners.remove(listener);
238                } else {
239                    mRemoteTaskStackListeners.unregister(listener);
240                }
241            }
242        }
243    }
244
245    private void forAllRemoteListeners(TaskStackConsumer callback, Message message) {
246        synchronized (mService) {
247            for (int i = mRemoteTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
248                try {
249                    // Make a one-way callback to the listener
250                    callback.accept(mRemoteTaskStackListeners.getBroadcastItem(i), message);
251                } catch (RemoteException e) {
252                    // Handled by the RemoteCallbackList.
253                }
254            }
255            mRemoteTaskStackListeners.finishBroadcast();
256        }
257    }
258
259    private void forAllLocalListeners(TaskStackConsumer callback, Message message) {
260        synchronized (mService) {
261            for (int i = mLocalTaskStackListeners.size() - 1; i >= 0; i--) {
262                try {
263                    callback.accept(mLocalTaskStackListeners.get(i), message);
264                } catch (RemoteException e) {
265                    // Never thrown since this is called locally.
266                }
267            }
268        }
269    }
270
271    /** Notifies all listeners when the task stack has changed. */
272    void notifyTaskStackChanged() {
273        mHandler.sendEmptyMessage(LOG_STACK_STATE_MSG);
274        mHandler.removeMessages(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
275        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
276        forAllLocalListeners(mNotifyTaskStackChanged, msg);
277        // Only the main task stack change notification requires a delay.
278        mHandler.sendMessageDelayed(msg, NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY);
279    }
280
281    /** Notifies all listeners when an Activity is pinned. */
282    void notifyActivityPinned(ActivityRecord r) {
283        mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
284        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
285                r.getTask().taskId, r.getStackId(), r.packageName);
286        msg.sendingUid = r.userId;
287        forAllLocalListeners(mNotifyActivityPinned, msg);
288        msg.sendToTarget();
289    }
290
291    /** Notifies all listeners when an Activity is unpinned. */
292    void notifyActivityUnpinned() {
293        mHandler.removeMessages(NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG);
294        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG);
295        forAllLocalListeners(mNotifyActivityUnpinned, msg);
296        msg.sendToTarget();
297    }
298
299    /**
300     * Notifies all listeners when an attempt was made to start an an activity that is already
301     * running in the pinned stack and the activity was not actually started, but the task is
302     * either brought to the front or a new Intent is delivered to it.
303     */
304    void notifyPinnedActivityRestartAttempt(boolean clearedTask) {
305        mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
306        final Message msg =
307                mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG,
308                        clearedTask ? 1 : 0, 0);
309        forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg);
310        msg.sendToTarget();
311    }
312
313    /** Notifies all listeners when the pinned stack animation starts. */
314    void notifyPinnedStackAnimationStarted() {
315        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
316        final Message msg =
317                mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
318        forAllLocalListeners(mNotifyPinnedStackAnimationStarted, msg);
319        msg.sendToTarget();
320    }
321
322    /** Notifies all listeners when the pinned stack animation ends. */
323    void notifyPinnedStackAnimationEnded() {
324        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
325        final Message msg =
326                mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
327        forAllLocalListeners(mNotifyPinnedStackAnimationEnded, msg);
328        msg.sendToTarget();
329    }
330
331    void notifyActivityDismissingDockedStack() {
332        mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
333        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
334        forAllLocalListeners(mNotifyActivityDismissingDockedStack, msg);
335        msg.sendToTarget();
336    }
337
338    void notifyActivityForcedResizable(int taskId, int reason, String packageName) {
339        mHandler.removeMessages(NOTIFY_FORCED_RESIZABLE_MSG);
340        final Message msg = mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, taskId, reason,
341                packageName);
342        forAllLocalListeners(mNotifyActivityForcedResizable, msg);
343        msg.sendToTarget();
344    }
345
346    void notifyActivityLaunchOnSecondaryDisplayFailed() {
347        mHandler.removeMessages(NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG);
348        final Message msg = mHandler.obtainMessage(
349                NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG);
350        forAllLocalListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg);
351        msg.sendToTarget();
352    }
353
354    void notifyTaskCreated(int taskId, ComponentName componentName) {
355        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_ADDED_LISTENERS_MSG,
356                taskId, 0 /* unused */, componentName);
357        forAllLocalListeners(mNotifyTaskCreated, msg);
358        msg.sendToTarget();
359    }
360
361    void notifyTaskRemoved(int taskId) {
362        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REMOVED_LISTENERS_MSG,
363                taskId, 0 /* unused */);
364        forAllLocalListeners(mNotifyTaskRemoved, msg);
365        msg.sendToTarget();
366    }
367
368    void notifyTaskMovedToFront(int taskId) {
369        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG,
370                taskId, 0 /* unused */);
371        forAllLocalListeners(mNotifyTaskMovedToFront, msg);
372        msg.sendToTarget();
373    }
374
375    void notifyTaskDescriptionChanged(int taskId, TaskDescription taskDescription) {
376        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG,
377                taskId, 0 /* unused */, taskDescription);
378        forAllLocalListeners(mNotifyTaskDescriptionChanged, msg);
379        msg.sendToTarget();
380
381    }
382
383    void notifyActivityRequestedOrientationChanged(int taskId, int orientation) {
384        final Message msg = mHandler.obtainMessage(
385                NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS, taskId, orientation);
386        forAllLocalListeners(mNotifyActivityRequestedOrientationChanged, msg);
387        msg.sendToTarget();
388    }
389
390    /**
391     * Notify listeners that the task is about to be finished before its surfaces are removed from
392     * the window manager. This allows interested parties to perform relevant animations before
393     * the window disappears.
394     */
395    void notifyTaskRemovalStarted(int taskId) {
396        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REMOVAL_STARTED_LISTENERS, taskId,
397                0 /* unused */);
398        forAllLocalListeners(mNotifyTaskRemovalStarted, msg);
399        msg.sendToTarget();
400
401    }
402
403    /**
404     * Notify listeners that the task has been put in a locked state because one or more of the
405     * activities inside it belong to a managed profile user that has been locked.
406     */
407    void notifyTaskProfileLocked(int taskId, int userId) {
408        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG, taskId,
409                userId);
410        forAllLocalListeners(mNotifyTaskProfileLocked, msg);
411        msg.sendToTarget();
412    }
413
414    /**
415     * Notify listeners that the snapshot of a task has changed.
416     */
417    void notifyTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
418        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG,
419                taskId, 0, snapshot);
420        forAllLocalListeners(mNotifyTaskSnapshotChanged, msg);
421        msg.sendToTarget();
422    }
423}
424