• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 android.server.cts;
18 
19 import com.android.tradefed.device.CollectingOutputReceiver;
20 import com.android.tradefed.device.DeviceNotAvailableException;
21 import com.android.tradefed.device.ITestDevice;
22 import com.android.tradefed.log.LogUtil.CLog;
23 
24 import java.awt.Rectangle;
25 import java.lang.String;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.LinkedList;
29 import java.util.List;
30 
31 import java.util.regex.Pattern;
32 import java.util.regex.Matcher;
33 
34 import static android.server.cts.StateLogger.log;
35 import static android.server.cts.StateLogger.logE;
36 
37 class WindowManagerState {
38     private static final String DUMPSYS_WINDOWS_APPS = "dumpsys window apps";
39     private static final String DUMPSYS_WINDOWS_VISIBLE_APPS = "dumpsys window visible-apps";
40 
41     private static final Pattern sWindowPattern =
42             Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+)\\}\\:");
43     private static final Pattern sStartingWindowPattern =
44             Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) Starting (.+)\\}\\:");
45     private static final Pattern sExitingWindowPattern =
46             Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+) EXITING\\}\\:");
47 
48     private static final Pattern sFocusedWindowPattern = Pattern.compile(
49             "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) (\\S+)\\}");
50     private static final Pattern sAppErrorFocusedWindowPattern = Pattern.compile(
51             "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) Application Error\\: (\\S+)\\}");
52     private static final Pattern sWaitingForDebuggerFocusedWindowPattern = Pattern.compile(
53             "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) Waiting For Debugger\\: (\\S+)\\}");
54 
55     private static final Pattern sFocusedAppPattern =
56             Pattern.compile("mFocusedApp=AppWindowToken\\{(.+) token=Token\\{(.+) "
57                     + "ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)");
58 
59     private static final Pattern sStackIdPattern = Pattern.compile("mStackId=(\\d+)");
60 
61     private static final Pattern[] sExtractStackExitPatterns = {
62             sStackIdPattern, sWindowPattern, sStartingWindowPattern, sExitingWindowPattern,
63             sFocusedWindowPattern, sAppErrorFocusedWindowPattern,
64             sWaitingForDebuggerFocusedWindowPattern, sFocusedAppPattern };
65 
66     // Windows in z-order with the top most at the front of the list.
67     private List<String> mWindows = new ArrayList();
68     private List<WindowState> mWindowStates = new ArrayList();
69     private List<WindowStack> mStacks = new ArrayList();
70     private List<Display> mDisplays = new ArrayList();
71     private String mFocusedWindow = null;
72     private String mFocusedApp = null;
73     private final LinkedList<String> mSysDump = new LinkedList();
74 
computeState(ITestDevice device, boolean visibleOnly)75     void computeState(ITestDevice device, boolean visibleOnly) throws DeviceNotAvailableException {
76         // It is possible the system is in the middle of transition to the right state when we get
77         // the dump. We try a few times to get the information we need before giving up.
78         int retriesLeft = 3;
79         boolean retry = false;
80         String dump = null;
81 
82         log("==============================");
83         log("      WindowManagerState      ");
84         log("==============================");
85         do {
86             if (retry) {
87                 log("***Incomplete WM state. Retrying...");
88                 // Wait half a second between retries for window manager to finish transitioning...
89                 try {
90                     Thread.sleep(500);
91                 } catch (InterruptedException e) {
92                     log(e.toString());
93                     // Well I guess we are not waiting...
94                 }
95             }
96 
97             final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
98             final String dumpsysCmd = visibleOnly ?
99                     DUMPSYS_WINDOWS_VISIBLE_APPS : DUMPSYS_WINDOWS_APPS;
100             device.executeShellCommand(dumpsysCmd, outputReceiver);
101             dump = outputReceiver.getOutput();
102             parseSysDump(dump, visibleOnly);
103 
104             retry = mWindows.isEmpty() || mFocusedWindow == null || mFocusedApp == null;
105         } while (retry && retriesLeft-- > 0);
106 
107         if (retry) {
108             log(dump);
109         }
110 
111         if (mWindows.isEmpty()) {
112             logE("No Windows found...");
113         }
114         if (mFocusedWindow == null) {
115             logE("No Focused Window...");
116         }
117         if (mFocusedApp == null) {
118             logE("No Focused App...");
119         }
120     }
121 
parseSysDump(String sysDump, boolean visibleOnly)122     private void parseSysDump(String sysDump, boolean visibleOnly) {
123         reset();
124 
125         Collections.addAll(mSysDump, sysDump.split("\\n"));
126 
127         while (!mSysDump.isEmpty()) {
128             final Display display =
129                     Display.create(mSysDump, sExtractStackExitPatterns);
130             if (display != null) {
131                 log(display.toString());
132                 mDisplays.add(display);
133                 continue;
134             }
135 
136             final WindowStack stack =
137                     WindowStack.create(mSysDump, sStackIdPattern, sExtractStackExitPatterns);
138 
139             if (stack != null) {
140                 mStacks.add(stack);
141                 continue;
142             }
143 
144 
145             final WindowState ws = WindowState.create(mSysDump, sExtractStackExitPatterns);
146             if (ws != null) {
147                 log(ws.toString());
148 
149                 if (visibleOnly) {
150                     // Check to see if we are in the middle of transitioning. If we are, we want to
151                     // skip dumping until window manager is done transitioning windows.
152                     if (ws.isStartingWindow()) {
153                         log("Skipping dump due to starting window transition...");
154                         return;
155                     }
156 
157                     if (ws.isExitingWindow()) {
158                         log("Skipping dump due to exiting window transition...");
159                         return;
160                     }
161                 }
162 
163                 mWindows.add(ws.getName());
164                 mWindowStates.add(ws);
165                 continue;
166             }
167 
168             final String line = mSysDump.pop().trim();
169 
170             Matcher matcher = sFocusedWindowPattern.matcher(line);
171             if (matcher.matches()) {
172                 log(line);
173                 final String focusedWindow = matcher.group(3);
174                 log(focusedWindow);
175                 mFocusedWindow = focusedWindow;
176                 continue;
177             }
178 
179             matcher = sAppErrorFocusedWindowPattern.matcher(line);
180             if (matcher.matches()) {
181                 log(line);
182                 final String focusedWindow = matcher.group(3);
183                 log(focusedWindow);
184                 mFocusedWindow = focusedWindow;
185                 continue;
186             }
187 
188             matcher = sWaitingForDebuggerFocusedWindowPattern.matcher(line);
189             if (matcher.matches()) {
190                 log(line);
191                 final String focusedWindow = matcher.group(3);
192                 log(focusedWindow);
193                 mFocusedWindow = focusedWindow;
194                 continue;
195             }
196 
197             matcher = sFocusedAppPattern.matcher(line);
198             if (matcher.matches()) {
199                 log(line);
200                 final String focusedApp = matcher.group(5);
201                 log(focusedApp);
202                 mFocusedApp = focusedApp;
203                 continue;
204             }
205         }
206     }
207 
getMatchingWindowTokens(final String windowName, List<String> tokenList)208     void getMatchingWindowTokens(final String windowName, List<String> tokenList) {
209         tokenList.clear();
210 
211         for (WindowState ws : mWindowStates) {
212             if (windowName.equals(ws.getName())) {
213                 tokenList.add(ws.getToken());
214             }
215         }
216     }
217 
getMatchingWindowState(final String windowName, List<WindowState> windowList)218     void getMatchingWindowState(final String windowName, List<WindowState> windowList) {
219         windowList.clear();
220         for (WindowState ws : mWindowStates) {
221             if (windowName.equals(ws.getName())) {
222                 windowList.add(ws);
223             }
224         }
225     }
226 
getDisplay(int displayId)227     Display getDisplay(int displayId) {
228         for (Display display : mDisplays) {
229             if (displayId == display.getDisplayId()) {
230                 return display;
231             }
232         }
233         return null;
234     }
235 
getFrontWindow()236     String getFrontWindow() {
237         if (mWindows == null || mWindows.isEmpty()) {
238             return null;
239         }
240         return mWindows.get(0);
241     }
242 
getFocusedWindow()243     String getFocusedWindow() {
244         return mFocusedWindow;
245     }
246 
getFocusedApp()247     String getFocusedApp() {
248         return mFocusedApp;
249     }
250 
getFrontStackId()251     int getFrontStackId() {
252         return mStacks.get(0).mStackId;
253     }
254 
containsStack(int stackId)255     boolean containsStack(int stackId) {
256         for (WindowStack stack : mStacks) {
257             if (stackId == stack.mStackId) {
258                 return true;
259             }
260         }
261         return false;
262     }
263 
isWindowVisible(String windowName)264     boolean isWindowVisible(String windowName) {
265         for (String window : mWindows) {
266             if (window.equals(windowName)) {
267                 return true;
268             }
269         }
270         return false;
271     }
272 
getStack(int stackId)273     WindowStack getStack(int stackId) {
274         for (WindowStack stack : mStacks) {
275             if (stackId == stack.mStackId) {
276                 return stack;
277             }
278         }
279         return null;
280     }
281 
reset()282     private void reset() {
283         mSysDump.clear();
284         mStacks.clear();
285         mDisplays.clear();
286         mWindows.clear();
287         mWindowStates.clear();
288         mFocusedWindow = null;
289         mFocusedApp = null;
290     }
291 
292     static class WindowStack extends WindowContainer {
293 
294         private static final Pattern sTaskIdPattern = Pattern.compile("taskId=(\\d+)");
295 
296         int mStackId;
297         ArrayList<WindowTask> mTasks = new ArrayList();
298 
WindowStack()299         private WindowStack() {
300 
301         }
302 
create( LinkedList<String> dump, Pattern stackIdPattern, Pattern[] exitPatterns)303         static WindowStack create(
304                 LinkedList<String> dump, Pattern stackIdPattern, Pattern[] exitPatterns) {
305             final String line = dump.peek().trim();
306 
307             final Matcher matcher = stackIdPattern.matcher(line);
308             if (!matcher.matches()) {
309                 // Not a stack.
310                 return null;
311             }
312             // For the stack Id line we just read.
313             dump.pop();
314 
315             final WindowStack stack = new WindowStack();
316             log(line);
317             final String stackId = matcher.group(1);
318             log(stackId);
319             stack.mStackId = Integer.parseInt(stackId);
320             stack.extract(dump, exitPatterns);
321             return stack;
322         }
323 
extract(LinkedList<String> dump, Pattern[] exitPatterns)324         void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
325 
326             final List<Pattern> taskExitPatterns = new ArrayList();
327             Collections.addAll(taskExitPatterns, exitPatterns);
328             taskExitPatterns.add(sTaskIdPattern);
329             final Pattern[] taskExitPatternsArray =
330                     taskExitPatterns.toArray(new Pattern[taskExitPatterns.size()]);
331 
332             while (!doneExtracting(dump, exitPatterns)) {
333                 final WindowTask task =
334                         WindowTask.create(dump, sTaskIdPattern, taskExitPatternsArray);
335 
336                 if (task != null) {
337                     mTasks.add(task);
338                     continue;
339                 }
340 
341                 final String line = dump.pop().trim();
342 
343                 if (extractFullscreen(line)) {
344                     continue;
345                 }
346 
347                 if (extractBounds(line)) {
348                     continue;
349                 }
350             }
351         }
352 
getTask(int taskId)353         WindowTask getTask(int taskId) {
354             for (WindowTask task : mTasks) {
355                 if (taskId == task.mTaskId) {
356                     return task;
357                 }
358             }
359             return null;
360         }
361     }
362 
363     static class WindowTask extends WindowContainer {
364         private static final Pattern sTempInsetBoundsPattern =
365                 Pattern.compile("mTempInsetBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
366 
367         private static final Pattern sAppTokenPattern = Pattern.compile(
368                 "Activity #(\\d+) AppWindowToken\\{(\\S+) token=Token\\{(\\S+) "
369                 + "ActivityRecord\\{(\\S+) u(\\d+) (\\S+) t(\\d+)\\}\\}\\}");
370 
371 
372         int mTaskId;
373         Rectangle mTempInsetBounds;
374         List<String> mAppTokens = new ArrayList();
375 
WindowTask()376         private WindowTask() {
377         }
378 
create( LinkedList<String> dump, Pattern taskIdPattern, Pattern[] exitPatterns)379         static WindowTask create(
380                 LinkedList<String> dump, Pattern taskIdPattern, Pattern[] exitPatterns) {
381             final String line = dump.peek().trim();
382 
383             final Matcher matcher = taskIdPattern.matcher(line);
384             if (!matcher.matches()) {
385                 // Not a task.
386                 return null;
387             }
388             // For the task Id line we just read.
389             dump.pop();
390 
391             final WindowTask task = new WindowTask();
392             log(line);
393             final String taskId = matcher.group(1);
394             log(taskId);
395             task.mTaskId = Integer.parseInt(taskId);
396             task.extract(dump, exitPatterns);
397             return task;
398         }
399 
extract(LinkedList<String> dump, Pattern[] exitPatterns)400         private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
401             while (!doneExtracting(dump, exitPatterns)) {
402                 final String line = dump.pop().trim();
403 
404                 if (extractFullscreen(line)) {
405                     continue;
406                 }
407 
408                 if (extractBounds(line)) {
409                     continue;
410                 }
411 
412                 Matcher matcher = sTempInsetBoundsPattern.matcher(line);
413                 if (matcher.matches()) {
414                     log(line);
415                     mTempInsetBounds = extractBounds(matcher);
416                 }
417 
418                 matcher = sAppTokenPattern.matcher(line);
419                 if (matcher.matches()) {
420                     log(line);
421                     final String appToken = matcher.group(6);
422                     log(appToken);
423                     mAppTokens.add(appToken);
424                     continue;
425                 }
426             }
427         }
428     }
429 
430     static abstract class WindowContainer {
431         protected static final Pattern sFullscreenPattern = Pattern.compile("mFullscreen=(\\S+)");
432         protected static final Pattern sBoundsPattern =
433                 Pattern.compile("mBounds=\\[(-?\\d+),(-?\\d+)\\]\\[(-?\\d+),(-?\\d+)\\]");
434 
435         protected boolean mFullscreen;
436         protected Rectangle mBounds;
437 
doneExtracting(LinkedList<String> dump, Pattern[] exitPatterns)438         static boolean doneExtracting(LinkedList<String> dump, Pattern[] exitPatterns) {
439             if (dump.isEmpty()) {
440                 return true;
441             }
442             final String line = dump.peek().trim();
443 
444             for (Pattern pattern : exitPatterns) {
445                 if (pattern.matcher(line).matches()) {
446                     return true;
447                 }
448             }
449             return false;
450         }
451 
extractFullscreen(String line)452         boolean extractFullscreen(String line) {
453             final Matcher matcher = sFullscreenPattern.matcher(line);
454             if (!matcher.matches()) {
455                 return false;
456             }
457             log(line);
458             final String fullscreen = matcher.group(1);
459             log(fullscreen);
460             mFullscreen = Boolean.valueOf(fullscreen);
461             return true;
462         }
463 
extractBounds(String line)464         boolean extractBounds(String line) {
465             final Matcher matcher = sBoundsPattern.matcher(line);
466             if (!matcher.matches()) {
467                 return false;
468             }
469             log(line);
470             mBounds = extractBounds(matcher);
471             return true;
472         }
473 
extractBounds(Matcher matcher)474         static Rectangle extractBounds(Matcher matcher) {
475             final int left = Integer.valueOf(matcher.group(1));
476             final int top = Integer.valueOf(matcher.group(2));
477             final int right = Integer.valueOf(matcher.group(3));
478             final int bottom = Integer.valueOf(matcher.group(4));
479             final Rectangle rect = new Rectangle(left, top, right - left, bottom - top);
480 
481             log(rect.toString());
482             return rect;
483         }
484 
extractMultipleBounds(Matcher matcher, int groupIndex, Rectangle... rectList)485         static void extractMultipleBounds(Matcher matcher, int groupIndex, Rectangle... rectList) {
486             for (Rectangle rect : rectList) {
487                 if (rect == null) {
488                     return;
489                 }
490                 final int left = Integer.valueOf(matcher.group(groupIndex++));
491                 final int top = Integer.valueOf(matcher.group(groupIndex++));
492                 final int right = Integer.valueOf(matcher.group(groupIndex++));
493                 final int bottom = Integer.valueOf(matcher.group(groupIndex++));
494                 rect.setBounds(left, top, right - left, bottom - top);
495             }
496         }
497 
getBounds()498         Rectangle getBounds() {
499             return mBounds;
500         }
501 
isFullscreen()502         boolean isFullscreen() {
503             return mFullscreen;
504         }
505     }
506 
507     static class Display extends WindowContainer {
508         private static final String TAG = "[Display] ";
509 
510         private static final Pattern sDisplayIdPattern =
511                 Pattern.compile("Display: mDisplayId=(\\d+)");
512         private static final Pattern sDisplayInfoPattern =
513                 Pattern.compile("(.+) (\\d+)dpi cur=(\\d+)x(\\d+) app=(\\d+)x(\\d+) (.+)");
514 
515         private final int mDisplayId;
516         private Rectangle mDisplayRect = new Rectangle();
517         private Rectangle mAppRect = new Rectangle();
518         private int mDpi;
519 
Display(int displayId)520         private Display(int displayId) {
521             mDisplayId = displayId;
522         }
523 
getDisplayId()524         int getDisplayId() {
525             return mDisplayId;
526         }
527 
getDpi()528         int getDpi() {
529             return mDpi;
530         }
531 
getDisplayRect()532         Rectangle getDisplayRect() {
533             return mDisplayRect;
534         }
535 
getAppRect()536         Rectangle getAppRect() {
537             return mAppRect;
538         }
539 
create(LinkedList<String> dump, Pattern[] exitPatterns)540         static Display create(LinkedList<String> dump, Pattern[] exitPatterns) {
541             // TODO: exit pattern for displays?
542             final String line = dump.peek().trim();
543 
544             Matcher matcher = sDisplayIdPattern.matcher(line);
545             if (!matcher.matches()) {
546                 return null;
547             }
548 
549             log(TAG + "DISPLAY_ID: " + line);
550             dump.pop();
551 
552             final int displayId = Integer.valueOf(matcher.group(1));
553             final Display display = new Display(displayId);
554             display.extract(dump, exitPatterns);
555             return display;
556         }
557 
extract(LinkedList<String> dump, Pattern[] exitPatterns)558         private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
559             while (!doneExtracting(dump, exitPatterns)) {
560                 final String line = dump.pop().trim();
561 
562                 final Matcher matcher = sDisplayInfoPattern.matcher(line);
563                 if (matcher.matches()) {
564                     log(TAG + "DISPLAY_INFO: " + line);
565                     mDpi = Integer.valueOf(matcher.group(2));
566 
567                     final int displayWidth = Integer.valueOf(matcher.group(3));
568                     final int displayHeight = Integer.valueOf(matcher.group(4));
569                     mDisplayRect.setBounds(0, 0, displayWidth, displayHeight);
570 
571                     final int appWidth = Integer.valueOf(matcher.group(5));
572                     final int appHeight = Integer.valueOf(matcher.group(6));
573                     mAppRect.setBounds(0, 0, appWidth, appHeight);
574 
575                     // break as we don't need other info for now
576                     break;
577                 }
578                 // Extract other info here if needed
579             }
580         }
581 
582         @Override
toString()583         public String toString() {
584             return "Display #" + mDisplayId + ": mDisplayRect=" + mDisplayRect
585                     + " mAppRect=" + mAppRect;
586         }
587     }
588 
589     static class WindowState extends WindowContainer {
590         private static final String TAG = "[WindowState] ";
591 
592         private static final String RECT_STR = "\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]";
593         private static final Pattern sFramePattern =
594                 Pattern.compile("Frames: containing=" + RECT_STR + " parent=" + RECT_STR);
595         private static final Pattern sWindowAssociationPattern =
596                 Pattern.compile("mDisplayId=(\\d+) stackId=(\\d+) (.+)");
597 
598         private final String mName;
599         private final String mAppToken;
600         private final boolean mStarting;
601         private final boolean mExiting;
602         private int mDisplayId;
603         private int mStackId;
604         private Rectangle mContainingFrame = new Rectangle();
605         private Rectangle mParentFrame = new Rectangle();
606 
WindowState(Matcher matcher, boolean starting, boolean exiting)607         private WindowState(Matcher matcher, boolean starting, boolean exiting) {
608             mName = matcher.group(4);
609             mAppToken = matcher.group(2);
610             mStarting = starting;
611             mExiting = exiting;
612         }
613 
getName()614         String getName() {
615             return mName;
616         }
617 
getToken()618         String getToken() {
619             return mAppToken;
620         }
621 
isStartingWindow()622         boolean isStartingWindow() {
623             return mStarting;
624         }
625 
isExitingWindow()626         boolean isExitingWindow() {
627             return mExiting;
628         }
629 
getDisplayId()630         int getDisplayId() {
631             return mDisplayId;
632         }
633 
getStackId()634         int getStackId() {
635             return mStackId;
636         }
637 
getContainingFrame()638         Rectangle getContainingFrame() {
639             return mContainingFrame;
640         }
641 
getParentFrame()642         Rectangle getParentFrame() {
643             return mParentFrame;
644         }
645 
create(LinkedList<String> dump, Pattern[] exitPatterns)646         static WindowState create(LinkedList<String> dump, Pattern[] exitPatterns) {
647             final String line = dump.peek().trim();
648 
649             Matcher matcher = sWindowPattern.matcher(line);
650             if (!matcher.matches()) {
651                 return null;
652             }
653 
654             log(TAG + "WINDOW: " + line);
655             dump.pop();
656 
657             final WindowState window;
658             Matcher specialMatcher = sStartingWindowPattern.matcher(line);
659             if (specialMatcher.matches()) {
660                 log(TAG + "STARTING: " + line);
661                 window = new WindowState(specialMatcher, true, false);
662             } else {
663                 specialMatcher = sExitingWindowPattern.matcher(line);
664                 if (specialMatcher.matches()) {
665                     log(TAG + "EXITING: " + line);
666                     window = new WindowState(specialMatcher, false, true);
667                 } else {
668                     window = new WindowState(matcher, false, false);
669                 }
670             }
671 
672             window.extract(dump, exitPatterns);
673             return window;
674         }
675 
extract(LinkedList<String> dump, Pattern[] exitPatterns)676         private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
677             while (!doneExtracting(dump, exitPatterns)) {
678                 final String line = dump.pop().trim();
679 
680                 Matcher matcher = sWindowAssociationPattern.matcher(line);
681                 if (matcher.matches()) {
682                     log(TAG + "WINDOW_ASSOCIATION: " + line);
683                     mDisplayId = Integer.valueOf(matcher.group(1));
684                     mStackId = Integer.valueOf(matcher.group(2));
685                     continue;
686                 }
687 
688                 matcher = sFramePattern.matcher(line);
689                 if (matcher.matches()) {
690                     log(TAG + "FRAME: " + line);
691                     extractMultipleBounds(matcher, 1, mContainingFrame, mParentFrame);
692                     continue;
693                 }
694 
695                 // Extract other info here if needed
696             }
697         }
698 
699         @Override
toString()700         public String toString() {
701             return "WindowState: {" + mAppToken + " " + mName
702                     + (mStarting ? " STARTING" : "") + (mExiting ? " EXITING" : "") + "}"
703                     + " cf=" + mContainingFrame + " pf=" + mParentFrame;
704         }
705     }
706 }
707