Add new methods to ITaskStackListener
Refactor task change notification logic into a separate class
TaskChangeNotificationController.
Add ActivityManagerService.unregisterTaskStackChangedListener
Add a no-op implementation that clients can subclass to avoid
having to reimplement all new methods when they are added.
Add new methods:
onTaskAdded
onTaskRemoved
onTaskMovedToFront
onTaskDescriptionChanged
onActivityRequestedOrientationChanged
onTaskFinishing
Design doc:
https://docs.google.com/document/d/1IgWZ44rKe9k1CzkjP2Mohv12OgRD1FxH8oLAyzhvCY4/edit#heading=h.yhzl6os0dbo5
Cherry-picked from I8302d6d3baf1ac1ca928765fe203091b9fab4070
Bug: 32277482
Test: Verify that callbacks are called in dummy implementations
Change-Id: I2ac2b870147ef049f3ee05fc5916c99332334526
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
new file mode 100644
index 0000000..b6e35d2
--- /dev/null
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.app.ITaskStackListener;
+import android.app.ActivityManager.TaskDescription;
+import android.content.ComponentName;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+
+class TaskChangeNotificationController {
+ static final int LOG_STACK_STATE_MSG = 1;
+ static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2;
+ static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3;
+ static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
+ static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5;
+ static final int NOTIFY_FORCED_RESIZABLE_MSG = 6;
+ static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7;
+ static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8;
+ static final int NOTIFY_TASK_REMOVED_LISTENERS_MSG = 9;
+ static final int NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG = 10;
+ static final int NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG = 11;
+ static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12;
+ static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
+
+ // Delay in notifying task stack change listeners (in millis)
+ static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
+
+ private final ActivityManagerService mService;
+ private final ActivityStackSupervisor mStackSupervisor;
+ private final Handler mHandler;
+
+ /** Task stack change listeners. */
+ private final RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
+ new RemoteCallbackList<ITaskStackListener>();
+
+ @FunctionalInterface
+ public interface ConsumerWithRemoteException<T> {
+ void accept(T t) throws RemoteException;
+ }
+
+ private class MainHandler extends Handler {
+ public MainHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case LOG_STACK_STATE_MSG: {
+ synchronized (mService) {
+ mStackSupervisor.logStackState();
+ }
+ break;
+ }
+ case NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG:
+ forAllListeners((listener) -> listener.onTaskStackChanged());
+ break;
+ case NOTIFY_TASK_ADDED_LISTENERS_MSG:
+ forAllListeners((listener) -> listener.onTaskCreated(msg.arg1,
+ (ComponentName) msg.obj));
+ break;
+ case NOTIFY_TASK_REMOVED_LISTENERS_MSG:
+ forAllListeners((listener) -> listener.onTaskRemoved(msg.arg1));
+ break;
+ case NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG:
+ forAllListeners((listener) -> listener.onTaskMovedToFront(msg.arg1));
+ break;
+ case NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG:
+ forAllListeners((listener) -> listener.onTaskDescriptionChanged(msg.arg1,
+ (TaskDescription) msg.obj));
+ break;
+ case NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS:
+ forAllListeners((listener) -> listener.onActivityRequestedOrientationChanged(
+ msg.arg1, msg.arg2));
+ case NOTIFY_TASK_REMOVAL_STARTED_LISTENERS:
+ forAllListeners((listener) -> listener.onTaskRemovalStarted(msg.arg1));
+ break;
+ case NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG:
+ forAllListeners((listener) -> listener.onActivityPinned());
+ break;
+ case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG:
+ forAllListeners((listener) -> listener.onPinnedActivityRestartAttempt());
+ break;
+ case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG:
+ forAllListeners((listener) -> listener.onPinnedStackAnimationEnded());
+ break;
+ case NOTIFY_FORCED_RESIZABLE_MSG:
+ forAllListeners((listener) -> listener.onActivityForcedResizable(
+ (String) msg.obj, msg.arg1));
+ break;
+ case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG:
+ forAllListeners((listener) -> listener.onActivityDismissingDockedStack());
+ break;
+ }
+ }
+ }
+
+ public TaskChangeNotificationController(ActivityManagerService service,
+ ActivityStackSupervisor stackSupervisor, Handler handler) {
+ mService = service;
+ mStackSupervisor = stackSupervisor;
+ mHandler = new MainHandler(handler.getLooper());
+ }
+
+ public void registerTaskStackListener(ITaskStackListener listener) {
+ synchronized (mService) {
+ if (listener != null) {
+ mTaskStackListeners.register(listener);
+ }
+ }
+ }
+
+ public void unregisterTaskStackListener(ITaskStackListener listener) {
+ synchronized (mService) {
+ if (listener != null) {
+ mTaskStackListeners.unregister(listener);
+ }
+ }
+ }
+
+ void forAllListeners(ConsumerWithRemoteException<ITaskStackListener> callback) {
+ synchronized (mService) {
+ for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
+ try {
+ // Make a one-way callback to the listener
+ callback.accept(mTaskStackListeners.getBroadcastItem(i));
+ } catch (RemoteException e) {
+ // Handled by the RemoteCallbackList.
+ }
+ }
+ mTaskStackListeners.finishBroadcast();
+ }
+ }
+
+ /** Notifies all listeners when the task stack has changed. */
+ void notifyTaskStackChanged() {
+ mHandler.sendEmptyMessage(LOG_STACK_STATE_MSG);
+ mHandler.removeMessages(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
+ Message msg = mHandler.obtainMessage(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
+ // Only the main task stack change notification requires a delay.
+ mHandler.sendMessageDelayed(msg, NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY);
+ }
+
+ /** Notifies all listeners when an Activity is pinned. */
+ void notifyActivityPinned() {
+ mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
+ mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG).sendToTarget();
+ }
+
+ /**
+ * Notifies all listeners when an attempt was made to start an an activity that is already
+ * running in the pinned stack and the activity was not actually started, but the task is
+ * either brought to the front or a new Intent is delivered to it.
+ */
+ void notifyPinnedActivityRestartAttempt() {
+ mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
+ mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG).sendToTarget();
+ }
+
+ /** Notifies all listeners when the pinned stack animation ends. */
+ void notifyPinnedStackAnimationEnded() {
+ mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
+ mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG)
+ .sendToTarget();
+ }
+
+ void notifyActivityDismissingDockedStack() {
+ mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
+ mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG).sendToTarget();
+ }
+
+ void notifyActivityForcedResizable(int taskId, String packageName) {
+ mHandler.removeMessages(NOTIFY_FORCED_RESIZABLE_MSG);
+ mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, taskId, 0 /* unused */, packageName)
+ .sendToTarget();
+ }
+
+ void notifyTaskCreated(int taskId, ComponentName componentName) {
+ mHandler.obtainMessage(NOTIFY_TASK_ADDED_LISTENERS_MSG, taskId, 0 /* unused */,
+ componentName).sendToTarget();
+ }
+
+ void notifyTaskRemoved(int taskId) {
+ mHandler.obtainMessage(NOTIFY_TASK_REMOVED_LISTENERS_MSG, taskId, 0 /* unused */)
+ .sendToTarget();
+ }
+
+ void notifyTaskMovedToFront(int taskId) {
+ mHandler.obtainMessage(NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG, taskId, 0 /* unused */)
+ .sendToTarget();
+ }
+
+ void notifyTaskDescriptionChanged(int taskId, TaskDescription taskDescription) {
+ mHandler.obtainMessage(NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG, taskId,
+ 0 /* unused */, taskDescription).sendToTarget();
+ }
+
+ void notifyActivityRequestedOrientationChanged(int taskId, int orientation) {
+ mHandler.obtainMessage(NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS, taskId,
+ orientation).sendToTarget();
+ }
+
+ /**
+ * Notify listeners that the task is about to be finished before its surfaces are removed from
+ * the window manager. This allows interested parties to perform relevant animations before
+ * the window disappears.
+ */
+ void notifyTaskRemovalStarted(int taskId) {
+ mHandler.obtainMessage(NOTIFY_TASK_REMOVAL_STARTED_LISTENERS, taskId, 0 /* unused */)
+ .sendToTarget();
+ }
+}