| /* |
| * 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.ActivityManager; |
| import android.app.ActivityManager.TaskSnapshot; |
| import android.app.ITaskStackListener; |
| import android.app.ActivityManager.TaskDescription; |
| import android.content.ComponentName; |
| import android.os.Binder; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.RemoteCallbackList; |
| import android.os.RemoteException; |
| |
| import java.util.ArrayList; |
| |
| 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; |
| static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14; |
| static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15; |
| static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16; |
| static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17; |
| |
| // 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 in a remote process. |
| private final RemoteCallbackList<ITaskStackListener> mRemoteTaskStackListeners = |
| new RemoteCallbackList<>(); |
| |
| /* |
| * Task stack change listeners in a local process. Tracked separately so that they can be |
| * called on the same thread. |
| */ |
| private final ArrayList<ITaskStackListener> mLocalTaskStackListeners = new ArrayList<>(); |
| |
| private final TaskStackConsumer mNotifyTaskStackChanged = (l, m) -> { |
| l.onTaskStackChanged(); |
| }; |
| |
| private final TaskStackConsumer mNotifyTaskCreated = (l, m) -> { |
| l.onTaskCreated(m.arg1, (ComponentName) m.obj); |
| }; |
| |
| private final TaskStackConsumer mNotifyTaskRemoved = (l, m) -> { |
| l.onTaskRemoved(m.arg1); |
| }; |
| |
| private final TaskStackConsumer mNotifyTaskMovedToFront = (l, m) -> { |
| l.onTaskMovedToFront(m.arg1); |
| }; |
| |
| private final TaskStackConsumer mNotifyTaskDescriptionChanged = (l, m) -> { |
| l.onTaskDescriptionChanged(m.arg1, (TaskDescription) m.obj); |
| }; |
| |
| private final TaskStackConsumer mNotifyActivityRequestedOrientationChanged = (l, m) -> { |
| l.onActivityRequestedOrientationChanged(m.arg1, m.arg2); |
| }; |
| |
| private final TaskStackConsumer mNotifyTaskRemovalStarted = (l, m) -> { |
| l.onTaskRemovalStarted(m.arg1); |
| }; |
| |
| private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> { |
| l.onActivityPinned((String) m.obj); |
| }; |
| |
| private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> { |
| l.onActivityUnpinned(); |
| }; |
| |
| private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> { |
| l.onPinnedActivityRestartAttempt(); |
| }; |
| |
| private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> { |
| l.onPinnedStackAnimationStarted(); |
| }; |
| |
| private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> { |
| l.onPinnedStackAnimationEnded(); |
| }; |
| |
| private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> { |
| l.onActivityForcedResizable((String) m.obj, m.arg1); |
| }; |
| |
| private final TaskStackConsumer mNotifyActivityDismissingDockedStack = (l, m) -> { |
| l.onActivityDismissingDockedStack(); |
| }; |
| |
| private final TaskStackConsumer mNotifyTaskProfileLocked = (l, m) -> { |
| l.onTaskProfileLocked(m.arg1, m.arg2); |
| }; |
| |
| private final TaskStackConsumer mNotifyTaskSnapshotChanged = (l, m) -> { |
| l.onTaskSnapshotChanged(m.arg1, (TaskSnapshot) m.obj); |
| }; |
| |
| @FunctionalInterface |
| public interface TaskStackConsumer { |
| void accept(ITaskStackListener t, Message m) 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: |
| forAllRemoteListeners(mNotifyTaskStackChanged, msg); |
| break; |
| case NOTIFY_TASK_ADDED_LISTENERS_MSG: |
| forAllRemoteListeners(mNotifyTaskCreated, msg); |
| break; |
| case NOTIFY_TASK_REMOVED_LISTENERS_MSG: |
| forAllRemoteListeners(mNotifyTaskRemoved, msg); |
| break; |
| case NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG: |
| forAllRemoteListeners(mNotifyTaskMovedToFront, msg); |
| break; |
| case NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG: |
| forAllRemoteListeners(mNotifyTaskDescriptionChanged, msg); |
| break; |
| case NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS: |
| forAllRemoteListeners(mNotifyActivityRequestedOrientationChanged, msg); |
| break; |
| case NOTIFY_TASK_REMOVAL_STARTED_LISTENERS: |
| forAllRemoteListeners(mNotifyTaskRemovalStarted, msg); |
| break; |
| case NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG: |
| forAllRemoteListeners(mNotifyActivityPinned, msg); |
| break; |
| case NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG: |
| forAllRemoteListeners(mNotifyActivityUnpinned, msg); |
| break; |
| case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: |
| forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg); |
| break; |
| case NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG: |
| forAllRemoteListeners(mNotifyPinnedStackAnimationStarted, msg); |
| break; |
| case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG: |
| forAllRemoteListeners(mNotifyPinnedStackAnimationEnded, msg); |
| break; |
| case NOTIFY_FORCED_RESIZABLE_MSG: |
| forAllRemoteListeners(mNotifyActivityForcedResizable, msg); |
| break; |
| case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG: |
| forAllRemoteListeners(mNotifyActivityDismissingDockedStack, msg); |
| break; |
| case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG: |
| forAllRemoteListeners(mNotifyTaskProfileLocked, msg); |
| break; |
| case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG: |
| forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg); |
| 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) { |
| if (Binder.getCallingPid() == android.os.Process.myPid()) { |
| if (!mLocalTaskStackListeners.contains(listener)) { |
| mLocalTaskStackListeners.add(listener); |
| } |
| } else { |
| mRemoteTaskStackListeners.register(listener); |
| } |
| } |
| } |
| } |
| |
| public void unregisterTaskStackListener(ITaskStackListener listener) { |
| synchronized (mService) { |
| if (listener != null) { |
| if (Binder.getCallingPid() == android.os.Process.myPid()) { |
| mLocalTaskStackListeners.remove(listener); |
| } else { |
| mRemoteTaskStackListeners.unregister(listener); |
| } |
| } |
| } |
| } |
| |
| void forAllRemoteListeners(TaskStackConsumer callback, Message message) { |
| synchronized (mService) { |
| for (int i = mRemoteTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) { |
| try { |
| // Make a one-way callback to the listener |
| callback.accept(mRemoteTaskStackListeners.getBroadcastItem(i), message); |
| } catch (RemoteException e) { |
| // Handled by the RemoteCallbackList. |
| } |
| } |
| mRemoteTaskStackListeners.finishBroadcast(); |
| } |
| } |
| |
| void forAllLocalListeners(TaskStackConsumer callback, Message message) { |
| synchronized (mService) { |
| for (int i = mLocalTaskStackListeners.size() - 1; i >= 0; i--) { |
| try { |
| callback.accept(mLocalTaskStackListeners.get(i), message); |
| } catch (RemoteException e) { |
| // Never thrown since this is called locally. |
| } |
| } |
| } |
| } |
| |
| /** 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); |
| final Message msg = mHandler.obtainMessage(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG); |
| forAllLocalListeners(mNotifyTaskStackChanged, 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(String packageName) { |
| mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG); |
| final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG, |
| packageName); |
| forAllLocalListeners(mNotifyActivityPinned, msg); |
| msg.sendToTarget(); |
| } |
| |
| /** Notifies all listeners when an Activity is unpinned. */ |
| void notifyActivityUnpinned() { |
| mHandler.removeMessages(NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG); |
| final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG); |
| forAllLocalListeners(mNotifyActivityUnpinned, msg); |
| 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); |
| final Message msg = |
| mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG); |
| forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg); |
| msg.sendToTarget(); |
| } |
| |
| /** Notifies all listeners when the pinned stack animation starts. */ |
| void notifyPinnedStackAnimationStarted() { |
| mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG); |
| final Message msg = |
| mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG); |
| forAllLocalListeners(mNotifyPinnedStackAnimationStarted, msg); |
| msg.sendToTarget(); |
| } |
| |
| /** Notifies all listeners when the pinned stack animation ends. */ |
| void notifyPinnedStackAnimationEnded() { |
| mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG); |
| final Message msg = |
| mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG); |
| forAllLocalListeners(mNotifyPinnedStackAnimationEnded, msg); |
| msg.sendToTarget(); |
| } |
| |
| void notifyActivityDismissingDockedStack() { |
| mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG); |
| final Message message = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG); |
| forAllLocalListeners(mNotifyActivityDismissingDockedStack, message); |
| } |
| |
| void notifyActivityForcedResizable(int taskId, String packageName) { |
| mHandler.removeMessages(NOTIFY_FORCED_RESIZABLE_MSG); |
| final Message msg = mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, taskId, |
| 0 /* unused */, packageName); |
| forAllLocalListeners(mNotifyActivityForcedResizable, msg); |
| msg.sendToTarget(); |
| } |
| |
| void notifyTaskCreated(int taskId, ComponentName componentName) { |
| final Message msg = mHandler.obtainMessage(NOTIFY_TASK_ADDED_LISTENERS_MSG, |
| taskId, 0 /* unused */, componentName); |
| forAllLocalListeners(mNotifyTaskCreated, msg); |
| msg.sendToTarget(); |
| } |
| |
| void notifyTaskRemoved(int taskId) { |
| final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REMOVED_LISTENERS_MSG, |
| taskId, 0 /* unused */); |
| forAllLocalListeners(mNotifyTaskRemoved, msg); |
| msg.sendToTarget(); |
| } |
| |
| void notifyTaskMovedToFront(int taskId) { |
| final Message msg = mHandler.obtainMessage(NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG, |
| taskId, 0 /* unused */); |
| forAllLocalListeners(mNotifyTaskMovedToFront, msg); |
| msg.sendToTarget(); |
| } |
| |
| void notifyTaskDescriptionChanged(int taskId, TaskDescription taskDescription) { |
| final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG, |
| taskId, 0 /* unused */, taskDescription); |
| forAllLocalListeners(mNotifyTaskDescriptionChanged, msg); |
| msg.sendToTarget(); |
| |
| } |
| |
| void notifyActivityRequestedOrientationChanged(int taskId, int orientation) { |
| final Message msg = mHandler.obtainMessage( |
| NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS, taskId, orientation); |
| forAllLocalListeners(mNotifyActivityRequestedOrientationChanged, msg); |
| msg.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) { |
| final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REMOVAL_STARTED_LISTENERS, taskId, |
| 0 /* unused */); |
| forAllLocalListeners(mNotifyTaskRemovalStarted, msg); |
| msg.sendToTarget(); |
| |
| } |
| |
| /** |
| * Notify listeners that the task has been put in a locked state because one or more of the |
| * activities inside it belong to a managed profile user that has been locked. |
| */ |
| void notifyTaskProfileLocked(int taskId, int userId) { |
| final Message msg = mHandler.obtainMessage(NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG, taskId, |
| userId); |
| forAllLocalListeners(mNotifyTaskProfileLocked, msg); |
| msg.sendToTarget(); |
| } |
| |
| /** |
| * Notify listeners that the snapshot of a task has changed. |
| */ |
| void notifyTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { |
| final Message msg = mHandler.obtainMessage(NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG, |
| taskId, 0, snapshot); |
| forAllLocalListeners(mNotifyTaskSnapshotChanged, msg); |
| msg.sendToTarget(); |
| } |
| } |