blob: 3b670057cb1a116787484439cfed8f29d6ab6422 [file] [log] [blame]
/*
* Copyright (C) 2020 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.wm.shell.common;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
import android.content.ComponentName;
import android.os.Handler;
import android.os.Message;
import android.os.Trace;
import android.util.Log;
import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import java.util.ArrayList;
import java.util.List;
/**
* Implementation of a {@link android.app.TaskStackListener}.
*/
public class TaskStackListenerImpl extends TaskStackListener implements Handler.Callback {
private static final String TAG = TaskStackListenerImpl.class.getSimpleName();
private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
private static final int ON_ACTIVITY_PINNED = 3;
private static final int ON_ACTIVITY_RESTART_ATTEMPT = 4;
private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5;
private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 6;
private static final int ON_TASK_PROFILE_LOCKED = 7;
private static final int ON_ACTIVITY_UNPINNED = 8;
private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 9;
private static final int ON_TASK_CREATED = 10;
private static final int ON_TASK_REMOVED = 11;
private static final int ON_TASK_MOVED_TO_FRONT = 12;
private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 13;
private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 14;
private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 15;
private static final int ON_TASK_DISPLAY_CHANGED = 16;
private static final int ON_TASK_LIST_UPDATED = 17;
private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 18;
private static final int ON_TASK_DESCRIPTION_CHANGED = 19;
private static final int ON_ACTIVITY_ROTATION = 20;
/**
* List of {@link TaskStackListenerCallback} registered from {@link #addListener}.
*/
private final List<TaskStackListenerCallback> mTaskStackListeners = new ArrayList<>();
private final List<TaskStackListenerCallback> mTmpListeners = new ArrayList<>();
private final IActivityTaskManager mActivityTaskManager;
// NOTE: In this case we do want to use a handler since we rely on the message system to
// efficiently dedupe sequential calls
private Handler mMainHandler;
public TaskStackListenerImpl(Handler mainHandler) {
mActivityTaskManager = ActivityTaskManager.getService();
mMainHandler = new Handler(mainHandler.getLooper(), this);
}
@VisibleForTesting
TaskStackListenerImpl(IActivityTaskManager activityTaskManager) {
mActivityTaskManager = activityTaskManager;
}
@VisibleForTesting
void setHandler(Handler mainHandler) {
mMainHandler = mainHandler;
}
public void addListener(TaskStackListenerCallback listener) {
final boolean wasEmpty;
synchronized (mTaskStackListeners) {
wasEmpty = mTaskStackListeners.isEmpty();
mTaskStackListeners.add(listener);
}
if (wasEmpty) {
// Register mTaskStackListener to IActivityManager only once if needed.
try {
mActivityTaskManager.registerTaskStackListener(this);
} catch (Exception e) {
Log.w(TAG, "Failed to call registerTaskStackListener", e);
}
}
}
public void removeListener(TaskStackListenerCallback listener) {
final boolean wasEmpty;
final boolean isEmpty;
synchronized (mTaskStackListeners) {
wasEmpty = mTaskStackListeners.isEmpty();
mTaskStackListeners.remove(listener);
isEmpty = mTaskStackListeners.isEmpty();
}
if (!wasEmpty && isEmpty) {
// Unregister mTaskStackListener once we have no more listeners
try {
mActivityTaskManager.unregisterTaskStackListener(this);
} catch (Exception e) {
Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
}
}
}
@Override
public void onRecentTaskListUpdated() {
mMainHandler.obtainMessage(ON_TASK_LIST_UPDATED).sendToTarget();
}
@Override
public void onRecentTaskListFrozenChanged(boolean frozen) {
mMainHandler.obtainMessage(ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0,
0 /* unused */).sendToTarget();
}
@Override
public void onTaskStackChanged() {
// Call the task changed callback for the non-ui thread listeners first. Copy to a set
// of temp listeners so that we don't lock on mTaskStackListeners while calling all the
// callbacks. This call is always on the same binder thread, so we can just synchronize
// on the copying of the listener list.
synchronized (mTaskStackListeners) {
mTmpListeners.addAll(mTaskStackListeners);
}
for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
mTmpListeners.get(i).onTaskStackChangedBackground();
}
mTmpListeners.clear();
mMainHandler.removeMessages(ON_TASK_STACK_CHANGED);
mMainHandler.sendEmptyMessage(ON_TASK_STACK_CHANGED);
}
@Override
public void onTaskProfileLocked(int taskId, int userId) {
mMainHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
}
@Override
public void onTaskDisplayChanged(int taskId, int newDisplayId) {
mMainHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId,
newDisplayId).sendToTarget();
}
@Override
public void onTaskCreated(int taskId, ComponentName componentName) {
mMainHandler.obtainMessage(ON_TASK_CREATED, taskId, 0, componentName).sendToTarget();
}
@Override
public void onTaskRemoved(int taskId) {
mMainHandler.obtainMessage(ON_TASK_REMOVED, taskId, 0).sendToTarget();
}
@Override
public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
mMainHandler.obtainMessage(ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget();
}
@Override
public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo) {
mMainHandler.obtainMessage(ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
}
@Override
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
mMainHandler.obtainMessage(ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot)
.sendToTarget();
}
@Override
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
mMainHandler.obtainMessage(ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
}
@Override
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = packageName;
args.argi1 = userId;
args.argi2 = taskId;
args.argi3 = stackId;
mMainHandler.removeMessages(ON_ACTIVITY_PINNED);
mMainHandler.obtainMessage(ON_ACTIVITY_PINNED, args).sendToTarget();
}
@Override
public void onActivityUnpinned() {
mMainHandler.removeMessages(ON_ACTIVITY_UNPINNED);
mMainHandler.sendEmptyMessage(ON_ACTIVITY_UNPINNED);
}
@Override
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = task;
args.argi1 = homeTaskVisible ? 1 : 0;
args.argi2 = clearedTask ? 1 : 0;
args.argi3 = wasVisible ? 1 : 0;
mMainHandler.removeMessages(ON_ACTIVITY_RESTART_ATTEMPT);
mMainHandler.obtainMessage(ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
}
@Override
public void onActivityForcedResizable(String packageName, int taskId, int reason) {
mMainHandler.obtainMessage(ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
.sendToTarget();
}
@Override
public void onActivityDismissingDockedTask() {
mMainHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK);
}
@Override
public void onActivityLaunchOnSecondaryDisplayFailed(
ActivityManager.RunningTaskInfo taskInfo,
int requestedDisplayId) {
mMainHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED,
requestedDisplayId,
0 /* unused */,
taskInfo).sendToTarget();
}
@Override
public void onActivityLaunchOnSecondaryDisplayRerouted(
ActivityManager.RunningTaskInfo taskInfo,
int requestedDisplayId) {
mMainHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
}
@Override
public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
mMainHandler.obtainMessage(ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
requestedOrientation).sendToTarget();
}
@Override
public void onActivityRotation(int displayId) {
mMainHandler.obtainMessage(ON_ACTIVITY_ROTATION, displayId, 0 /* unused */)
.sendToTarget();
}
@Override
public boolean handleMessage(Message msg) {
synchronized (mTaskStackListeners) {
switch (msg.what) {
case ON_TASK_STACK_CHANGED: {
Trace.beginSection("onTaskStackChanged");
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onTaskStackChanged();
}
Trace.endSection();
break;
}
case ON_TASK_SNAPSHOT_CHANGED: {
Trace.beginSection("onTaskSnapshotChanged");
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1,
(TaskSnapshot) msg.obj);
}
Trace.endSection();
break;
}
case ON_ACTIVITY_PINNED: {
final SomeArgs args = (SomeArgs) msg.obj;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onActivityPinned((String) args.arg1, args.argi1,
args.argi2, args.argi3);
}
break;
}
case ON_ACTIVITY_UNPINNED: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onActivityUnpinned();
}
break;
}
case ON_ACTIVITY_RESTART_ATTEMPT: {
final SomeArgs args = (SomeArgs) msg.obj;
final ActivityManager.RunningTaskInfo
task = (ActivityManager.RunningTaskInfo) args.arg1;
final boolean homeTaskVisible = args.argi1 != 0;
final boolean clearedTask = args.argi2 != 0;
final boolean wasVisible = args.argi3 != 0;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onActivityRestartAttempt(task,
homeTaskVisible, clearedTask, wasVisible);
}
break;
}
case ON_ACTIVITY_FORCED_RESIZABLE: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onActivityForcedResizable(
(String) msg.obj, msg.arg1, msg.arg2);
}
break;
}
case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onActivityDismissingDockedStack();
}
break;
}
case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: {
final ActivityManager.RunningTaskInfo
info = (ActivityManager.RunningTaskInfo) msg.obj;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i)
.onActivityLaunchOnSecondaryDisplayFailed(info);
}
break;
}
case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED: {
final ActivityManager.RunningTaskInfo
info = (ActivityManager.RunningTaskInfo) msg.obj;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i)
.onActivityLaunchOnSecondaryDisplayRerouted(info);
}
break;
}
case ON_TASK_PROFILE_LOCKED: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2);
}
break;
}
case ON_TASK_CREATED: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onTaskCreated(msg.arg1,
(ComponentName) msg.obj);
}
break;
}
case ON_TASK_REMOVED: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onTaskRemoved(msg.arg1);
}
break;
}
case ON_TASK_MOVED_TO_FRONT: {
final ActivityManager.RunningTaskInfo
info = (ActivityManager.RunningTaskInfo) msg.obj;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onTaskMovedToFront(info);
}
break;
}
case ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i)
.onActivityRequestedOrientationChanged(msg.arg1, msg.arg2);
}
break;
}
case ON_BACK_PRESSED_ON_TASK_ROOT: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onBackPressedOnTaskRoot(
(ActivityManager.RunningTaskInfo) msg.obj);
}
break;
}
case ON_TASK_DISPLAY_CHANGED: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onTaskDisplayChanged(msg.arg1, msg.arg2);
}
break;
}
case ON_TASK_LIST_UPDATED: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onRecentTaskListUpdated();
}
break;
}
case ON_TASK_LIST_FROZEN_UNFROZEN: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onRecentTaskListFrozenChanged(
msg.arg1 != 0);
}
break;
}
case ON_TASK_DESCRIPTION_CHANGED: {
final ActivityManager.RunningTaskInfo
info = (ActivityManager.RunningTaskInfo) msg.obj;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onTaskDescriptionChanged(info);
}
break;
}
case ON_ACTIVITY_ROTATION: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onActivityRotation(msg.arg1);
}
break;
}
}
}
if (msg.obj instanceof SomeArgs) {
((SomeArgs) msg.obj).recycle();
}
return true;
}
}