blob: 7f165ed83f5452a78dea2a2ebc294f99e6124d4d [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.compatibility.common.util;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.android.server.wm.ActivityRecordProto;
import com.android.server.wm.DisplayAreaProto;
import com.android.server.wm.DisplayContentProto;
import com.android.server.wm.RootWindowContainerProto;
import com.android.server.wm.TaskProto;
import com.android.server.wm.WindowContainerChildProto;
import com.android.server.wm.WindowContainerProto;
import com.android.server.wm.WindowManagerServiceDumpProto;
import com.android.server.wm.WindowStateProto;
import com.android.server.wm.WindowTokenProto;
import com.android.tradefed.device.CollectingByteOutputReceiver;
import com.android.tradefed.device.ITestDevice;
import java.util.ArrayList;
import java.util.List;
public class WindowManagerUtil {
private static final String SHELL_DUMPSYS_WINDOW = "dumpsys window --proto";
@Nonnull
public static WindowManagerServiceDumpProto getDump(@Nonnull ITestDevice device)
throws Exception {
final CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
device.executeShellCommand(SHELL_DUMPSYS_WINDOW, receiver);
return WindowManagerServiceDumpProto.parser().parseFrom(receiver.getOutput());
}
@Nonnull
public static List<WindowStateProto> getWindows(@Nonnull ITestDevice device) throws Exception {
final WindowManagerServiceDumpProto windowManagerServiceDump = getDump(device);
final RootWindowContainerProto rootWindowContainer =
windowManagerServiceDump.getRootWindowContainer();
final WindowContainerProto windowContainer = rootWindowContainer.getWindowContainer();
final List<WindowStateProto> windows = new ArrayList<>();
collectWindowStates(windowContainer, windows);
return windows;
}
@Nullable
public static WindowStateProto getWindowWithTitle(@Nonnull ITestDevice device,
@Nonnull String expectedTitle) throws Exception {
List<WindowStateProto> windows = getWindows(device);
for (WindowStateProto window : windows) {
if (expectedTitle.equals(window.getIdentifier().getTitle())) {
return window;
}
}
return null;
}
public static boolean hasWindowWithTitle(@Nonnull ITestDevice device,
@Nonnull String expectedTitle) throws Exception {
return getWindowWithTitle(device, expectedTitle) != null;
}
/**
* This methods implements a DFS that goes through a tree of window containers and collects all
* the WindowStateProto-s.
*
* WindowContainer is generic class that can hold windows directly or through its children in a
* hierarchy form. WindowContainer's children are WindowContainer as well. This forms a tree of
* WindowContainers.
*
* There are a few classes that extend WindowContainer: Task, DisplayContent, WindowToken etc.
* The one we are interested in is WindowState.
* Since Proto does not have concept of inheritance, {@link TaskProto}, {@link WindowTokenProto}
* etc hold a reference to a {@link WindowContainerProto} (in java code would be {@code super}
* reference).
* {@link WindowContainerProto} may a have a number of children of type
* {@link WindowContainerChildProto}, which represents a generic child of a WindowContainer: a
* WindowContainer can have multiple children of different types stored as a
* {@link WindowContainerChildProto}, but each instance of {@link WindowContainerChildProto} can
* only contain a single type.
*
* For details see /frameworks/base/core/proto/android/server/windowmanagerservice.proto
*/
private static void collectWindowStates(@Nullable WindowContainerProto windowContainer,
@Nonnull List<WindowStateProto> out) {
if (windowContainer == null) return;
final List<WindowContainerChildProto> children = windowContainer.getChildrenList();
for (WindowContainerChildProto child : children) {
if (child.hasWindowContainer()) {
collectWindowStates(child.getWindowContainer(), out);
} else if (child.hasDisplayContent()) {
final DisplayContentProto displayContent = child.getDisplayContent();
for (WindowTokenProto windowToken : displayContent.getOverlayWindowsList()) {
collectWindowStates(windowToken.getWindowContainer(), out);
}
if (displayContent.hasRootDisplayArea()) {
final DisplayAreaProto displayArea = displayContent.getRootDisplayArea();
collectWindowStates(displayArea.getWindowContainer(), out);
}
collectWindowStates(displayContent.getWindowContainer(), out);
} else if (child.hasDisplayArea()) {
final DisplayAreaProto displayArea = child.getDisplayArea();
collectWindowStates(displayArea.getWindowContainer(), out);
} else if (child.hasTask()) {
final TaskProto task = child.getTask();
collectWindowStates(task.getWindowContainer(), out);
} else if (child.hasActivity()) {
final ActivityRecordProto activity = child.getActivity();
if (activity.hasWindowToken()) {
final WindowTokenProto windowToken = activity.getWindowToken();
collectWindowStates(windowToken.getWindowContainer(), out);
}
} else if (child.hasWindowToken()) {
final WindowTokenProto windowToken = child.getWindowToken();
collectWindowStates(windowToken.getWindowContainer(), out);
} else if (child.hasWindow()) {
final WindowStateProto window = child.getWindow();
// We found a Window!
out.add(window);
// ... but still aren't done
collectWindowStates(window.getWindowContainer(), out);
}
}
}
}