blob: b159333e9a0eae113db56b0befa1516480b9eb55 [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.apppairs;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager;
import android.util.Slog;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import java.io.PrintWriter;
/**
* Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}.
*/
public class AppPairsController {
private static final String TAG = AppPairsController.class.getSimpleName();
private final ShellTaskOrganizer mTaskOrganizer;
private final SyncTransactionQueue mSyncQueue;
private final ShellExecutor mMainExecutor;
private final AppPairsImpl mImpl = new AppPairsImpl();
private AppPairsPool mPairsPool;
// Active app-pairs mapped by root task id key.
private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>();
private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
DisplayController displayController, ShellExecutor mainExecutor,
DisplayImeController displayImeController) {
mTaskOrganizer = organizer;
mSyncQueue = syncQueue;
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mMainExecutor = mainExecutor;
}
public AppPairs asAppPairs() {
return mImpl;
}
public void onOrganizerRegistered() {
if (mPairsPool == null) {
setPairsPool(new AppPairsPool(this));
}
}
@VisibleForTesting
public void setPairsPool(AppPairsPool pool) {
mPairsPool = pool;
}
public boolean pair(int taskId1, int taskId2) {
final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1);
final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2);
if (task1 == null || task2 == null) {
return false;
}
return pair(task1, task2);
}
public boolean pair(ActivityManager.RunningTaskInfo task1,
ActivityManager.RunningTaskInfo task2) {
return pairInner(task1, task2) != null;
}
@VisibleForTesting
public AppPair pairInner(
@NonNull ActivityManager.RunningTaskInfo task1,
@NonNull ActivityManager.RunningTaskInfo task2) {
final AppPair pair = mPairsPool.acquire();
if (!pair.pair(task1, task2)) {
mPairsPool.release(pair);
return null;
}
mActiveAppPairs.put(pair.getRootTaskId(), pair);
return pair;
}
public void unpair(int taskId) {
unpair(taskId, true /* releaseToPool */);
}
public void unpair(int taskId, boolean releaseToPool) {
AppPair pair = mActiveAppPairs.get(taskId);
if (pair == null) {
for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
final AppPair candidate = mActiveAppPairs.valueAt(i);
if (candidate.contains(taskId)) {
pair = candidate;
break;
}
}
}
if (pair == null) {
ProtoLog.v(WM_SHELL_TASK_ORG, "taskId %d isn't isn't in an app-pair.", taskId);
return;
}
ProtoLog.v(WM_SHELL_TASK_ORG, "unpair taskId=%d pair=%s", taskId, pair);
mActiveAppPairs.remove(pair.getRootTaskId());
pair.unpair();
if (releaseToPool) {
mPairsPool.release(pair);
}
}
ShellTaskOrganizer getTaskOrganizer() {
return mTaskOrganizer;
}
SyncTransactionQueue getSyncTransactionQueue() {
return mSyncQueue;
}
DisplayController getDisplayController() {
return mDisplayController;
}
DisplayImeController getDisplayImeController() {
return mDisplayImeController;
}
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
pw.println(prefix + this);
for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
mActiveAppPairs.valueAt(i).dump(pw, childPrefix);
}
if (mPairsPool != null) {
mPairsPool.dump(pw, prefix);
}
}
@Override
public String toString() {
return TAG + "#" + mActiveAppPairs.size();
}
private class AppPairsImpl implements AppPairs {
@Override
public boolean pair(int task1, int task2) {
boolean[] result = new boolean[1];
try {
mMainExecutor.executeBlocking(() -> {
result[0] = AppPairsController.this.pair(task1, task2);
});
} catch (InterruptedException e) {
Slog.e(TAG, "Failed to pair tasks: " + task1 + ", " + task2);
}
return result[0];
}
@Override
public boolean pair(ActivityManager.RunningTaskInfo task1,
ActivityManager.RunningTaskInfo task2) {
boolean[] result = new boolean[1];
try {
mMainExecutor.executeBlocking(() -> {
result[0] = AppPairsController.this.pair(task1, task2);
});
} catch (InterruptedException e) {
Slog.e(TAG, "Failed to pair tasks: " + task1 + ", " + task2);
}
return result[0];
}
@Override
public void unpair(int taskId) {
mMainExecutor.execute(() -> {
AppPairsController.this.unpair(taskId);
});
}
}
}