Home | History | Annotate | Download | only in cts
      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 
     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 
    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 
    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 
    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 
    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 
    236     String getFrontWindow() {
    237         if (mWindows == null || mWindows.isEmpty()) {
    238             return null;
    239         }
    240         return mWindows.get(0);
    241     }
    242 
    243     String getFocusedWindow() {
    244         return mFocusedWindow;
    245     }
    246 
    247     String getFocusedApp() {
    248         return mFocusedApp;
    249     }
    250 
    251     int getFrontStackId() {
    252         return mStacks.get(0).mStackId;
    253     }
    254 
    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 
    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 
    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 
    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 
    299         private WindowStack() {
    300 
    301         }
    302 
    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 
    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 
    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 
    376         private WindowTask() {
    377         }
    378 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    498         Rectangle getBounds() {
    499             return mBounds;
    500         }
    501 
    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 
    520         private Display(int displayId) {
    521             mDisplayId = displayId;
    522         }
    523 
    524         int getDisplayId() {
    525             return mDisplayId;
    526         }
    527 
    528         int getDpi() {
    529             return mDpi;
    530         }
    531 
    532         Rectangle getDisplayRect() {
    533             return mDisplayRect;
    534         }
    535 
    536         Rectangle getAppRect() {
    537             return mAppRect;
    538         }
    539 
    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 
    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
    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 
    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 
    614         String getName() {
    615             return mName;
    616         }
    617 
    618         String getToken() {
    619             return mAppToken;
    620         }
    621 
    622         boolean isStartingWindow() {
    623             return mStarting;
    624         }
    625 
    626         boolean isExitingWindow() {
    627             return mExiting;
    628         }
    629 
    630         int getDisplayId() {
    631             return mDisplayId;
    632         }
    633 
    634         int getStackId() {
    635             return mStackId;
    636         }
    637 
    638         Rectangle getContainingFrame() {
    639             return mContainingFrame;
    640         }
    641 
    642         Rectangle getParentFrame() {
    643             return mParentFrame;
    644         }
    645 
    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 
    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
    700         public String toString() {
    701             return "WindowState: {" + mAppToken + " " + mName
    702                     + (mStarting ? " STARTING" : "") + (mExiting ? " EXITING" : "") + "}"
    703                     + " cf=" + mContainingFrame + " pf=" + mParentFrame;
    704         }
    705     }
    706 }
    707