• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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