1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.compatibility.common.util; 18 19 import javax.annotation.Nonnull; 20 import javax.annotation.Nullable; 21 22 import com.android.server.wm.ActivityRecordProto; 23 import com.android.server.wm.DisplayAreaProto; 24 import com.android.server.wm.DisplayContentProto; 25 import com.android.server.wm.RootWindowContainerProto; 26 import com.android.server.wm.TaskProto; 27 import com.android.server.wm.WindowContainerChildProto; 28 import com.android.server.wm.WindowContainerProto; 29 import com.android.server.wm.WindowManagerServiceDumpProto; 30 import com.android.server.wm.WindowStateProto; 31 import com.android.server.wm.WindowTokenProto; 32 import com.android.tradefed.device.CollectingByteOutputReceiver; 33 import com.android.tradefed.device.ITestDevice; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 38 public class WindowManagerUtil { 39 private static final String SHELL_DUMPSYS_WINDOW = "dumpsys window --proto"; 40 private static final String STATE_RESUMED = "RESUMED"; 41 42 @Nonnull getDump(@onnull ITestDevice device)43 public static WindowManagerServiceDumpProto getDump(@Nonnull ITestDevice device) 44 throws Exception { 45 final CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver(); 46 device.executeShellCommand(SHELL_DUMPSYS_WINDOW, receiver); 47 return WindowManagerServiceDumpProto.parser().parseFrom(receiver.getOutput()); 48 } 49 50 @Nonnull getWindows(@onnull ITestDevice device)51 public static List<WindowStateProto> getWindows(@Nonnull ITestDevice device) throws Exception { 52 final WindowManagerServiceDumpProto windowManagerServiceDump = getDump(device); 53 final RootWindowContainerProto rootWindowContainer = 54 windowManagerServiceDump.getRootWindowContainer(); 55 final WindowContainerProto windowContainer = rootWindowContainer.getWindowContainer(); 56 57 final List<WindowStateProto> windows = new ArrayList<>(); 58 collectWindowStates(windowContainer, windows); 59 60 return windows; 61 } 62 63 @Nonnull getActivityRecords(@onnull ITestDevice device)64 public static List<ActivityRecordProto> getActivityRecords(@Nonnull ITestDevice device) 65 throws Exception { 66 final WindowManagerServiceDumpProto windowManagerServiceDump = getDump(device); 67 final RootWindowContainerProto rootWindowContainer = 68 windowManagerServiceDump.getRootWindowContainer(); 69 final WindowContainerProto windowContainer = rootWindowContainer.getWindowContainer(); 70 71 final List<ActivityRecordProto> activityRecords = new ArrayList<>(); 72 collectActivityRecords(windowContainer, activityRecords); 73 74 return activityRecords; 75 } 76 77 @Nonnull getResumedActivities(@onnull ITestDevice device)78 public static List<String> getResumedActivities(@Nonnull ITestDevice device) throws Exception { 79 List<String> resumedActivities = new ArrayList<>(); 80 List<ActivityRecordProto> activityRecords = getActivityRecords(device); 81 for (ActivityRecordProto activityRecord : activityRecords) { 82 if (STATE_RESUMED.equals(activityRecord.getState())) { 83 resumedActivities.add(activityRecord.getName()); 84 } 85 } 86 87 return resumedActivities; 88 } 89 90 @Nullable getWindowWithTitle(@onnull ITestDevice device, @Nonnull String expectedTitle)91 public static WindowStateProto getWindowWithTitle(@Nonnull ITestDevice device, 92 @Nonnull String expectedTitle) throws Exception { 93 List<WindowStateProto> windows = getWindows(device); 94 for (WindowStateProto window : windows) { 95 String title = window.getWindowContainer().getIdentifier().getTitle(); 96 if (expectedTitle.equals(title)) { 97 return window; 98 } 99 } 100 101 return null; 102 } 103 hasWindowWithTitle(@onnull ITestDevice device, @Nonnull String expectedTitle)104 public static boolean hasWindowWithTitle(@Nonnull ITestDevice device, 105 @Nonnull String expectedTitle) throws Exception { 106 return getWindowWithTitle(device, expectedTitle) != null; 107 } 108 109 /** 110 * This methods implements a DFS that goes through a tree of window containers and collects all 111 * the WindowStateProto-s. 112 * 113 * WindowContainer is generic class that can hold windows directly or through its children in a 114 * hierarchy form. WindowContainer's children are WindowContainer as well. This forms a tree of 115 * WindowContainers. 116 * 117 * There are a few classes that extend WindowContainer: Task, DisplayContent, WindowToken etc. 118 * The one we are interested in is WindowState. 119 * Since Proto does not have concept of inheritance, {@link TaskProto}, {@link WindowTokenProto} 120 * etc hold a reference to a {@link WindowContainerProto} (in java code would be {@code super} 121 * reference). 122 * {@link WindowContainerProto} may a have a number of children of type 123 * {@link WindowContainerChildProto}, which represents a generic child of a WindowContainer: a 124 * WindowContainer can have multiple children of different types stored as a 125 * {@link WindowContainerChildProto}, but each instance of {@link WindowContainerChildProto} can 126 * only contain a single type. 127 * 128 * For details see /frameworks/base/core/proto/android/server/windowmanagerservice.proto 129 */ collectWindowStates(@ullable WindowContainerProto windowContainer, @Nonnull List<WindowStateProto> out)130 private static void collectWindowStates(@Nullable WindowContainerProto windowContainer, 131 @Nonnull List<WindowStateProto> out) { 132 if (windowContainer == null) return; 133 134 final List<WindowContainerChildProto> children = windowContainer.getChildrenList(); 135 for (WindowContainerChildProto child : children) { 136 if (child.hasWindowContainer()) { 137 collectWindowStates(child.getWindowContainer(), out); 138 } else if (child.hasDisplayContent()) { 139 final DisplayContentProto displayContent = child.getDisplayContent(); 140 for (WindowTokenProto windowToken : displayContent.getOverlayWindowsList()) { 141 collectWindowStates(windowToken.getWindowContainer(), out); 142 } 143 if (displayContent.hasRootDisplayArea()) { 144 final DisplayAreaProto displayArea = displayContent.getRootDisplayArea(); 145 collectWindowStates(displayArea.getWindowContainer(), out); 146 } 147 collectWindowStates(displayContent.getWindowContainer(), out); 148 } else if (child.hasDisplayArea()) { 149 final DisplayAreaProto displayArea = child.getDisplayArea(); 150 collectWindowStates(displayArea.getWindowContainer(), out); 151 } else if (child.hasTask()) { 152 final TaskProto task = child.getTask(); 153 collectWindowStates(task.getWindowContainer(), out); 154 } else if (child.hasActivity()) { 155 final ActivityRecordProto activity = child.getActivity(); 156 if (activity.hasWindowToken()) { 157 final WindowTokenProto windowToken = activity.getWindowToken(); 158 collectWindowStates(windowToken.getWindowContainer(), out); 159 } 160 } else if (child.hasWindowToken()) { 161 final WindowTokenProto windowToken = child.getWindowToken(); 162 collectWindowStates(windowToken.getWindowContainer(), out); 163 } else if (child.hasWindow()) { 164 final WindowStateProto window = child.getWindow(); 165 // We found a Window! 166 out.add(window); 167 // ... but still aren't done 168 collectWindowStates(window.getWindowContainer(), out); 169 } 170 } 171 } 172 collectActivityRecords(@ullable WindowContainerProto windowContainer, @Nonnull List<ActivityRecordProto> out)173 private static void collectActivityRecords(@Nullable WindowContainerProto windowContainer, 174 @Nonnull List<ActivityRecordProto> out) { 175 if (windowContainer == null) return; 176 177 final List<WindowContainerChildProto> children = windowContainer.getChildrenList(); 178 for (WindowContainerChildProto child : children) { 179 if (child.hasWindowContainer()) { 180 collectActivityRecords(child.getWindowContainer(), out); 181 } else if (child.hasDisplayContent()) { 182 final DisplayContentProto displayContent = child.getDisplayContent(); 183 for (WindowTokenProto windowToken : displayContent.getOverlayWindowsList()) { 184 collectActivityRecords(windowToken.getWindowContainer(), out); 185 } 186 if (displayContent.hasRootDisplayArea()) { 187 final DisplayAreaProto displayArea = displayContent.getRootDisplayArea(); 188 collectActivityRecords(displayArea.getWindowContainer(), out); 189 } 190 collectActivityRecords(displayContent.getWindowContainer(), out); 191 } else if (child.hasDisplayArea()) { 192 final DisplayAreaProto displayArea = child.getDisplayArea(); 193 collectActivityRecords(displayArea.getWindowContainer(), out); 194 } else if (child.hasTask()) { 195 final TaskProto task = child.getTask(); 196 collectActivityRecords(task.getWindowContainer(), out); 197 } else if (child.hasActivity()) { 198 final ActivityRecordProto activity = child.getActivity(); 199 out.add(activity); 200 if (activity.hasWindowToken()) { 201 final WindowTokenProto windowToken = activity.getWindowToken(); 202 collectActivityRecords(windowToken.getWindowContainer(), out); 203 } 204 } else if (child.hasWindowToken()) { 205 final WindowTokenProto windowToken = child.getWindowToken(); 206 collectActivityRecords(windowToken.getWindowContainer(), out); 207 } else if (child.hasWindow()) { 208 final WindowStateProto window = child.getWindow(); 209 collectActivityRecords(window.getWindowContainer(), out); 210 } 211 } 212 } 213 } 214