blob: badcfa9d69642e925db756ddb93aa3d0863a3241 [file] [log] [blame]
/*
* Copyright (C) 2022 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.wm;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import android.annotation.IntDef;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.function.Consumer;
/**
* Integrates common functionality from TaskSnapshotController and ActivitySnapshotController.
*/
class SnapshotController {
private static final boolean DEBUG = false;
private static final String TAG = AbsAppSnapshotController.TAG;
static final int ACTIVITY_OPEN = 1;
static final int ACTIVITY_CLOSE = 2;
static final int TASK_OPEN = 4;
static final int TASK_CLOSE = 8;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
value = {ACTIVITY_OPEN,
ACTIVITY_CLOSE,
TASK_OPEN,
TASK_CLOSE})
@interface TransitionStateType {}
private final SnapshotPersistQueue mSnapshotPersistQueue;
final TaskSnapshotController mTaskSnapshotController;
final ActivitySnapshotController mActivitySnapshotController;
private final ArraySet<Task> mTmpCloseTasks = new ArraySet<>();
private final ArraySet<Task> mTmpOpenTasks = new ArraySet<>();
private final SparseArray<TransitionState> mTmpOpenCloseRecord = new SparseArray<>();
private final ArraySet<Integer> mTmpAnalysisRecord = new ArraySet<>();
private final SparseArray<ArrayList<Consumer<TransitionState>>> mTransitionStateConsumer =
new SparseArray<>();
private int mActivatedType;
private final ActivityOrderCheck mActivityOrderCheck = new ActivityOrderCheck();
private final ActivityOrderCheck.AnalysisResult mResultHandler = (type, close, open) -> {
addTransitionRecord(type, true/* open */, open);
addTransitionRecord(type, false/* open */, close);
};
private static class ActivityOrderCheck {
private ActivityRecord mOpenActivity;
private ActivityRecord mCloseActivity;
private int mOpenIndex = -1;
private int mCloseIndex = -1;
private void reset() {
mOpenActivity = null;
mCloseActivity = null;
mOpenIndex = -1;
mCloseIndex = -1;
}
private void setTarget(boolean open, ActivityRecord ar, int index) {
if (open) {
mOpenActivity = ar;
mOpenIndex = index;
} else {
mCloseActivity = ar;
mCloseIndex = index;
}
}
void analysisOrder(ArraySet<ActivityRecord> closeApps,
ArraySet<ActivityRecord> openApps, Task task, AnalysisResult result) {
for (int j = closeApps.size() - 1; j >= 0; j--) {
final ActivityRecord ar = closeApps.valueAt(j);
if (ar.getTask() == task) {
setTarget(false, ar, task.mChildren.indexOf(ar));
break;
}
}
for (int j = openApps.size() - 1; j >= 0; j--) {
final ActivityRecord ar = openApps.valueAt(j);
if (ar.getTask() == task) {
setTarget(true, ar, task.mChildren.indexOf(ar));
break;
}
}
if (mOpenIndex > mCloseIndex && mCloseIndex != -1) {
result.onCheckResult(ACTIVITY_OPEN, mCloseActivity, mOpenActivity);
} else if (mOpenIndex < mCloseIndex && mOpenIndex != -1) {
result.onCheckResult(ACTIVITY_CLOSE, mCloseActivity, mOpenActivity);
}
reset();
}
private interface AnalysisResult {
void onCheckResult(@TransitionStateType int type,
ActivityRecord close, ActivityRecord open);
}
}
private void addTransitionRecord(int type, boolean open, WindowContainer target) {
TransitionState record = mTmpOpenCloseRecord.get(type);
if (record == null) {
record = new TransitionState();
mTmpOpenCloseRecord.set(type, record);
}
record.addParticipant(target, open);
mTmpAnalysisRecord.add(type);
}
private void clearRecord() {
mTmpOpenCloseRecord.clear();
mTmpAnalysisRecord.clear();
}
static class TransitionState<TYPE extends WindowContainer> {
private final ArraySet<TYPE> mOpenParticipant = new ArraySet<>();
private final ArraySet<TYPE> mCloseParticipant = new ArraySet<>();
void addParticipant(TYPE target, boolean open) {
final ArraySet<TYPE> participant = open
? mOpenParticipant : mCloseParticipant;
participant.add(target);
}
ArraySet<TYPE> getParticipant(boolean open) {
return open ? mOpenParticipant : mCloseParticipant;
}
}
SnapshotController(WindowManagerService wms) {
mSnapshotPersistQueue = new SnapshotPersistQueue();
mTaskSnapshotController = new TaskSnapshotController(wms, mSnapshotPersistQueue);
mActivitySnapshotController = new ActivitySnapshotController(wms, mSnapshotPersistQueue);
}
void registerTransitionStateConsumer(@TransitionStateType int type,
Consumer<TransitionState> consumer) {
ArrayList<Consumer<TransitionState>> consumers = mTransitionStateConsumer.get(type);
if (consumers == null) {
consumers = new ArrayList<>();
mTransitionStateConsumer.set(type, consumers);
}
if (!consumers.contains(consumer)) {
consumers.add(consumer);
}
mActivatedType |= type;
}
void unregisterTransitionStateConsumer(int type, Consumer<TransitionState> consumer) {
final ArrayList<Consumer<TransitionState>> consumers = mTransitionStateConsumer.get(type);
if (consumers == null) {
return;
}
consumers.remove(consumer);
if (consumers.size() == 0) {
mActivatedType &= ~type;
}
}
private boolean hasTransitionStateConsumer(@TransitionStateType int type) {
return (mActivatedType & type) != 0;
}
void systemReady() {
mSnapshotPersistQueue.systemReady();
mTaskSnapshotController.systemReady();
mActivitySnapshotController.systemReady();
}
void setPause(boolean paused) {
mSnapshotPersistQueue.setPaused(paused);
}
void onAppRemoved(ActivityRecord activity) {
mTaskSnapshotController.onAppRemoved(activity);
mActivitySnapshotController.onAppRemoved(activity);
}
void onAppDied(ActivityRecord activity) {
mTaskSnapshotController.onAppDied(activity);
mActivitySnapshotController.onAppDied(activity);
}
void notifyAppVisibilityChanged(ActivityRecord appWindowToken, boolean visible) {
if (!visible && hasTransitionStateConsumer(TASK_CLOSE)) {
final Task task = appWindowToken.getTask();
if (task == null || task.isVisibleRequested()) {
return;
}
// close task transition
addTransitionRecord(TASK_CLOSE, false /*open*/, task);
mActivitySnapshotController.preTransitionStart();
notifyTransition(TASK_CLOSE);
mActivitySnapshotController.postTransitionStart();
clearRecord();
}
}
// For legacy transition
void onTransitionStarting(DisplayContent displayContent) {
handleAppTransition(displayContent.mClosingApps, displayContent.mOpeningApps);
}
// For shell transition, adapt to legacy transition.
void onTransitionReady(@WindowManager.TransitionType int type,
ArraySet<WindowContainer> participants) {
final boolean isTransitionOpen = isTransitionOpen(type);
final boolean isTransitionClose = isTransitionClose(type);
if (!isTransitionOpen && !isTransitionClose && type < TRANSIT_FIRST_CUSTOM
|| (mActivatedType == 0)) {
return;
}
final ArraySet<ActivityRecord> openingApps = new ArraySet<>();
final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
for (int i = participants.size() - 1; i >= 0; --i) {
final ActivityRecord ar = participants.valueAt(i).asActivityRecord();
if (ar == null || ar.getTask() == null) continue;
if (ar.isVisibleRequested()) {
openingApps.add(ar);
} else {
closingApps.add(ar);
}
}
handleAppTransition(closingApps, openingApps);
}
private static boolean isTransitionOpen(int type) {
return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT;
}
private static boolean isTransitionClose(int type) {
return type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK;
}
@VisibleForTesting
void handleAppTransition(ArraySet<ActivityRecord> closingApps,
ArraySet<ActivityRecord> openApps) {
if (mActivatedType == 0) {
return;
}
analysisTransition(closingApps, openApps);
mActivitySnapshotController.preTransitionStart();
for (Integer transitionType : mTmpAnalysisRecord) {
notifyTransition(transitionType);
}
mActivitySnapshotController.postTransitionStart();
clearRecord();
}
private void notifyTransition(int transitionType) {
final TransitionState record = mTmpOpenCloseRecord.get(transitionType);
final ArrayList<Consumer<TransitionState>> consumers =
mTransitionStateConsumer.get(transitionType);
for (Consumer<TransitionState> consumer : consumers) {
consumer.accept(record);
}
}
private void analysisTransition(ArraySet<ActivityRecord> closingApps,
ArraySet<ActivityRecord> openingApps) {
getParticipantTasks(closingApps, mTmpCloseTasks, false /* isOpen */);
getParticipantTasks(openingApps, mTmpOpenTasks, true /* isOpen */);
if (DEBUG) {
Slog.d(TAG, "AppSnapshotController#analysisTransition participants"
+ " mTmpCloseTasks " + mTmpCloseTasks
+ " mTmpOpenTasks " + mTmpOpenTasks);
}
for (int i = mTmpCloseTasks.size() - 1; i >= 0; i--) {
final Task closeTask = mTmpCloseTasks.valueAt(i);
if (mTmpOpenTasks.contains(closeTask)) {
if (hasTransitionStateConsumer(ACTIVITY_OPEN)
|| hasTransitionStateConsumer(ACTIVITY_CLOSE)) {
mActivityOrderCheck.analysisOrder(closingApps, openingApps, closeTask,
mResultHandler);
}
} else if (hasTransitionStateConsumer(TASK_CLOSE)) {
// close task transition
addTransitionRecord(TASK_CLOSE, false /*open*/, closeTask);
}
}
if (hasTransitionStateConsumer(TASK_OPEN)) {
for (int i = mTmpOpenTasks.size() - 1; i >= 0; i--) {
final Task openTask = mTmpOpenTasks.valueAt(i);
if (!mTmpCloseTasks.contains(openTask)) {
// this is open task transition
addTransitionRecord(TASK_OPEN, true /*open*/, openTask);
}
}
}
mTmpCloseTasks.clear();
mTmpOpenTasks.clear();
}
private void getParticipantTasks(ArraySet<ActivityRecord> activityRecords, ArraySet<Task> tasks,
boolean isOpen) {
for (int i = activityRecords.size() - 1; i >= 0; i--) {
final ActivityRecord activity = activityRecords.valueAt(i);
final Task task = activity.getTask();
if (task == null) continue;
if (isOpen == activity.isVisibleRequested()) {
tasks.add(task);
}
}
}
void dump(PrintWriter pw, String prefix) {
mTaskSnapshotController.dump(pw, prefix);
mActivitySnapshotController.dump(pw, prefix);
}
}