blob: 2308932418057ee79482ab2c3d07484fa1c7c452 [file] [log] [blame]
/*
* 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 android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.server.wm.ComponentNameUtils.getActivityName;
import static android.server.wm.ProtoExtractors.extract;
import static android.server.wm.StateLogger.log;
import static android.server.wm.StateLogger.logE;
import static android.server.wm.TestTaskOrganizer.INVALID_TASK_ID;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.ActivityTaskManager;
import android.content.ComponentName;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.util.SparseArray;
import android.view.WindowManager;
import android.view.nano.DisplayInfoProto;
import android.view.nano.ViewProtoEnums;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.server.wm.nano.ActivityRecordProto;
import com.android.server.wm.nano.AppTransitionProto;
import com.android.server.wm.nano.ConfigurationContainerProto;
import com.android.server.wm.nano.DisplayAreaProto;
import com.android.server.wm.nano.DisplayContentProto;
import com.android.server.wm.nano.DisplayFramesProto;
import com.android.server.wm.nano.DisplayRotationProto;
import com.android.server.wm.nano.IdentifierProto;
import com.android.server.wm.nano.KeyguardControllerProto;
import com.android.server.wm.nano.PinnedTaskControllerProto;
import com.android.server.wm.nano.RootWindowContainerProto;
import com.android.server.wm.nano.TaskFragmentProto;
import com.android.server.wm.nano.TaskProto;
import com.android.server.wm.nano.WindowContainerChildProto;
import com.android.server.wm.nano.WindowContainerProto;
import com.android.server.wm.nano.WindowFramesProto;
import com.android.server.wm.nano.WindowManagerServiceDumpProto;
import com.android.server.wm.nano.WindowStateAnimatorProto;
import com.android.server.wm.nano.WindowStateProto;
import com.android.server.wm.nano.WindowSurfaceControllerProto;
import com.android.server.wm.nano.WindowTokenProto;
import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WindowManagerState {
public static final String STATE_INITIALIZING = "INITIALIZING";
public static final String STATE_STARTED = "STARTED";
public static final String STATE_RESUMED = "RESUMED";
public static final String STATE_PAUSED = "PAUSED";
public static final String STATE_STOPPED = "STOPPED";
public static final String STATE_DESTROYED = "DESTROYED";
public static final String TRANSIT_ACTIVITY_OPEN = "TRANSIT_ACTIVITY_OPEN";
public static final String TRANSIT_ACTIVITY_CLOSE = "TRANSIT_ACTIVITY_CLOSE";
public static final String TRANSIT_TASK_OPEN = "TRANSIT_TASK_OPEN";
public static final String TRANSIT_TASK_CLOSE = "TRANSIT_TASK_CLOSE";
public static final String TRANSIT_WALLPAPER_OPEN = "TRANSIT_WALLPAPER_OPEN";
public static final String TRANSIT_WALLPAPER_CLOSE = "TRANSIT_WALLPAPER_CLOSE";
public static final String TRANSIT_WALLPAPER_INTRA_OPEN = "TRANSIT_WALLPAPER_INTRA_OPEN";
public static final String TRANSIT_WALLPAPER_INTRA_CLOSE = "TRANSIT_WALLPAPER_INTRA_CLOSE";
public static final String TRANSIT_KEYGUARD_GOING_AWAY = "TRANSIT_KEYGUARD_GOING_AWAY";
public static final String TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
"TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER";
public static final String TRANSIT_KEYGUARD_OCCLUDE = "TRANSIT_KEYGUARD_OCCLUDE";
public static final String TRANSIT_KEYGUARD_UNOCCLUDE = "TRANSIT_KEYGUARD_UNOCCLUDE";
public static final String TRANSIT_TRANSLUCENT_ACTIVITY_OPEN =
"TRANSIT_TRANSLUCENT_ACTIVITY_OPEN";
public static final String TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE =
"TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE";
public static final String APP_STATE_IDLE = "APP_STATE_IDLE";
public static final String APP_STATE_RUNNING = "APP_STATE_RUNNING";
private static final String DUMPSYS_WINDOW = "dumpsys window -a --proto";
private static final String STARTING_WINDOW_PREFIX = "Starting ";
private static final String DEBUGGER_WINDOW_PREFIX = "Waiting For Debugger: ";
/** @see WindowManager.LayoutParams */
private static final int TYPE_NAVIGATION_BAR = 2019;
/** @see WindowManager.LayoutParams */
private static final int TYPE_NAVIGATION_BAR_PANEL = 2024;
/** @see WindowManager.LayoutParams */
private static final int TYPE_NOTIFICATION_SHADE = 2040;
private RootWindowContainer mRoot = null;
// Displays in z-order with the top most at the front of the list, starting with primary.
private final List<DisplayContent> mDisplays = new ArrayList<>();
/**
* Root tasks in z-order with the top most at the front of the list, starting with primary
* display.
*/
private final List<Task> mRootTasks = new ArrayList<>();
// Windows in z-order with the top most at the front of the list.
private final List<WindowState> mWindowStates = new ArrayList<>();
private KeyguardControllerState mKeyguardControllerState;
private final List<String> mPendingActivities = new ArrayList<>();
private int mTopFocusedTaskId = -1;
private int mFocusedDisplayId = DEFAULT_DISPLAY;
private String mFocusedWindow = null;
private String mFocusedApp = null;
private Boolean mIsHomeRecentsComponent;
private int mDefaultMinSizeOfResizableTaskDp;
private String mTopResumedActivityRecord = null;
final List<String> mResumedActivitiesInRootTasks = new ArrayList<>();
final List<String> mResumedActivitiesInDisplays = new ArrayList<>();
private Rect mDefaultPinnedStackBounds = new Rect();
private Rect mPinnedStackMovementBounds = new Rect();
private String mInputMethodWindowAppToken = null;
private boolean mDisplayFrozen;
private boolean mSanityCheckFocusedWindow = true;
static String appStateToString(int appState) {
switch (appState) {
case AppTransitionProto.APP_STATE_IDLE:
return "APP_STATE_IDLE";
case AppTransitionProto.APP_STATE_READY:
return "APP_STATE_READY";
case AppTransitionProto.APP_STATE_RUNNING:
return "APP_STATE_RUNNING";
case AppTransitionProto.APP_STATE_TIMEOUT:
return "APP_STATE_TIMEOUT";
default:
fail("Invalid AppTransitionState");
return null;
}
}
static String appTransitionToString(int transition) {
switch (transition) {
case ViewProtoEnums.TRANSIT_UNSET: {
return "TRANSIT_UNSET";
}
case ViewProtoEnums.TRANSIT_NONE: {
return "TRANSIT_NONE";
}
case ViewProtoEnums.TRANSIT_ACTIVITY_OPEN: {
return TRANSIT_ACTIVITY_OPEN;
}
case ViewProtoEnums.TRANSIT_ACTIVITY_CLOSE: {
return TRANSIT_ACTIVITY_CLOSE;
}
case ViewProtoEnums.TRANSIT_TASK_OPEN: {
return TRANSIT_TASK_OPEN;
}
case ViewProtoEnums.TRANSIT_TASK_CLOSE: {
return TRANSIT_TASK_CLOSE;
}
case ViewProtoEnums.TRANSIT_TASK_TO_FRONT: {
return "TRANSIT_TASK_TO_FRONT";
}
case ViewProtoEnums.TRANSIT_TASK_TO_BACK: {
return "TRANSIT_TASK_TO_BACK";
}
case ViewProtoEnums.TRANSIT_WALLPAPER_CLOSE: {
return TRANSIT_WALLPAPER_CLOSE;
}
case ViewProtoEnums.TRANSIT_WALLPAPER_OPEN: {
return TRANSIT_WALLPAPER_OPEN;
}
case ViewProtoEnums.TRANSIT_WALLPAPER_INTRA_OPEN: {
return TRANSIT_WALLPAPER_INTRA_OPEN;
}
case ViewProtoEnums.TRANSIT_WALLPAPER_INTRA_CLOSE: {
return TRANSIT_WALLPAPER_INTRA_CLOSE;
}
case ViewProtoEnums.TRANSIT_TASK_OPEN_BEHIND: {
return "TRANSIT_TASK_OPEN_BEHIND";
}
case ViewProtoEnums.TRANSIT_ACTIVITY_RELAUNCH: {
return "TRANSIT_ACTIVITY_RELAUNCH";
}
case ViewProtoEnums.TRANSIT_DOCK_TASK_FROM_RECENTS: {
return "TRANSIT_DOCK_TASK_FROM_RECENTS";
}
case ViewProtoEnums.TRANSIT_KEYGUARD_GOING_AWAY: {
return TRANSIT_KEYGUARD_GOING_AWAY;
}
case ViewProtoEnums.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER: {
return TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
}
case ViewProtoEnums.TRANSIT_KEYGUARD_OCCLUDE: {
return TRANSIT_KEYGUARD_OCCLUDE;
}
case ViewProtoEnums.TRANSIT_KEYGUARD_UNOCCLUDE: {
return TRANSIT_KEYGUARD_UNOCCLUDE;
}
case ViewProtoEnums.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN: {
return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
}
case ViewProtoEnums.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE: {
return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
}
case ViewProtoEnums.TRANSIT_CRASHING_ACTIVITY_CLOSE: {
return "TRANSIT_CRASHING_ACTIVITY_CLOSE";
}
default: {
fail("Invalid lastUsedAppTransition");
return null;
}
}
}
static boolean isValidNavBarType(WindowState navState) {
return TYPE_NAVIGATION_BAR == navState.getType();
}
/**
* For a given WindowContainer, traverse down the hierarchy and add all children of type
* {@code T} to {@code outChildren}.
*/
private static <T extends WindowContainer> void collectDescendantsOfType(Class<T> clazz,
WindowContainer root, List<T> outChildren) {
collectDescendantsOfTypeIf(clazz, t -> true, root, outChildren);
}
/**
* For a given WindowContainer, traverse down the hierarchy and add all children of type
* {@code T} to {@code outChildren} if the child passes the test {@code predicate}.
*/
private static <T extends WindowContainer> void collectDescendantsOfTypeIf(Class<T> clazz,
Predicate<T> predicate, WindowContainer root, List<T> outChildren) {
// Traverse top to bottom
for (int i = root.mChildren.size()-1; i >= 0; i--) {
final WindowContainer child = root.mChildren.get(i);
if (clazz.isInstance(child)) {
if(predicate.test(clazz.cast(child))) {
outChildren.add(clazz.cast(child));
}
}
collectDescendantsOfTypeIf(clazz, predicate, child, outChildren);
}
}
/**
* For a given WindowContainer, traverse down the hierarchy and add all immediate children of
* type {@code T} to {@code outChildren}.
*/
private static <T extends WindowContainer> void collectChildrenOfType(Class<T> clazz,
WindowContainer root, List<T> outChildren) {
for (int i = root.mChildren.size()-1; i >= 0; i--) {
final WindowContainer child = root.mChildren.get(i);
if (clazz.isInstance(child)) {
outChildren.add(clazz.cast(child));
}
}
}
/** Enable/disable the mFocusedWindow check during the computeState.*/
void setSanityCheckWithFocusedWindow(boolean sanityCheckFocusedWindow) {
mSanityCheckFocusedWindow = sanityCheckFocusedWindow;
}
public void computeState() {
// It is possible the system is in the middle of transition to the right state when we get
// the dump. We try a few times to get the information we need before giving up.
int retriesLeft = 3;
boolean retry = false;
byte[] dump = null;
log("==============================");
log(" WindowManagerState ");
log("==============================");
do {
if (retry) {
log("***Incomplete AM state. Retrying...");
// Wait half a second between retries for activity manager to finish transitioning.
SystemClock.sleep(500);
}
dump = executeShellCommand(DUMPSYS_WINDOW);
try {
parseSysDumpProto(dump);
} catch (InvalidProtocolBufferNanoException ex) {
throw new RuntimeException("Failed to parse dumpsys:\n"
+ new String(dump, StandardCharsets.UTF_8), ex);
}
retry = mRootTasks.isEmpty() || mTopFocusedTaskId == -1 || mWindowStates.isEmpty()
|| mFocusedApp == null || (mSanityCheckFocusedWindow && mFocusedWindow == null)
|| (mTopResumedActivityRecord == null
|| mResumedActivitiesInRootTasks.isEmpty())
&& !mKeyguardControllerState.keyguardShowing;
} while (retry && retriesLeft-- > 0);
if (mRootTasks.isEmpty()) {
logE("No root tasks found...");
}
if (mTopFocusedTaskId == -1) {
logE("No focused task found...");
}
if (mTopResumedActivityRecord == null) {
logE("No focused activity found...");
}
if (mResumedActivitiesInRootTasks.isEmpty()) {
logE("No resumed activities found...");
}
if (mWindowStates.isEmpty()) {
logE("No Windows found...");
}
if (mFocusedWindow == null) {
logE("No Focused Window...");
}
if (mFocusedApp == null) {
logE("No Focused App...");
}
}
private byte[] executeShellCommand(String cmd) {
try {
ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
.executeShellCommand(cmd);
byte[] buf = new byte[512];
int bytesRead;
FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
while ((bytesRead = fis.read(buf)) != -1) {
stdout.write(buf, 0, bytesRead);
}
fis.close();
return stdout.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/** Update WindowManagerState state for a newly added DisplayContent. */
private void updateForDisplayContent(DisplayContent display) {
if (display.mResumedActivity != null) {
mResumedActivitiesInDisplays.add(display.mResumedActivity);
}
for (int i = 0; i < display.mRootTasks.size(); i++) {
Task task = display.mRootTasks.get(i);
mRootTasks.add(task);
addResumedActivity(task);
}
if (display.mDefaultPinnedStackBounds != null) {
mDefaultPinnedStackBounds = display.mDefaultPinnedStackBounds;
mPinnedStackMovementBounds = display.mPinnedStackMovementBounds;
}
}
private void addResumedActivity(Task task) {
final int numChildTasks = task.mTasks.size();
if (numChildTasks > 0) {
for (int i = numChildTasks - 1; i >=0; i--) {
addResumedActivity(task.mTasks.get(i));
}
} else if (task.mResumedActivity != null) {
mResumedActivitiesInRootTasks.add(task.mResumedActivity);
}
}
private void parseSysDumpProto(byte[] sysDump) throws InvalidProtocolBufferNanoException {
reset();
WindowManagerServiceDumpProto state = WindowManagerServiceDumpProto.parseFrom(sysDump);
final RootWindowContainerProto root = state.rootWindowContainer;
if (state.focusedWindow != null) {
mFocusedWindow = state.focusedWindow.title;
}
mRoot = new RootWindowContainer(root);
collectDescendantsOfType(DisplayContent.class, mRoot, mDisplays);
for (int i = 0; i < mDisplays.size(); i++) {
DisplayContent display = mDisplays.get(i);
updateForDisplayContent(display);
}
mKeyguardControllerState = new KeyguardControllerState(root.keyguardController);
mFocusedApp = state.focusedApp;
mFocusedDisplayId = state.focusedDisplayId;
final DisplayContent focusedDisplay = getDisplay(mFocusedDisplayId);
if (focusedDisplay != null) {
mTopFocusedTaskId = focusedDisplay.mFocusedRootTaskId;
mTopResumedActivityRecord = focusedDisplay.mResumedActivity;
}
mIsHomeRecentsComponent = new Boolean(root.isHomeRecentsComponent);
mDefaultMinSizeOfResizableTaskDp = root.defaultMinSizeResizableTask;
for (int i = 0; i < root.pendingActivities.length; i++) {
mPendingActivities.add(root.pendingActivities[i].title);
}
collectDescendantsOfType(WindowState.class, mRoot, mWindowStates);
if (state.inputMethodWindow != null) {
mInputMethodWindowAppToken = Integer.toHexString(state.inputMethodWindow.hashCode);
}
mDisplayFrozen = state.displayFrozen;
}
private void reset() {
mRoot = null;
mDisplays.clear();
mRootTasks.clear();
mWindowStates.clear();
mTopFocusedTaskId = -1;
mFocusedDisplayId = DEFAULT_DISPLAY;
mFocusedWindow = null;
mFocusedApp = null;
mTopResumedActivityRecord = null;
mResumedActivitiesInRootTasks.clear();
mResumedActivitiesInDisplays.clear();
mKeyguardControllerState = null;
mIsHomeRecentsComponent = null;
mPendingActivities.clear();
mDefaultPinnedStackBounds.setEmpty();
mPinnedStackMovementBounds.setEmpty();
mInputMethodWindowAppToken = null;
mDisplayFrozen = false;
}
public String getFocusedApp() {
return mFocusedApp;
}
public String getFocusedWindow() {
return mFocusedWindow;
}
/** @return Whether the home activity is the recents component. */
boolean isHomeRecentsComponent() {
if (mIsHomeRecentsComponent == null) {
computeState();
}
return mIsHomeRecentsComponent;
}
DisplayContent getDisplay(int displayId) {
for (DisplayContent display : mDisplays) {
if (display.mId == displayId) {
return display;
}
}
return null;
}
@Nullable
DisplayArea getTaskDisplayArea(ComponentName activityName) {
final List<DisplayArea> result = new ArrayList<>();
for (DisplayContent display : mDisplays) {
final DisplayArea tda = display.getTaskDisplayArea(activityName);
if (tda != null) {
result.add(tda);
}
}
assertWithMessage("There must be exactly one activity among all TaskDisplayAreas.")
.that(result.size()).isAtMost(1);
return result.stream().findFirst().orElse(null);
}
@Nullable
DisplayArea getDisplayArea(String windowName) {
final List<DisplayArea> result = new ArrayList<>();
for (DisplayContent display : mDisplays) {
final DisplayArea da = display.getDisplayArea(windowName);
if (da != null) {
result.add(da);
}
}
assertWithMessage("There must be exactly one window among all DisplayAreas.")
.that(result.size()).isAtMost(1);
return result.stream().findFirst().orElse(null);
}
int getFrontRootTaskId(int displayId) {
return getDisplay(displayId).mRootTasks.get(0).mRootTaskId;
}
public int getFrontRootTaskActivityType(int displayId) {
return getDisplay(displayId).mRootTasks.get(0).getActivityType();
}
public int getFrontRootTaskWindowingMode(int displayId) {
return getDisplay(displayId).mRootTasks.get(0).getWindowingMode();
}
public String getTopActivityName(int displayId) {
if (!getDisplay(displayId).mRootTasks.isEmpty()) {
final Task topRootTask = getDisplay(displayId).mRootTasks.get(0);
final Task topTask = topRootTask.getTopTask();
if (!topTask.mActivities.isEmpty()) {
return topTask.mActivities.get(0).name;
}
}
return null;
}
int getFocusedTaskId() {
return mTopFocusedTaskId;
}
public int getFocusedRootTaskActivityType() {
final Task rootTask = getRootTask(mTopFocusedTaskId);
return rootTask != null ? rootTask.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
}
public int getFocusedRootTaskWindowingMode() {
final Task rootTask = getRootTask(mTopFocusedTaskId);
return rootTask != null ? rootTask.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
}
public String getFocusedActivity() {
return mTopResumedActivityRecord;
}
public int getResumedActivitiesCount() {
return mResumedActivitiesInRootTasks.size();
}
public int getResumedActivitiesCountInPackage(String packageName) {
final String componentPrefix = packageName + "/";
int count = 0;
for (int i = mDisplays.size() - 1; i >= 0; --i) {
final ArrayList<Task> rootTasks = mDisplays.get(i).getRootTasks();
for (int j = rootTasks.size() - 1; j >= 0; --j) {
final String resumedActivity = rootTasks.get(j).mResumedActivity;
if (resumedActivity != null && resumedActivity.startsWith(componentPrefix)) {
count++;
}
}
}
return count;
}
public String getResumedActivityOnDisplay(int displayId) {
return getDisplay(displayId).mResumedActivity;
}
public KeyguardControllerState getKeyguardControllerState() {
return mKeyguardControllerState;
}
public boolean containsRootTasks(int windowingMode, int activityType) {
return countRootTasks(windowingMode, activityType) > 0;
}
public int countRootTasks(int windowingMode, int activityType) {
int count = 0;
for (Task rootTask : mRootTasks) {
if (activityType != ACTIVITY_TYPE_UNDEFINED
&& activityType != rootTask.getActivityType()) {
continue;
}
if (windowingMode != WINDOWING_MODE_UNDEFINED
&& windowingMode != rootTask.getWindowingMode()) {
continue;
}
++count;
}
return count;
}
public Task getRootTask(int taskId) {
for (Task rootTask : mRootTasks) {
if (taskId == rootTask.mRootTaskId) {
return rootTask;
}
}
return null;
}
public Task getRootTaskByActivityType(int activityType) {
for (Task rootTask : mRootTasks) {
if (activityType == rootTask.getActivityType()) {
return rootTask;
}
}
return null;
}
public int getStandardTaskCountByWindowingMode(int windowingMode) {
int count = 0;
for (Task rootTask : mRootTasks) {
if (rootTask.getActivityType() != ACTIVITY_TYPE_STANDARD) {
continue;
}
if (rootTask.getWindowingMode() == windowingMode) {
count += rootTask.mTasks.isEmpty() ? 1 : rootTask.mTasks.size();
}
}
return count;
}
/** Gets the position of root task on its display with the given {@code activityType}. */
int getRootTaskIndexByActivityType(int activityType) {
for (DisplayContent display : mDisplays) {
for (int i = 0; i < display.mRootTasks.size(); i++) {
if (activityType == display.mRootTasks.get(i).getActivityType()) {
return i;
}
}
}
return -1;
}
/** Gets the root task on its display with the given {@code activityName}. */
@Nullable
Task getRootTaskByActivity(ComponentName activityName) {
for (DisplayContent display : mDisplays) {
for (int i = display.mRootTasks.size() - 1; i >= 0; --i) {
final Task rootTask = display.mRootTasks.get(i);
if (rootTask.containsActivity(activityName)) return rootTask;
}
}
return null;
}
/** Get display id by activity on it. */
public int getDisplayByActivity(ComponentName activityComponent) {
final Task task = getTaskByActivity(activityComponent);
if (task == null) {
return -1;
}
return getRootTask(task.mRootTaskId).mDisplayId;
}
List<DisplayContent> getDisplays() {
return new ArrayList<>(mDisplays);
}
public List<Task> getRootTasks() {
return new ArrayList<>(mRootTasks);
}
public int getRootTaskCount() {
return mRootTasks.size();
}
public int getDisplayCount() {
return mDisplays.size();
}
public boolean containsActivity(ComponentName activityName) {
for (Task rootTask : mRootTasks) {
if (rootTask.containsActivity(activityName)) return true;
}
return false;
}
public boolean containsNoneOf(Iterable<ComponentName> activityNames) {
for (ComponentName activityName : activityNames) {
for (Task rootTask : mRootTasks) {
if (rootTask.containsActivity(activityName)) return false;
}
}
return true;
}
public boolean containsActivityInWindowingMode(ComponentName activityName, int windowingMode) {
for (Task rootTask : mRootTasks) {
final Activity activity = rootTask.getActivity(activityName);
if (activity != null && activity.getWindowingMode() == windowingMode) {
return true;
}
}
return false;
}
public boolean isActivityVisible(ComponentName activityName) {
for (Task rootTask : mRootTasks) {
final Activity activity = rootTask.getActivity(activityName);
if (activity != null) return activity.visible;
}
return false;
}
public boolean isActivityTranslucent(ComponentName activityName) {
for (Task rootTask : mRootTasks) {
final Activity activity = rootTask.getActivity(activityName);
if (activity != null) return activity.translucent;
}
return false;
}
public boolean isBehindOpaqueActivities(ComponentName activityName) {
final String fullName = getActivityName(activityName);
for (Task rootTask : mRootTasks) {
final Activity activity =
rootTask.getActivity((a) -> a.name.equals(fullName) || !a.translucent);
if (activity != null) {
if (activity.name.equals(fullName)) {
return false;
}
if (!activity.translucent) {
return true;
}
}
}
return false;
}
public boolean containsStartedActivities() {
for (Task rootTask : mRootTasks) {
final Activity activity = rootTask.getActivity(
(a) -> !a.state.equals(STATE_STOPPED) && !a.state.equals(STATE_DESTROYED));
if (activity != null) return true;
}
return false;
}
boolean hasActivityState(ComponentName activityName, String activityState) {
for (Task rootTask : mRootTasks) {
final Activity activity = rootTask.getActivity(activityName);
if (activity != null) return activity.state.equals(activityState);
}
return false;
}
int getActivityProcId(ComponentName activityName) {
for (Task rootTask : mRootTasks) {
final Activity activity = rootTask.getActivity(activityName);
if (activity != null) return activity.procId;
}
return -1;
}
boolean isRecentsActivityVisible() {
final Activity recentsActivity = getRecentsActivity();
return recentsActivity != null && recentsActivity.visible;
}
ComponentName getHomeActivityName() {
Activity activity = getHomeActivity();
if (activity == null) {
return null;
}
return ComponentName.unflattenFromString(activity.name);
}
Task getDreamTask() {
final Task dreamRootTask = getRootTaskByActivityType(ACTIVITY_TYPE_DREAM);
if (dreamRootTask != null) {
return dreamRootTask.getTopTask();
}
return null;
}
Task getHomeTask() {
final Task homeRootTask = getRootTaskByActivityType(ACTIVITY_TYPE_HOME);
if (homeRootTask != null) {
return homeRootTask.getTopTask();
}
return null;
}
private Task getRecentsTask() {
final Task recentsRootTask = getRootTaskByActivityType(ACTIVITY_TYPE_RECENTS);
if (recentsRootTask != null) {
return recentsRootTask.getTopTask();
}
return null;
}
private Activity getHomeActivity() {
final Task homeTask = getHomeTask();
return homeTask != null ? homeTask.mActivities.get(homeTask.mActivities.size() - 1) : null;
}
private Activity getRecentsActivity() {
final Task recentsTask = getRecentsTask();
return recentsTask != null ? recentsTask.mActivities.get(recentsTask.mActivities.size() - 1)
: null;
}
public int getRootTaskIdByActivity(ComponentName activityName) {
final Task task = getTaskByActivity(activityName);
return (task == null) ? INVALID_TASK_ID : task.mRootTaskId;
}
public Task getTaskByActivity(ComponentName activityName) {
return getTaskByActivity(
activityName, WINDOWING_MODE_UNDEFINED, new int[]{ INVALID_TASK_ID });
}
public Task getTaskByActivity(ComponentName activityName, int[] excludeTaskIds) {
return getTaskByActivity(activityName, WINDOWING_MODE_UNDEFINED, excludeTaskIds);
}
private Task getTaskByActivity(ComponentName activityName, int windowingMode,
int[] excludeTaskIds) {
Activity activity = getActivity(activityName, windowingMode, excludeTaskIds);
return activity == null ? null : activity.getTask();
}
@Nullable
public TaskFragment getTaskFragmentByActivity(ComponentName activityName) {
return getActivity(activityName).getTaskFragment();
}
public Activity getActivity(ComponentName activityName) {
return getActivity(activityName, WINDOWING_MODE_UNDEFINED, new int[]{ INVALID_TASK_ID });
}
private Activity getActivity(ComponentName activityName, int windowingMode,
int[] excludeTaskIds) {
for (Task rootTask : mRootTasks) {
if (windowingMode == WINDOWING_MODE_UNDEFINED
|| windowingMode == rootTask.getWindowingMode()) {
Activity activity = rootTask.getActivity(activityName, excludeTaskIds);
if (activity != null) return activity;
}
}
return null;
}
/**
* Get the number of activities in the task, with the option to count only activities with
* specific name.
* @param taskId Id of the task where we're looking for the number of activities.
* @param activityName Optional name of the activity we're interested in.
* @return Number of all activities in the task if activityName is {@code null}, otherwise will
* report number of activities that have specified name.
*/
public int getActivityCountInTask(int taskId, @Nullable ComponentName activityName) {
// If activityName is null, count all activities in the task.
// Otherwise count activities that have specified name.
for (Task rootTask : mRootTasks) {
final Task task = rootTask.getTask(taskId);
if (task == null) continue;
if (activityName == null) {
return task.mActivities.size();
}
final String fullName = getActivityName(activityName);
int count = 0;
for (Activity activity : task.mActivities) {
if (activity.name.equals(fullName)) {
count++;
}
}
return count;
}
return 0;
}
public int getRootTasksCount() {
return mRootTasks.size();
}
public int getRootTasksCount(int displayId) {
return getRootTasksCount(t -> t.mDisplayId == displayId);
}
/**
* Count root tasks filtered by the predicate passed as argument.
*/
public int getRootTasksCount(Predicate<? super Task> predicate) {
return (int) mRootTasks.stream().filter(predicate).count();
}
boolean pendingActivityContain(ComponentName activityName) {
return mPendingActivities.contains(getActivityName(activityName));
}
// Get the logical display size of the default display.
public static Point getLogicalDisplaySize() {
WindowManagerState mWmState = new WindowManagerState();
mWmState.computeState();
Rect size = mWmState.getDisplay(DEFAULT_DISPLAY).getDisplayRect();
return new Point(size.width(), size.height());
}
String getDefaultDisplayLastTransition() {
return getDisplay(DEFAULT_DISPLAY).getLastTransition();
}
String getDefaultDisplayAppTransitionState() {
return getDisplay(DEFAULT_DISPLAY).getAppTransitionState();
}
public List<WindowState> getMatchingVisibleWindowState(final String windowName) {
return getMatchingWindows(ws -> ws.isSurfaceShown() && windowName.equals(ws.getName()))
.collect(Collectors.toList());
}
private Stream<WindowState> getMatchingWindows(Predicate<WindowState> condition) {
return mWindowStates.stream().filter(condition);
}
@Nullable
public WindowState getWindowByPackageName(String packageName, int windowType) {
final List<WindowState> windowList = getWindowsByPackageName(packageName, windowType);
return windowList.isEmpty() ? null : windowList.get(0);
}
public List<WindowState> getWindowsByPackageName(String packageName, int... restrictToTypes) {
return getMatchingWindows(ws ->
(ws.getName().equals(packageName) || ws.getName().startsWith(packageName + "/"))
&& Arrays.stream(restrictToTypes).anyMatch(type -> type == ws.getType()))
.collect(Collectors.toList());
}
public boolean hasNotificationShade() {
computeState();
return !getMatchingWindowType(TYPE_NOTIFICATION_SHADE).isEmpty();
}
List<WindowState> getWindows() {
return new ArrayList<>(mWindowStates);
}
List<WindowState> getMatchingWindowType(int type) {
return getMatchingWindows(ws -> type == ws.mType).collect(Collectors.toList());
}
List<String> getMatchingWindowTokens(final String windowName) {
return getMatchingWindows(ws -> windowName.equals(ws.getName()))
.map(WindowState::getToken)
.collect(Collectors.toList());
}
List<WindowState> getAllNavigationBarStates() {
return getMatchingWindows(WindowManagerState::isValidNavBarType)
.collect(Collectors.toList());
}
@Nullable
List<WindowState> getAndAssertNavBarWindowsOnDisplay(int displayId, int expectedNavBarCount) {
List<WindowState> navWindows = getMatchingWindows(ws -> isValidNavBarType(ws)
&& ws.getDisplayId() == displayId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
// We may need some time to wait for nav bar showing.
// It's Ok to get less that expected nav bars here.
assertTrue("There should be at most expectedNavBarCount navigation bar on a display",
navWindows.size() <= expectedNavBarCount);
return navWindows.size() == expectedNavBarCount ? navWindows : null;
}
WindowState getWindowStateForAppToken(String appToken) {
return getMatchingWindows(ws -> ws.getToken().equals(appToken))
.findFirst()
.orElse(null);
}
String getFrontWindow() {
if (mWindowStates == null || mWindowStates.isEmpty()) {
return null;
}
return mWindowStates.get(0).getName();
}
/** Check if there exists a window record with matching windowName. */
public boolean containsWindow(String windowName) {
for (WindowState window : mWindowStates) {
if (window.getName().equals(windowName)) {
return true;
}
}
return false;
}
/** Check if at least one window which matches the specified name has shown it's surface. */
public boolean isWindowSurfaceShown(String windowName) {
for (WindowState window : mWindowStates) {
if (window.getName().equals(windowName)) {
if (window.isSurfaceShown()) {
return true;
}
}
}
return false;
}
/** Check if at least one window which matches provided window name is visible. */
public boolean isWindowVisible(String windowName) {
for (WindowState window : mWindowStates) {
if (window.getName().equals(windowName)) {
if (window.isVisible()) {
return true;
}
}
}
return false;
}
public boolean allWindowSurfacesShown(String windowName) {
boolean allShown = false;
for (WindowState window : mWindowStates) {
if (window.getName().equals(windowName)) {
if (!window.isSurfaceShown()) {
log("[VISIBLE] not visible" + windowName);
return false;
}
log("[VISIBLE] visible" + windowName);
allShown = true;
}
}
return allShown;
}
/** Checks whether the display contains the given activity. */
boolean hasActivityInDisplay(int displayId, ComponentName activityName) {
for (Task rootTask : getDisplay(displayId).getRootTasks()) {
if (rootTask.containsActivity(activityName)) {
return true;
}
}
return false;
}
WindowState findFirstWindowWithType(int type) {
for (WindowState window : mWindowStates) {
if (window.getType() == type) {
return window;
}
}
return null;
}
public int getZOrder(WindowState w) {
return mWindowStates.size() - mWindowStates.indexOf(w);
}
Task getStandardRootTaskByWindowingMode(int windowingMode) {
for (Task task : mRootTasks) {
if (task.getActivityType() != ACTIVITY_TYPE_STANDARD) {
continue;
}
if (task.getWindowingMode() == windowingMode) {
return task;
}
}
return null;
}
WindowManagerState.WindowState getInputMethodWindowState() {
return getWindowStateForAppToken(mInputMethodWindowAppToken);
}
public boolean isDisplayFrozen() {
return mDisplayFrozen;
}
public int getRotation() {
return getDisplay(DEFAULT_DISPLAY).mRotation;
}
public int getLastOrientation() {
return getDisplay(DEFAULT_DISPLAY).mLastOrientation;
}
public int getFocusedDisplayId() {
return mFocusedDisplayId;
}
public static class DisplayContent extends DisplayArea {
public int mId;
ArrayList<Task> mRootTasks = new ArrayList<>();
int mFocusedRootTaskId;
String mResumedActivity;
boolean mSingleTaskInstance;
Rect mDefaultPinnedStackBounds = null;
Rect mPinnedStackMovementBounds = null;
private Rect mDisplayRect = new Rect();
private Rect mAppRect = new Rect();
private int mDpi;
private int mFlags;
private String mName;
private int mSurfaceSize;
private String mFocusedApp;
private String mLastTransition;
private String mAppTransitionState;
private int mRotation;
private boolean mFrozenToUserRotation;
private int mUserRotation;
private int mFixedToUserRotationMode;
private int mLastOrientation;
DisplayContent(DisplayContentProto proto) {
super(proto.rootDisplayArea);
mId = proto.id;
mFocusedRootTaskId = proto.focusedRootTaskId;
mSingleTaskInstance = proto.singleTaskInstance;
if (proto.resumedActivity != null) {
mResumedActivity = proto.resumedActivity.title;
}
addRootTasks();
mDpi = proto.dpi;
DisplayInfoProto infoProto = proto.displayInfo;
if (infoProto != null) {
mDisplayRect.set(0, 0, infoProto.logicalWidth, infoProto.logicalHeight);
mAppRect.set(0, 0, infoProto.appWidth, infoProto.appHeight);
mName = infoProto.name;
mFlags = infoProto.flags;
}
final DisplayFramesProto displayFramesProto = proto.displayFrames;
mSurfaceSize = proto.surfaceSize;
mFocusedApp = proto.focusedApp;
final AppTransitionProto appTransitionProto = proto.appTransition;
int appState = 0;
int lastTransition = 0;
if (appTransitionProto != null) {
appState = appTransitionProto.appTransitionState;
lastTransition = appTransitionProto.lastUsedAppTransition;
}
mAppTransitionState = appStateToString(appState);
mLastTransition = appTransitionToString(lastTransition);
PinnedTaskControllerProto pinnedTaskProto = proto.pinnedTaskController;
if (pinnedTaskProto != null) {
mDefaultPinnedStackBounds = extract(pinnedTaskProto.defaultBounds);
mPinnedStackMovementBounds = extract(pinnedTaskProto.movementBounds);
}
final DisplayRotationProto rotationProto = proto.displayRotation;
if (rotationProto != null) {
mRotation = rotationProto.rotation;
mFrozenToUserRotation = rotationProto.frozenToUserRotation;
mUserRotation = rotationProto.userRotation;
mFixedToUserRotationMode = rotationProto.fixedToUserRotationMode;
mLastOrientation = rotationProto.lastOrientation;
}
}
public String getName() {
return mName;
}
private void addRootTasks() {
// TODO(b/149338177): figure out how CTS tests deal with organizer. For now,
// don't treat them as regular root tasks
collectDescendantsOfTypeIf(Task.class, t -> t.isRootTask(), this,
mRootTasks);
ArrayList<Task> rootOrganizedTasks = new ArrayList<>();
for (int i = mRootTasks.size() -1; i >= 0; --i) {
final Task task = mRootTasks.get(i);
// Skip tasks created by an organizer
if (task.mCreatedByOrganizer) {
mRootTasks.remove(task);
rootOrganizedTasks.add(task);
}
}
// Add root tasks controlled by an organizer
while (rootOrganizedTasks.size() > 0) {
final Task task = rootOrganizedTasks.remove(0);
for (int i = task.mChildren.size() - 1; i >= 0; i--) {
final Task child = (Task) task.mChildren.get(i);
if (!child.mCreatedByOrganizer) {
mRootTasks.add(child);
} else {
rootOrganizedTasks.add(child);
}
}
}
}
boolean containsActivity(ComponentName activityName) {
for (Task task : mRootTasks) {
if (task.containsActivity(activityName)) return true;
}
return false;
}
List<DisplayArea> getAllTaskDisplayAreas() {
final List<DisplayArea> taskDisplayAreas = new ArrayList<>();
collectDescendantsOfTypeIf(DisplayArea.class, DisplayArea::isTaskDisplayArea, this,
taskDisplayAreas);
return taskDisplayAreas;
}
@Nullable
DisplayArea getTaskDisplayArea(ComponentName activityName) {
final List<DisplayArea> taskDisplayAreas = getAllTaskDisplayAreas();
List<DisplayArea> result = taskDisplayAreas.stream().filter(
tda -> tda.containsActivity(activityName))
.collect(Collectors.toList());
assertWithMessage("There must be exactly one activity among all TaskDisplayAreas.")
.that(result.size()).isAtMost(1);
return result.stream().findFirst().orElse(null);
}
List<DisplayArea> getAllChildDisplayAreas() {
final List<DisplayArea> displayAreas = new ArrayList<>();
collectDescendantsOfType(DisplayArea.class,this, displayAreas);
return displayAreas;
}
@Nullable
DisplayArea getDisplayArea(String windowName) {
List<DisplayArea> displayAreas = new ArrayList<>();
final Predicate<DisplayArea> p = da -> {
final boolean containsChildWindowToken = !da.mChildren.isEmpty()
&& da.mChildren.get(0) instanceof WindowToken;
return !da.isTaskDisplayArea() && containsChildWindowToken;
};
collectDescendantsOfTypeIf(DisplayArea.class, p, this, displayAreas);
List<DisplayArea> result = displayAreas.stream().filter(
da -> da.containsWindow(windowName))
.collect(Collectors.toList());
assertWithMessage("There must be exactly one window among all DisplayAreas.")
.that(result.size()).isAtMost(1);
return result.stream().findFirst().orElse(null);
}
ArrayList<Task> getRootTasks() {
return mRootTasks;
}
int getDpi() {
return mDpi;
}
Rect getDisplayRect() {
return mDisplayRect;
}
public Rect getAppRect() {
return mAppRect;
}
int getFlags() {
return mFlags;
}
int getSurfaceSize() {
return mSurfaceSize;
}
String getFocusedApp() {
return mFocusedApp;
}
String getLastTransition() { return mLastTransition; }
String getAppTransitionState() { return mAppTransitionState; }
@Override
public String toString() {
return "Display #" + mId + ": name=" + mName + " mDisplayRect=" + mDisplayRect
+ " mAppRect=" + mAppRect + " mFlags=" + mFlags;
}
}
public static class Task extends ActivityContainer {
int mTaskId;
int mRootTaskId;
public int mDisplayId;
Rect mLastNonFullscreenBounds;
String mRealActivity;
String mOrigActivity;
ArrayList<Task> mTasks = new ArrayList<>();
/** Contains TaskFragment but not Task children */
ArrayList<TaskFragment> mTaskFragments = new ArrayList<>();
ArrayList<Activity> mActivities = new ArrayList<>();
int mTaskType;
private int mResizeMode;
String mResumedActivity;
boolean mAnimatingBounds;
private int mSurfaceWidth;
private int mSurfaceHeight;
boolean mCreatedByOrganizer;
String mAffinity;
boolean mHasChildPipActivity;
WindowContainer mParent;
Task(TaskProto proto, WindowContainer parent) {
super(proto.taskFragment.windowContainer);
mTaskId = proto.id;
mRootTaskId = proto.rootTaskId;
mParent = parent;
mDisplayId = proto.taskFragment.displayId;
mLastNonFullscreenBounds = extract(proto.lastNonFullscreenBounds);
mRealActivity = proto.realActivity;
mOrigActivity = proto.origActivity;
mTaskType = proto.taskFragment.activityType;
mResizeMode = proto.resizeMode;
mFullscreen = proto.fillsParent;
mBounds = extract(proto.bounds);
mMinWidth = proto.taskFragment.minWidth;
mMinHeight = proto.taskFragment.minHeight;
mAnimatingBounds = proto.animatingBounds;
mSurfaceWidth = proto.surfaceWidth;
mSurfaceHeight = proto.surfaceHeight;
mCreatedByOrganizer = proto.createdByOrganizer;
mAffinity = proto.affinity;
mHasChildPipActivity = proto.hasChildPipActivity;
if (proto.resumedActivity != null) {
mResumedActivity = proto.resumedActivity.title;
}
collectChildrenOfType(Task.class, this, mTasks);
collectChildrenOfType(TaskFragment.class, this, mTaskFragments);
collectChildrenOfType(Activity.class, this, mActivities);
}
boolean isEmpty() {
return mTasks.isEmpty() && mTaskFragments.isEmpty() && mActivities.isEmpty();
}
/** Gets the pure parent TaskFragment if exist. */
public TaskFragment getParentTaskFragment() {
if (mParent instanceof TaskFragment) {
return (TaskFragment) mParent;
}
if (mParent instanceof Task) {
return ((Task) mParent).getParentTaskFragment();
}
// If the parent is a TaskDisplayArea, it means this Task doesn't have TaskFragment
// parent.
return null;
}
public int getResizeMode() {
return mResizeMode;
}
public int getTaskId() {
return mTaskId;
}
boolean isRootTask() {
return mTaskId == mRootTaskId;
}
boolean isLeafTask() {
return mTasks.size() == 0;
}
public int getRootTaskId() {
return mRootTaskId;
}
int getSurfaceWidth() {
return mSurfaceWidth;
}
int getSurfaceHeight() {
return mSurfaceHeight;
}
public String getAffinity() { return mAffinity; }
public ArrayList<Activity> getActivities() {
return mActivities;
}
/** @return the top task in the root task. */
Task getTopTask() {
// NOTE: Unlike the WindowManager internals, we dump the state from top to bottom,
// so the indices are inverted
return getTask((t) -> true);
}
public String getResumedActivity() {
return mResumedActivity;
}
public List<Task> getTasks() {
return new ArrayList<>(mTasks);
}
/** Returns non-Task leaf {@link TaskFragment} list. */
public List<TaskFragment> getTaskFragments() {
return new ArrayList<>(mTaskFragments);
}
Task getTask(Predicate<Task> predicate) {
for (Task task : mTasks) {
if (predicate.test(task)) return task;
}
return predicate.test(this) ? this : null;
}
Task getTask(int taskId) {
return getTask((t) -> t.mTaskId == taskId);
}
void forAllTasks(Consumer<Task> consumer) {
for (Task task : mTasks) {
consumer.accept(task);
}
consumer.accept(this);
}
Activity getActivity(Predicate<Activity> predicate) {
for (Activity activity : mActivities) {
if (predicate.test(activity)) return activity;
}
for (TaskFragment taskFragment : mTaskFragments) {
final Activity activity = taskFragment.getActivity(predicate);
if (activity != null) return activity;
}
for (Task task : mTasks) {
final Activity activity = task.getActivity(predicate);
if (activity != null) return activity;
}
return null;
}
public Activity getActivity(ComponentName activityName) {
final String fullName = getActivityName(activityName);
return getActivity((activity) -> activity.name.equals(fullName));
}
public Activity getActivity(ComponentName activityName, int[] excludeTaskIds) {
final String fullName = getActivityName(activityName);
return getActivity((activity) -> {
if (!activity.name.equals(fullName)) {
return false;
}
for (int excludeTaskId : excludeTaskIds) {
if (activity.getTask().mTaskId == excludeTaskId) {
return false;
}
}
return true;
});
}
boolean containsActivity(ComponentName activityName) {
return getActivity(activityName) != null;
}
@Override
int getActivityType() {
return mTaskType;
}
}
public static class TaskFragment extends ActivityContainer {
public int mDisplayId;
Task mParentTask;
ArrayList<Task> mTasks = new ArrayList<>();
ArrayList<TaskFragment> mTaskFragments = new ArrayList<>();
ArrayList<Activity> mActivities = new ArrayList<>();
int mTaskFragmentType;
TaskFragment(TaskFragmentProto proto, WindowContainer parent) {
super(proto.windowContainer);
mParentTask = (Task) parent;
mDisplayId = proto.displayId;
mTaskFragmentType = proto.activityType;
mMinWidth = proto.minWidth;
mMinHeight = proto.minHeight;
collectChildrenOfType(Task.class, this, mTasks);
collectChildrenOfType(TaskFragment.class, this, mTaskFragments);
collectChildrenOfType(Activity.class, this, mActivities);
}
public List<Task> getTasks() {
return mTasks;
}
/** Returns non-Task TaskFragment children. */
public List<TaskFragment> getTaskFragments() {
return mTaskFragments;
}
public List<Activity> getActivities() {
return mActivities;
}
Activity getActivity(Predicate<Activity> predicate) {
for (Activity activity : mActivities) {
if (predicate.test(activity)) {
return activity;
}
}
for (TaskFragment taskFragment : mTaskFragments) {
final Activity activity = taskFragment.getActivity(predicate);
if (activity != null) {
return activity;
}
}
for (Task task : mTasks) {
final Activity activity = task.getActivity(predicate);
if (activity != null) {
return activity;
}
}
return null;
}
@Override
int getActivityType() {
return mTaskFragmentType;
}
}
public static class Activity extends ActivityContainer {
String name;
String state;
boolean visible;
boolean frontOfTask;
boolean inSizeCompatMode;
float minAspectRatio;
boolean providesMaxBounds;
int procId = -1;
public boolean translucent;
private WindowContainer mParent;
Activity(ActivityRecordProto proto, WindowContainer parent) {
super(proto.windowToken.windowContainer);
name = proto.name;
state = proto.state;
visible = proto.visible;
frontOfTask = proto.frontOfTask;
inSizeCompatMode = proto.inSizeCompatMode;
minAspectRatio = proto.minAspectRatio;
providesMaxBounds = proto.providesMaxBounds;
if (proto.procId != 0) {
procId = proto.procId;
}
translucent = proto.translucent;
mParent = parent;
}
@NonNull
public Task getTask() {
if (mParent instanceof Task) {
return (Task) mParent;
}
return ((TaskFragment) mParent).mParentTask;
}
@Nullable
public TaskFragment getTaskFragment() {
if (mParent instanceof TaskFragment) {
return (TaskFragment) mParent;
}
return ((Task) mParent).getParentTaskFragment();
}
public String getName() {
return name;
}
public String getState() {
return state;
}
public boolean inSizeCompatMode() {
return inSizeCompatMode;
}
public float getMinAspectRatio() {
return minAspectRatio;
}
public boolean providesMaxBounds() {
return providesMaxBounds;
}
@Override
public Rect getBounds() {
if (mBounds == null) {
return mFullConfiguration.windowConfiguration.getBounds();
}
return mBounds;
}
public Rect getMaxBounds() {
return mFullConfiguration.windowConfiguration.getMaxBounds();
}
public Rect getAppBounds() {
return mFullConfiguration.windowConfiguration.getAppBounds();
}
}
static abstract class ActivityContainer extends WindowContainer {
protected boolean mFullscreen;
protected Rect mBounds;
protected int mMinWidth = -1;
protected int mMinHeight = -1;
ActivityContainer(WindowContainerProto proto) {
super(proto);
}
public Rect getBounds() {
return mBounds;
}
boolean isFullscreen() {
return mFullscreen;
}
int getMinWidth() {
return mMinWidth;
}
int getMinHeight() {
return mMinHeight;
}
}
static class KeyguardControllerState {
boolean aodShowing = false;
boolean keyguardShowing = false;
SparseArray<Boolean> mKeyguardOccludedStates = new SparseArray<>();
KeyguardControllerState(KeyguardControllerProto proto) {
if (proto != null) {
aodShowing = proto.aodShowing;
keyguardShowing = proto.keyguardShowing;
for (int i = 0; i < proto.keyguardOccludedStates.length; i++) {
mKeyguardOccludedStates.append(proto.keyguardOccludedStates[i].displayId,
proto.keyguardOccludedStates[i].keyguardOccluded);
}
}
}
boolean isKeyguardOccluded(int displayId) {
if (mKeyguardOccludedStates.get(displayId) != null) {
return mKeyguardOccludedStates.get(displayId);
}
return false;
}
}
static class ConfigurationContainer {
final Configuration mOverrideConfiguration = new Configuration();
final Configuration mFullConfiguration = new Configuration();
final Configuration mMergedOverrideConfiguration = new Configuration();
ConfigurationContainer(ConfigurationContainerProto proto) {
if (proto == null) {
return;
}
mOverrideConfiguration.setTo(extract(proto.overrideConfiguration));
mFullConfiguration.setTo(extract(proto.fullConfiguration));
mMergedOverrideConfiguration.setTo(extract(proto.mergedOverrideConfiguration));
}
boolean isWindowingModeCompatible(int requestedWindowingMode) {
if (requestedWindowingMode == WINDOWING_MODE_UNDEFINED) {
return true;
}
return getWindowingMode() == requestedWindowingMode;
}
public int getWindowingMode() {
if (mFullConfiguration == null) {
return WINDOWING_MODE_UNDEFINED;
}
return mFullConfiguration.windowConfiguration.getWindowingMode();
}
int getActivityType() {
if (mFullConfiguration == null) {
return ACTIVITY_TYPE_UNDEFINED;
}
return mFullConfiguration.windowConfiguration.getActivityType();
}
}
public static class RootWindowContainer extends WindowContainer {
RootWindowContainer(RootWindowContainerProto proto) {
super(proto.windowContainer);
}
}
public static class DisplayArea extends WindowContainer {
private final boolean mIsTaskDisplayArea;
private final boolean mIsRootDisplayArea;
private final int mFeatureId;
private final boolean mIsOrganized;
private ArrayList<Activity> mActivities;
private final ArrayList<WindowState> mWindows = new ArrayList<>();
DisplayArea(DisplayAreaProto proto) {
super(proto.windowContainer);
mIsTaskDisplayArea = proto.isTaskDisplayArea;
mIsRootDisplayArea = proto.isRootDisplayArea;
mFeatureId = proto.featureId;
mIsOrganized = proto.isOrganized;
if (mIsTaskDisplayArea) {
mActivities = new ArrayList<>();
collectDescendantsOfType(Activity.class, this, mActivities);
}
collectDescendantsOfType(WindowState.class, this, mWindows);
}
boolean isTaskDisplayArea() {
return mIsTaskDisplayArea;
}
boolean isRootDisplayArea() {
return mIsRootDisplayArea;
}
int getFeatureId() {
return mFeatureId;
}
boolean isOrganized() {
return mIsOrganized;
}
@Override
public Rect getBounds() {
if (mBounds == null) {
return mFullConfiguration.windowConfiguration.getBounds();
}
return mBounds;
}
boolean containsActivity(ComponentName activityName) {
if (!mIsTaskDisplayArea) {
return false;
}
final String fullName = getActivityName(activityName);
for (Activity a : mActivities) {
if (a.name.equals(fullName)) {
return true;
}
}
return false;
}
boolean containsWindow(String windowName) {
for (WindowState w : mWindows) {
if (w.mName.equals(windowName)) {
return true;
}
}
return false;
}
}
public static class WindowToken extends WindowContainer {
WindowToken(WindowTokenProto proto) {
super(proto.windowContainer);
}
}
/**
* Represents WindowContainer classes such as DisplayContent.WindowContainers and
* DisplayContent.NonAppWindowContainers. This can be expanded into a specific class
* if we need track and assert some state in the future.
*/
public static class GenericWindowContainer extends WindowContainer {
GenericWindowContainer(WindowContainerProto proto) {
super(proto);
}
}
static WindowContainer getWindowContainer(WindowContainerChildProto proto,
WindowContainer parent) {
if (proto.displayContent != null) {
return new DisplayContent(proto.displayContent);
}
if (proto.displayArea != null) {
return new DisplayArea(proto.displayArea);
}
if (proto.task != null) {
return new Task(proto.task, parent);
}
if (proto.taskFragment != null) {
return new TaskFragment(proto.taskFragment, parent);
}
if (proto.activity != null) {
return new Activity(proto.activity, parent);
}
if (proto.windowToken != null) {
return new WindowToken(proto.windowToken);
}
if (proto.window != null) {
return new WindowState(proto.window);
}
if (proto.windowContainer != null) {
return new GenericWindowContainer(proto.windowContainer);
}
return null;
}
static abstract class WindowContainer extends ConfigurationContainer {
protected String mName;
protected final String mAppToken;
protected boolean mFullscreen;
protected Rect mBounds;
protected int mOrientation;
protected boolean mVisible;
protected List<WindowState> mSubWindows = new ArrayList<>();
protected List<WindowContainer> mChildren = new ArrayList<>();
WindowContainer(WindowContainerProto proto) {
super(proto.configurationContainer);
IdentifierProto identifierProto = proto.identifier;
mName = identifierProto.title;
mAppToken = Integer.toHexString(identifierProto.hashCode);
mOrientation = proto.orientation;
for (int i = 0; i < proto.children.length; i++) {
final WindowContainer child = getWindowContainer(proto.children[i], this);
if (child != null) {
mChildren.add(child);
}
}
mVisible = proto.visible;
}
@NonNull
public String getName() {
return mName;
}
@NonNull
public String getPackageName() {
int sep = mName.indexOf('/');
return sep == -1 ? mName : mName.substring(0, sep);
}
String getToken() {
return mAppToken;
}
Rect getBounds() {
return mBounds;
}
boolean isFullscreen() {
return mFullscreen;
}
boolean isVisible() {
return mVisible;
}
List<WindowState> getWindows() {
return mSubWindows;
}
}
public static class WindowState extends WindowContainer {
private static final int WINDOW_TYPE_NORMAL = 0;
public static final int WINDOW_TYPE_STARTING = 1;
private static final int WINDOW_TYPE_EXITING = 2;
private static final int WINDOW_TYPE_DEBUGGER = 3;
private final int mWindowType;
private int mType = 0;
private int mDisplayId;
private int mStackId;
private int mLayer;
private boolean mShown;
private Rect mContainingFrame;
private Rect mParentFrame;
private Rect mFrame;
private Rect mCompatFrame;
private Rect mSurfaceInsets = new Rect();
private Rect mGivenContentInsets = new Rect();
private Rect mCrop = new Rect();
private boolean mHasCompatScale;
private float mGlobalScale;
private int mRequestedWidth;
private int mRequestedHeight;
WindowState(WindowStateProto proto) {
super(proto.windowContainer);
mDisplayId = proto.displayId;
mStackId = proto.stackId;
if (proto.attributes != null) {
mType = proto.attributes.type;
}
WindowStateAnimatorProto animatorProto = proto.animator;
if (animatorProto != null) {
if (animatorProto.surface != null) {
WindowSurfaceControllerProto surfaceProto = animatorProto.surface;
mShown = surfaceProto.shown;
mLayer = surfaceProto.layer;
}
mCrop = extract(animatorProto.lastClipRect);
}
mGivenContentInsets = extract(proto.givenContentInsets);
WindowFramesProto windowFramesProto = proto.windowFrames;
if (windowFramesProto != null) {
mFrame = extract(windowFramesProto.frame);
mContainingFrame = extract(windowFramesProto.containingFrame);
mParentFrame = extract(windowFramesProto.parentFrame);
mCompatFrame = extract(windowFramesProto.compatFrame);
}
mSurfaceInsets = extract(proto.surfaceInsets);
if (mName.startsWith(STARTING_WINDOW_PREFIX)) {
mWindowType = WINDOW_TYPE_STARTING;
// Existing code depends on the prefix being removed
mName = mName.substring(STARTING_WINDOW_PREFIX.length());
} else if (proto.animatingExit) {
mWindowType = WINDOW_TYPE_EXITING;
} else if (mName.startsWith(DEBUGGER_WINDOW_PREFIX)) {
mWindowType = WINDOW_TYPE_STARTING;
mName = mName.substring(DEBUGGER_WINDOW_PREFIX.length());
} else {
mWindowType = 0;
}
collectDescendantsOfType(WindowState.class, this, mSubWindows);
mHasCompatScale = proto.hasCompatScale;
mGlobalScale = proto.globalScale;
mRequestedWidth = proto.requestedWidth;
mRequestedHeight = proto.requestedHeight;
}
boolean isStartingWindow() {
return mWindowType == WINDOW_TYPE_STARTING;
}
boolean isExitingWindow() {
return mWindowType == WINDOW_TYPE_EXITING;
}
boolean isDebuggerWindow() {
return mWindowType == WINDOW_TYPE_DEBUGGER;
}
int getDisplayId() {
return mDisplayId;
}
int getStackId() {
return mStackId;
}
Rect getContainingFrame() {
return mContainingFrame;
}
public Rect getFrame() {
return mFrame;
}
Rect getSurfaceInsets() {
return mSurfaceInsets;
}
Rect getGivenContentInsets() {
return mGivenContentInsets;
}
Rect getParentFrame() {
return mParentFrame;
}
public Rect getCompatFrame() {
return mCompatFrame;
}
Rect getCrop() {
return mCrop;
}
public boolean isSurfaceShown() {
return mShown;
}
public int getType() {
return mType;
}
public boolean hasCompatScale() {
return mHasCompatScale;
}
public float getGlobalScale() {
return mGlobalScale;
}
public int getRequestedWidth() {
return mRequestedWidth;
}
public int getRequestedHeight() {
return mRequestedHeight;
}
private String getWindowTypeSuffix(int windowType) {
switch (windowType) {
case WINDOW_TYPE_STARTING:
return " STARTING";
case WINDOW_TYPE_EXITING:
return " EXITING";
case WINDOW_TYPE_DEBUGGER:
return " DEBUGGER";
default:
break;
}
return "";
}
@Override
public String toString() {
return "WindowState: {" + mAppToken + " " + mName
+ getWindowTypeSuffix(mWindowType) + "}" + " type=" + mType
+ " cf=" + mContainingFrame + " pf=" + mParentFrame;
}
public String toLongString() {
return toString() + " f=" + mFrame + " crop=" + mCrop + " isSurfaceShown="
+ isSurfaceShown();
}
}
static int dpToPx(float dp, int densityDpi) {
return (int) (dp * densityDpi / DENSITY_DEFAULT + 0.5f);
}
int defaultMinimalTaskSize(int displayId) {
return dpToPx(mDefaultMinSizeOfResizableTaskDp, getDisplay(displayId).getDpi());
}
int defaultMinimalDisplaySizeForSplitScreen(int displayId) {
return dpToPx(ActivityTaskManager.DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP,
getDisplay(displayId).getDpi());
}
}