Made ShellTaskOrganizer thread safe

- Post callbacks from WM Core on executor.
- Added lock to protect critical sections.

Bug: 171384876
Test: They pass!
Change-Id: Ia1808b66104c2a1e6efb9f43c31e6a3a2f67a952
This commit is contained in:
Wale Ogunwale
2020-10-21 15:15:10 -07:00
parent 05c11205a4
commit c21aca86d8
2 changed files with 144 additions and 115 deletions

View File

@@ -29,6 +29,7 @@ import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Interface for ActivityTaskManager/WindowManager to delegate control of tasks.
@@ -37,15 +38,19 @@ import java.util.List;
@TestApi
public class TaskOrganizer extends WindowOrganizer {
private ITaskOrganizerController mTaskOrganizerController;
private final ITaskOrganizerController mTaskOrganizerController;
// Callbacks WM Core are posted on this executor if it isn't null, otherwise direct calls are
// made on the incoming binder call.
private final Executor mExecutor;
public TaskOrganizer() {
this(null);
this(null /*taskOrganizerController*/, null /*executor*/);
}
/** @hide */
@VisibleForTesting
public TaskOrganizer(ITaskOrganizerController taskOrganizerController) {
public TaskOrganizer(ITaskOrganizerController taskOrganizerController, Executor executor) {
mExecutor = executor != null ? executor : command -> command.run();
mTaskOrganizerController = taskOrganizerController != null
? taskOrganizerController : getController();
}
@@ -183,22 +188,22 @@ public class TaskOrganizer extends WindowOrganizer {
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
TaskOrganizer.this.onTaskAppeared(taskInfo, leash);
mExecutor.execute(() -> TaskOrganizer.this.onTaskAppeared(taskInfo, leash));
}
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
TaskOrganizer.this.onTaskVanished(taskInfo);
mExecutor.execute(() -> TaskOrganizer.this.onTaskVanished(taskInfo));
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
TaskOrganizer.this.onTaskInfoChanged(info);
mExecutor.execute(() -> TaskOrganizer.this.onTaskInfoChanged(info));
}
@Override
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo info) {
TaskOrganizer.this.onBackPressedOnTaskRoot(info);
mExecutor.execute(() -> TaskOrganizer.this.onBackPressedOnTaskRoot(info));
}
};

View File

@@ -105,6 +105,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
// TODO(shell-transitions): move to a more "global" Shell location as this isn't only for Tasks
private final Transitions mTransitions;
private final Object mLock = new Object();
public ShellTaskOrganizer(SyncTransactionQueue syncQueue, TransactionPool transactionPool,
ShellExecutor mainExecutor, ShellExecutor animExecutor) {
this(null, syncQueue, transactionPool, mainExecutor, animExecutor);
@@ -114,7 +116,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController,
SyncTransactionQueue syncQueue, TransactionPool transactionPool,
ShellExecutor mainExecutor, ShellExecutor animExecutor) {
super(taskOrganizerController);
super(taskOrganizerController, mainExecutor);
addListenerForType(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN);
mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
@@ -122,68 +124,76 @@ public class ShellTaskOrganizer extends TaskOrganizer {
@Override
public List<TaskAppearedInfo> registerOrganizer() {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Registering organizer");
final List<TaskAppearedInfo> taskInfos = super.registerOrganizer();
for (int i = 0; i < taskInfos.size(); i++) {
final TaskAppearedInfo info = taskInfos.get(i);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s",
info.getTaskInfo().taskId, info.getTaskInfo().baseIntent);
onTaskAppeared(info);
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Registering organizer");
final List<TaskAppearedInfo> taskInfos = super.registerOrganizer();
for (int i = 0; i < taskInfos.size(); i++) {
final TaskAppearedInfo info = taskInfos.get(i);
ProtoLog.v(WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s",
info.getTaskInfo().taskId, info.getTaskInfo().baseIntent);
onTaskAppeared(info);
}
return taskInfos;
}
return taskInfos;
}
public TaskAppearedInfo createRootTask(
int displayId, int windowingMode, TaskListener listener) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG,
"createRootTask() displayId=%d winMode=%d listener=%s",
displayId, windowingMode, listener.toString());
final TaskAppearedInfo info = super.createRootTask(displayId, windowingMode);
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "createRootTask() displayId=%d winMode=%d listener=%s",
displayId, windowingMode, listener.toString());
final TaskAppearedInfo info = super.createRootTask(displayId, windowingMode);
// Add the listener and send the task appeared signal
mTaskListeners.put(info.getTaskInfo().taskId, listener);
onTaskAppeared(info);
return info;
// Add the listener and send the task appeared signal
mTaskListeners.put(info.getTaskInfo().taskId, listener);
onTaskAppeared(info);
return info;
}
}
/**
* Adds a listener for a specific task id.
*/
public void addListenerForTaskId(TaskListener listener, int taskId) {
ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForTaskId taskId=%s", taskId);
if (mTaskListeners.get(taskId) != null) {
throw new IllegalArgumentException("Listener for taskId=" + taskId + " already exists");
}
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForTaskId taskId=%s", taskId);
if (mTaskListeners.get(taskId) != null) {
throw new IllegalArgumentException(
"Listener for taskId=" + taskId + " already exists");
}
final TaskAppearedInfo info = mTasks.get(taskId);
if (info == null) {
throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId);
}
final TaskAppearedInfo info = mTasks.get(taskId);
if (info == null) {
throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId);
}
final TaskListener oldListener = getTaskListener(info.getTaskInfo());
mTaskListeners.put(taskId, listener);
updateTaskListenerIfNeeded(info.getTaskInfo(), info.getLeash(), oldListener, listener);
final TaskListener oldListener = getTaskListener(info.getTaskInfo());
mTaskListeners.put(taskId, listener);
updateTaskListenerIfNeeded(info.getTaskInfo(), info.getLeash(), oldListener, listener);
}
}
/**
* Adds a listener for tasks with given types.
*/
public void addListenerForType(TaskListener listener, @TaskListenerType int... listenerTypes) {
ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForType types=%s listener=%s",
Arrays.toString(listenerTypes), listener);
for (int listenerType : listenerTypes) {
if (mTaskListeners.get(listenerType) != null) {
throw new IllegalArgumentException("Listener for listenerType=" + listenerType
+ " already exists");
}
mTaskListeners.put(listenerType, listener);
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForType types=%s listener=%s",
Arrays.toString(listenerTypes), listener);
for (int listenerType : listenerTypes) {
if (mTaskListeners.get(listenerType) != null) {
throw new IllegalArgumentException("Listener for listenerType=" + listenerType
+ " already exists");
}
mTaskListeners.put(listenerType, listener);
// Notify the listener of all existing tasks with the given type.
for (int i = mTasks.size() - 1; i >= 0; --i) {
final TaskAppearedInfo data = mTasks.valueAt(i);
final TaskListener taskListener = getTaskListener(data.getTaskInfo());
if (taskListener != listener) continue;
listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
// Notify the listener of all existing tasks with the given type.
for (int i = mTasks.size() - 1; i >= 0; --i) {
final TaskAppearedInfo data = mTasks.valueAt(i);
final TaskListener taskListener = getTaskListener(data.getTaskInfo());
if (taskListener != listener) continue;
listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
}
}
}
}
@@ -192,30 +202,32 @@ public class ShellTaskOrganizer extends TaskOrganizer {
* Removes a registered listener.
*/
public void removeListener(TaskListener listener) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
final int index = mTaskListeners.indexOfValue(listener);
if (index == -1) {
Log.w(TAG, "No registered listener found");
return;
}
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
final int index = mTaskListeners.indexOfValue(listener);
if (index == -1) {
Log.w(TAG, "No registered listener found");
return;
}
// Collect tasks associated with the listener we are about to remove.
final ArrayList<TaskAppearedInfo> tasks = new ArrayList<>();
for (int i = mTasks.size() - 1; i >= 0; --i) {
final TaskAppearedInfo data = mTasks.valueAt(i);
final TaskListener taskListener = getTaskListener(data.getTaskInfo());
if (taskListener != listener) continue;
tasks.add(data);
}
// Collect tasks associated with the listener we are about to remove.
final ArrayList<TaskAppearedInfo> tasks = new ArrayList<>();
for (int i = mTasks.size() - 1; i >= 0; --i) {
final TaskAppearedInfo data = mTasks.valueAt(i);
final TaskListener taskListener = getTaskListener(data.getTaskInfo());
if (taskListener != listener) continue;
tasks.add(data);
}
// Remove listener
mTaskListeners.removeAt(index);
// Remove listener
mTaskListeners.removeAt(index);
// Associate tasks with new listeners if needed.
for (int i = tasks.size() - 1; i >= 0; --i) {
final TaskAppearedInfo data = tasks.get(i);
updateTaskListenerIfNeeded(data.getTaskInfo(), data.getLeash(),
null /* oldListener already removed*/, getTaskListener(data.getTaskInfo()));
// Associate tasks with new listeners if needed.
for (int i = tasks.size() - 1; i >= 0; --i) {
final TaskAppearedInfo data = tasks.get(i);
updateTaskListenerIfNeeded(data.getTaskInfo(), data.getLeash(),
null /* oldListener already removed*/, getTaskListener(data.getTaskInfo()));
}
}
}
@@ -224,12 +236,16 @@ public class ShellTaskOrganizer extends TaskOrganizer {
* appears.
*/
public void setPendingLaunchCookieListener(IBinder cookie, TaskListener listener) {
mLaunchCookieToListener.put(cookie, listener);
synchronized (mLock) {
mLaunchCookieToListener.put(cookie, listener);
}
}
@Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
synchronized (mLock) {
onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
}
}
private void onTaskAppeared(TaskAppearedInfo info) {
@@ -245,35 +261,41 @@ public class ShellTaskOrganizer extends TaskOrganizer {
@Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId);
final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
final TaskListener oldListener = getTaskListener(data.getTaskInfo());
final TaskListener newListener = getTaskListener(taskInfo);
mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash()));
final boolean updated = updateTaskListenerIfNeeded(
taskInfo, data.getLeash(), oldListener, newListener);
if (!updated && newListener != null) {
newListener.onTaskInfoChanged(taskInfo);
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId);
final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
final TaskListener oldListener = getTaskListener(data.getTaskInfo());
final TaskListener newListener = getTaskListener(taskInfo);
mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash()));
final boolean updated = updateTaskListenerIfNeeded(
taskInfo, data.getLeash(), oldListener, newListener);
if (!updated && newListener != null) {
newListener.onTaskInfoChanged(taskInfo);
}
}
}
@Override
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", taskInfo.taskId);
final TaskListener listener = getTaskListener(taskInfo);
if (listener != null) {
listener.onBackPressedOnTaskRoot(taskInfo);
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", taskInfo.taskId);
final TaskListener listener = getTaskListener(taskInfo);
if (listener != null) {
listener.onBackPressedOnTaskRoot(taskInfo);
}
}
}
@Override
public void onTaskVanished(RunningTaskInfo taskInfo) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId);
final int taskId = taskInfo.taskId;
final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo());
mTasks.remove(taskId);
if (listener != null) {
listener.onTaskVanished(taskInfo);
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId);
final int taskId = taskInfo.taskId;
final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo());
mTasks.remove(taskId);
if (listener != null) {
listener.onTaskVanished(taskInfo);
}
}
}
@@ -368,32 +390,34 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
pw.println(prefix + TAG);
pw.println(innerPrefix + mTaskListeners.size() + " Listeners");
for (int i = mTaskListeners.size() - 1; i >= 0; --i) {
final int key = mTaskListeners.keyAt(i);
final TaskListener listener = mTaskListeners.valueAt(i);
pw.println(innerPrefix + "#" + i + " " + taskListenerTypeToString(key));
listener.dump(pw, childPrefix);
}
synchronized (mLock) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
pw.println(prefix + TAG);
pw.println(innerPrefix + mTaskListeners.size() + " Listeners");
for (int i = mTaskListeners.size() - 1; i >= 0; --i) {
final int key = mTaskListeners.keyAt(i);
final TaskListener listener = mTaskListeners.valueAt(i);
pw.println(innerPrefix + "#" + i + " " + taskListenerTypeToString(key));
listener.dump(pw, childPrefix);
}
pw.println();
pw.println(innerPrefix + mTasks.size() + " Tasks");
for (int i = mTasks.size() - 1; i >= 0; --i) {
final int key = mTasks.keyAt(i);
final TaskAppearedInfo info = mTasks.valueAt(i);
final TaskListener listener = getTaskListener(info.getTaskInfo());
pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener);
}
pw.println();
pw.println(innerPrefix + mTasks.size() + " Tasks");
for (int i = mTasks.size() - 1; i >= 0; --i) {
final int key = mTasks.keyAt(i);
final TaskAppearedInfo info = mTasks.valueAt(i);
final TaskListener listener = getTaskListener(info.getTaskInfo());
pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener);
}
pw.println();
pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies");
for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) {
final IBinder key = mLaunchCookieToListener.keyAt(i);
final TaskListener listener = mLaunchCookieToListener.valueAt(i);
pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener);
pw.println();
pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies");
for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) {
final IBinder key = mLaunchCookieToListener.keyAt(i);
final TaskListener listener = mLaunchCookieToListener.valueAt(i);
pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener);
}
}
}
}