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 static android.server.cts.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
     20 import static android.server.cts.StateLogger.log;
     21 import static android.server.cts.StateLogger.logE;
     22 
     23 import com.android.tradefed.device.CollectingOutputReceiver;
     24 import com.android.tradefed.device.DeviceNotAvailableException;
     25 import com.android.tradefed.device.ITestDevice;
     26 
     27 import java.awt.*;
     28 import java.util.ArrayList;
     29 import java.util.Collections;
     30 import java.util.Comparator;
     31 import java.util.HashMap;
     32 import java.util.LinkedList;
     33 import java.util.List;
     34 import java.util.Map;
     35 import java.util.regex.Matcher;
     36 import java.util.regex.Pattern;
     37 
     38 public class WindowManagerState {
     39 
     40     public static final String TRANSIT_ACTIVITY_OPEN = "TRANSIT_ACTIVITY_OPEN";
     41     public static final String TRANSIT_ACTIVITY_CLOSE = "TRANSIT_ACTIVITY_CLOSE";
     42     public static final String TRANSIT_TASK_OPEN = "TRANSIT_TASK_OPEN";
     43     public static final String TRANSIT_TASK_CLOSE = "TRANSIT_TASK_CLOSE";
     44 
     45     public static final String TRANSIT_WALLPAPER_OPEN = "TRANSIT_WALLPAPER_OPEN";
     46     public static final String TRANSIT_WALLPAPER_CLOSE = "TRANSIT_WALLPAPER_CLOSE";
     47     public static final String TRANSIT_WALLPAPER_INTRA_OPEN = "TRANSIT_WALLPAPER_INTRA_OPEN";
     48     public static final String TRANSIT_WALLPAPER_INTRA_CLOSE = "TRANSIT_WALLPAPER_INTRA_CLOSE";
     49 
     50     public static final String TRANSIT_KEYGUARD_GOING_AWAY = "TRANSIT_KEYGUARD_GOING_AWAY";
     51     public static final String TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
     52             "TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER";
     53     public static final String TRANSIT_KEYGUARD_OCCLUDE = "TRANSIT_KEYGUARD_OCCLUDE";
     54     public static final String TRANSIT_KEYGUARD_UNOCCLUDE = "TRANSIT_KEYGUARD_UNOCCLUDE";
     55 
     56     public static final String APP_STATE_IDLE = "APP_STATE_IDLE";
     57 
     58     private static final String DUMPSYS_WINDOW = "dumpsys window -a";
     59 
     60     private static final Pattern sWindowPattern =
     61             Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+)\\}\\:");
     62     private static final Pattern sStartingWindowPattern =
     63             Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) Starting (.+)\\}\\:");
     64     private static final Pattern sExitingWindowPattern =
     65             Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+) EXITING\\}\\:");
     66     private static final Pattern sDebuggerWindowPattern =
     67             Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) Waiting For Debugger: (.+)\\}\\:");
     68 
     69     private static final Pattern sFocusedWindowPattern = Pattern.compile(
     70             "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) (\\S+)\\}");
     71     private static final Pattern sAppErrorFocusedWindowPattern = Pattern.compile(
     72             "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) Application Error\\: (\\S+)\\}");
     73     private static final Pattern sWaitingForDebuggerFocusedWindowPattern = Pattern.compile(
     74             "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) Waiting For Debugger\\: (\\S+)\\}");
     75 
     76     private static final Pattern sFocusedAppPattern =
     77             Pattern.compile("mFocusedApp=AppWindowToken\\{(.+) token=Token\\{(.+) "
     78                     + "ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)");
     79     private static final Pattern sStableBoundsPattern = Pattern.compile(
     80             "mStable=\\((\\d+),(\\d+)\\)-\\((\\d+),(\\d+)\\)");
     81     private static final Pattern sDefaultPinnedStackBoundsPattern = Pattern.compile(
     82             "defaultBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
     83     private static final Pattern sPinnedStackMovementBoundsPattern = Pattern.compile(
     84             "movementBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
     85     private static final Pattern sRotationPattern = Pattern.compile(
     86             "mRotation=(\\d).*");
     87     private static final Pattern sLastOrientationPattern = Pattern.compile(
     88             ".*mLastOrientation=(\\d)");
     89 
     90     private static final Pattern sLastAppTransitionPattern =
     91             Pattern.compile("mLastUsedAppTransition=(.+)");
     92     private static final Pattern sAppTransitionStatePattern =
     93             Pattern.compile("mAppTransitionState=(.+)");
     94 
     95     private static final Pattern sStackIdPattern = Pattern.compile("mStackId=(\\d+)");
     96 
     97     private static final Pattern sInputMethodWindowPattern =
     98             Pattern.compile("mInputMethodWindow=Window\\{([0-9a-fA-F]+) u\\d+ .+\\}.*");
     99 
    100     private static final Pattern sDisplayIdPattern =
    101             Pattern.compile("Display: mDisplayId=(\\d+)");
    102 
    103     private static final Pattern sDisplayFrozenPattern =
    104             Pattern.compile("mDisplayFrozen=([a-z]*) .*");
    105 
    106     private static final Pattern sDockedStackMinimizedPattern =
    107             Pattern.compile("mMinimizedDock=([a-z]*)");
    108 
    109     private static final Pattern[] sExtractStackExitPatterns = {
    110             sStackIdPattern, sWindowPattern, sStartingWindowPattern, sExitingWindowPattern,
    111             sDebuggerWindowPattern, sFocusedWindowPattern, sAppErrorFocusedWindowPattern,
    112             sWaitingForDebuggerFocusedWindowPattern,
    113             sFocusedAppPattern, sLastAppTransitionPattern, sDefaultPinnedStackBoundsPattern,
    114             sPinnedStackMovementBoundsPattern, sDisplayIdPattern, sDockedStackMinimizedPattern};
    115 
    116     // Windows in z-order with the top most at the front of the list.
    117     private List<WindowState> mWindowStates = new ArrayList();
    118     // Stacks in z-order with the top most at the front of the list, starting with primary display.
    119     private final List<WindowStack> mStacks = new ArrayList();
    120     // Stacks on all attached displays, in z-order with the top most at the front of the list.
    121     private final Map<Integer, List<WindowStack>> mDisplayStacks
    122             = new HashMap<>();
    123     private List<Display> mDisplays = new ArrayList();
    124     private String mFocusedWindow = null;
    125     private String mFocusedApp = null;
    126     private String mLastTransition = null;
    127     private String mAppTransitionState = null;
    128     private String mInputMethodWindowAppToken = null;
    129     private Rectangle mStableBounds = new Rectangle();
    130     private final Rectangle mDefaultPinnedStackBounds = new Rectangle();
    131     private final Rectangle mPinnedStackMovementBounds = new Rectangle();
    132     private final LinkedList<String> mSysDump = new LinkedList();
    133     private int mRotation;
    134     private int mLastOrientation;
    135     private boolean mDisplayFrozen;
    136     private boolean mIsDockedStackMinimized;
    137 
    138     void computeState(ITestDevice device) throws DeviceNotAvailableException {
    139         // It is possible the system is in the middle of transition to the right state when we get
    140         // the dump. We try a few times to get the information we need before giving up.
    141         int retriesLeft = 3;
    142         boolean retry = false;
    143         String dump = null;
    144 
    145         log("==============================");
    146         log("      WindowManagerState      ");
    147         log("==============================");
    148         do {
    149             if (retry) {
    150                 log("***Incomplete WM state. Retrying...");
    151                 // Wait half a second between retries for window manager to finish transitioning...
    152                 try {
    153                     Thread.sleep(500);
    154                 } catch (InterruptedException e) {
    155                     log(e.toString());
    156                     // Well I guess we are not waiting...
    157                 }
    158             }
    159 
    160             final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
    161             device.executeShellCommand(DUMPSYS_WINDOW, outputReceiver);
    162             dump = outputReceiver.getOutput();
    163             parseSysDump(dump);
    164 
    165             retry = mWindowStates.isEmpty() || mFocusedApp == null;
    166         } while (retry && retriesLeft-- > 0);
    167 
    168         if (retry) {
    169             log(dump);
    170         }
    171 
    172         if (mWindowStates.isEmpty()) {
    173             logE("No Windows found...");
    174         }
    175         if (mFocusedWindow == null) {
    176             logE("No Focused Window...");
    177         }
    178         if (mFocusedApp == null) {
    179             logE("No Focused App...");
    180         }
    181     }
    182 
    183     private void parseSysDump(String sysDump) {
    184         reset();
    185 
    186         Collections.addAll(mSysDump, sysDump.split("\\n"));
    187 
    188         int currentDisplayId = DEFAULT_DISPLAY_ID;
    189         while (!mSysDump.isEmpty()) {
    190             final Display display =
    191                     Display.create(mSysDump, sExtractStackExitPatterns);
    192             if (display != null) {
    193                 log(display.toString());
    194                 mDisplays.add(display);
    195                 currentDisplayId = display.mDisplayId;
    196                 mDisplayStacks.put(currentDisplayId, new ArrayList<>());
    197                 continue;
    198             }
    199 
    200             final WindowStack stack =
    201                     WindowStack.create(mSysDump, sStackIdPattern, sExtractStackExitPatterns);
    202 
    203             if (stack != null) {
    204                 mStacks.add(stack);
    205                 mDisplayStacks.get(currentDisplayId).add(stack);
    206                 continue;
    207             }
    208 
    209 
    210             final WindowState ws = WindowState.create(mSysDump, sExtractStackExitPatterns);
    211             if (ws != null) {
    212                 log(ws.toString());
    213 
    214                 // Check to see if we are in the middle of transitioning. If we are, we want to
    215                 // skip dumping until window manager is done transitioning windows.
    216                 if (ws.isStartingWindow()) {
    217                     log("Skipping dump due to starting window transition...");
    218                     return;
    219                 }
    220 
    221                 if (ws.isExitingWindow()) {
    222                     log("Skipping dump due to exiting window transition...");
    223                     return;
    224                 }
    225 
    226                 mWindowStates.add(ws);
    227                 continue;
    228             }
    229 
    230             final String line = mSysDump.pop().trim();
    231 
    232             Matcher matcher = sFocusedWindowPattern.matcher(line);
    233             if (matcher.matches()) {
    234                 log(line);
    235                 final String focusedWindow = matcher.group(3);
    236                 log(focusedWindow);
    237                 mFocusedWindow = focusedWindow;
    238                 continue;
    239             }
    240 
    241             matcher = sAppErrorFocusedWindowPattern.matcher(line);
    242             if (matcher.matches()) {
    243                 log(line);
    244                 final String focusedWindow = matcher.group(3);
    245                 log(focusedWindow);
    246                 mFocusedWindow = focusedWindow;
    247                 continue;
    248             }
    249 
    250             matcher = sWaitingForDebuggerFocusedWindowPattern.matcher(line);
    251             if (matcher.matches()) {
    252                 log(line);
    253                 final String focusedWindow = matcher.group(3);
    254                 log(focusedWindow);
    255                 mFocusedWindow = focusedWindow;
    256                 continue;
    257             }
    258 
    259             matcher = sFocusedAppPattern.matcher(line);
    260             if (matcher.matches()) {
    261                 log(line);
    262                 final String focusedApp = matcher.group(5);
    263                 log(focusedApp);
    264                 mFocusedApp = focusedApp;
    265                 continue;
    266             }
    267 
    268             matcher = sAppTransitionStatePattern.matcher(line);
    269             if (matcher.matches()) {
    270                 log(line);
    271                 final String appTransitionState = matcher.group(1);
    272                 log(appTransitionState);
    273                 mAppTransitionState = appTransitionState;
    274                 continue;
    275             }
    276 
    277             matcher = sLastAppTransitionPattern.matcher(line);
    278             if (matcher.matches()) {
    279                 log(line);
    280                 final String lastAppTransitionPattern = matcher.group(1);
    281                 log(lastAppTransitionPattern);
    282                 mLastTransition = lastAppTransitionPattern;
    283                 continue;
    284             }
    285 
    286             matcher = sStableBoundsPattern.matcher(line);
    287             if (matcher.matches()) {
    288                 log(line);
    289                 int left = Integer.parseInt(matcher.group(1));
    290                 int top = Integer.parseInt(matcher.group(2));
    291                 int right = Integer.parseInt(matcher.group(3));
    292                 int bottom = Integer.parseInt(matcher.group(4));
    293                 mStableBounds.setBounds(left, top, right - left, bottom - top);
    294                 log(mStableBounds.toString());
    295                 continue;
    296             }
    297 
    298             matcher = sDefaultPinnedStackBoundsPattern.matcher(line);
    299             if (matcher.matches()) {
    300                 log(line);
    301                 int left = Integer.parseInt(matcher.group(1));
    302                 int top = Integer.parseInt(matcher.group(2));
    303                 int right = Integer.parseInt(matcher.group(3));
    304                 int bottom = Integer.parseInt(matcher.group(4));
    305                 mDefaultPinnedStackBounds.setBounds(left, top, right - left, bottom - top);
    306                 log(mDefaultPinnedStackBounds.toString());
    307                 continue;
    308             }
    309 
    310             matcher = sPinnedStackMovementBoundsPattern.matcher(line);
    311             if (matcher.matches()) {
    312                 log(line);
    313                 int left = Integer.parseInt(matcher.group(1));
    314                 int top = Integer.parseInt(matcher.group(2));
    315                 int right = Integer.parseInt(matcher.group(3));
    316                 int bottom = Integer.parseInt(matcher.group(4));
    317                 mPinnedStackMovementBounds.setBounds(left, top, right - left, bottom - top);
    318                 log(mPinnedStackMovementBounds.toString());
    319                 continue;
    320             }
    321 
    322             matcher = sInputMethodWindowPattern.matcher(line);
    323             if (matcher.matches()) {
    324                 log(line);
    325                 mInputMethodWindowAppToken = matcher.group(1);
    326                 log(mInputMethodWindowAppToken);
    327                 continue;
    328             }
    329 
    330             matcher = sRotationPattern.matcher(line);
    331             if (matcher.matches()) {
    332                 log(line);
    333                 mRotation = Integer.parseInt(matcher.group(1));
    334                 continue;
    335             }
    336 
    337             matcher = sLastOrientationPattern.matcher(line);
    338             if (matcher.matches()) {
    339                 log(line);
    340                 mLastOrientation = Integer.parseInt(matcher.group(1));
    341                 continue;
    342             }
    343 
    344             matcher = sDisplayFrozenPattern.matcher(line);
    345             if (matcher.matches()) {
    346                 log(line);
    347                 mDisplayFrozen = Boolean.parseBoolean(matcher.group(1));
    348                 continue;
    349             }
    350 
    351             matcher = sDockedStackMinimizedPattern.matcher(line);
    352             if (matcher.matches()) {
    353                 log(line);
    354                 mIsDockedStackMinimized = Boolean.parseBoolean(matcher.group(1));
    355                 continue;
    356             }
    357         }
    358     }
    359 
    360     void getMatchingWindowTokens(final String windowName, List<String> tokenList) {
    361         tokenList.clear();
    362 
    363         for (WindowState ws : mWindowStates) {
    364             if (windowName.equals(ws.getName())) {
    365                 tokenList.add(ws.getToken());
    366             }
    367         }
    368     }
    369 
    370     void getMatchingVisibleWindowState(final String windowName, List<WindowState> windowList) {
    371         windowList.clear();
    372         for (WindowState ws : mWindowStates) {
    373             if (ws.isShown() && windowName.equals(ws.getName())) {
    374                 windowList.add(ws);
    375             }
    376         }
    377     }
    378 
    379     void getPrefixMatchingVisibleWindowState(final String windowName, List<WindowState> windowList) {
    380         windowList.clear();
    381         for (WindowState ws : mWindowStates) {
    382             if (ws.isShown() && ws.getName().startsWith(windowName)) {
    383                 windowList.add(ws);
    384             }
    385         }
    386     }
    387 
    388     WindowState getWindowByPackageName(String packageName, int windowType) {
    389         for (WindowState ws : mWindowStates) {
    390             final String name = ws.getName();
    391             if (name == null || !name.contains(packageName)) {
    392                 continue;
    393             }
    394             if (windowType != ws.getType()) {
    395                 continue;
    396             }
    397             return ws;
    398         }
    399 
    400         return null;
    401     }
    402 
    403     void getWindowsByPackageName(String packageName, List<Integer> restrictToTypeList,
    404             List<WindowState> outWindowList) {
    405         outWindowList.clear();
    406         for (WindowState ws : mWindowStates) {
    407             final String name = ws.getName();
    408             if (name == null || !name.contains(packageName)) {
    409                 continue;
    410             }
    411             if (restrictToTypeList != null && !restrictToTypeList.contains(ws.getType())) {
    412                 continue;
    413             }
    414             outWindowList.add(ws);
    415         }
    416     }
    417 
    418     void sortWindowsByLayer(List<WindowState> windows) {
    419         windows.sort(Comparator.comparingInt(WindowState::getLayer));
    420     }
    421 
    422     WindowState getWindowStateForAppToken(String appToken) {
    423         for (WindowState ws : mWindowStates) {
    424             if (ws.getToken().equals(appToken)) {
    425                 return ws;
    426             }
    427         }
    428         return null;
    429     }
    430 
    431     Display getDisplay(int displayId) {
    432         for (Display display : mDisplays) {
    433             if (displayId == display.getDisplayId()) {
    434                 return display;
    435             }
    436         }
    437         return null;
    438     }
    439 
    440     String getFrontWindow() {
    441         if (mWindowStates == null || mWindowStates.isEmpty()) {
    442             return null;
    443         }
    444         return mWindowStates.get(0).getName();
    445     }
    446 
    447     String getFocusedWindow() {
    448         return mFocusedWindow;
    449     }
    450 
    451     String getFocusedApp() {
    452         return mFocusedApp;
    453     }
    454 
    455     String getLastTransition() {
    456         return mLastTransition;
    457     }
    458 
    459     String getAppTransitionState() {
    460         return mAppTransitionState;
    461     }
    462 
    463     int getFrontStackId(int displayId) {
    464         return mDisplayStacks.get(displayId).get(0).mStackId;
    465     }
    466 
    467     public int getRotation() {
    468         return mRotation;
    469     }
    470 
    471     int getLastOrientation() {
    472         return mLastOrientation;
    473     }
    474 
    475     boolean containsStack(int stackId) {
    476         for (WindowStack stack : mStacks) {
    477             if (stackId == stack.mStackId) {
    478                 return true;
    479             }
    480         }
    481         return false;
    482     }
    483 
    484     /** Check if there exists a window record with matching windowName. */
    485     boolean containsWindow(String windowName) {
    486         for (WindowState window : mWindowStates) {
    487             if (window.getName().equals(windowName)) {
    488                 return true;
    489             }
    490         }
    491         return false;
    492     }
    493 
    494     /** Check if at least one window which matches provided window name is visible. */
    495     boolean isWindowVisible(String windowName) {
    496         for (WindowState window : mWindowStates) {
    497             if (window.getName().equals(windowName)) {
    498                 if (window.isShown()) {
    499                     return true;
    500                 }
    501             }
    502         }
    503         return false;
    504     }
    505 
    506     boolean allWindowsVisible(String windowName) {
    507         boolean allVisible = false;
    508         for (WindowState window : mWindowStates) {
    509             if (window.getName().equals(windowName)) {
    510                 if (!window.isShown()) {
    511                     log("[VISIBLE] not visible" + windowName);
    512                     return false;
    513                 }
    514                 log("[VISIBLE] visible" + windowName);
    515                 allVisible = true;
    516             }
    517         }
    518         return allVisible;
    519     }
    520 
    521     WindowStack getStack(int stackId) {
    522         for (WindowStack stack : mStacks) {
    523             if (stackId == stack.mStackId) {
    524                 return stack;
    525             }
    526         }
    527         return null;
    528     }
    529 
    530 
    531     int getStackPosition(int stackId) {
    532         for (int i = 0; i < mStacks.size(); i++) {
    533             if (stackId == mStacks.get(i).mStackId) {
    534                 return i;
    535             }
    536         }
    537         return -1;
    538     }
    539 
    540     WindowState getInputMethodWindowState() {
    541         return getWindowStateForAppToken(mInputMethodWindowAppToken);
    542     }
    543 
    544     Rectangle getStableBounds() {
    545         return mStableBounds;
    546     }
    547 
    548     Rectangle getDefaultPinnedStackBounds() {
    549         return mDefaultPinnedStackBounds;
    550     }
    551 
    552     Rectangle getPinnedStackMomentBounds() {
    553         return mPinnedStackMovementBounds;
    554     }
    555 
    556     WindowState findFirstWindowWithType(int type) {
    557         for (WindowState window : mWindowStates) {
    558             if (window.getType() == type) {
    559                 return window;
    560             }
    561         }
    562         return null;
    563     }
    564 
    565     public boolean isDisplayFrozen() {
    566         return mDisplayFrozen;
    567     }
    568 
    569     public boolean isDockedStackMinimized() {
    570         return mIsDockedStackMinimized;
    571     }
    572 
    573     private void reset() {
    574         mSysDump.clear();
    575         mStacks.clear();
    576         mDisplays.clear();
    577         mWindowStates.clear();
    578         mFocusedWindow = null;
    579         mFocusedApp = null;
    580         mInputMethodWindowAppToken = null;
    581     }
    582 
    583     static class WindowStack extends WindowContainer {
    584 
    585         private static final Pattern sTaskIdPattern = Pattern.compile("taskId=(\\d+)");
    586         private static final Pattern sWindowAnimationBackgroundSurfacePattern =
    587                 Pattern.compile("mWindowAnimationBackgroundSurface:");
    588 
    589         int mStackId;
    590         ArrayList<WindowTask> mTasks = new ArrayList();
    591         boolean mWindowAnimationBackgroundSurfaceShowing;
    592 
    593         private WindowStack() {
    594 
    595         }
    596 
    597         static WindowStack create(
    598                 LinkedList<String> dump, Pattern stackIdPattern, Pattern[] exitPatterns) {
    599             final String line = dump.peek().trim();
    600 
    601             final Matcher matcher = stackIdPattern.matcher(line);
    602             if (!matcher.matches()) {
    603                 // Not a stack.
    604                 return null;
    605             }
    606             // For the stack Id line we just read.
    607             dump.pop();
    608 
    609             final WindowStack stack = new WindowStack();
    610             log(line);
    611             final String stackId = matcher.group(1);
    612             log(stackId);
    613             stack.mStackId = Integer.parseInt(stackId);
    614             stack.extract(dump, exitPatterns);
    615             return stack;
    616         }
    617 
    618         void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
    619 
    620             final List<Pattern> taskExitPatterns = new ArrayList();
    621             Collections.addAll(taskExitPatterns, exitPatterns);
    622             taskExitPatterns.add(sTaskIdPattern);
    623             taskExitPatterns.add(sWindowAnimationBackgroundSurfacePattern);
    624             final Pattern[] taskExitPatternsArray =
    625                     taskExitPatterns.toArray(new Pattern[taskExitPatterns.size()]);
    626 
    627             while (!doneExtracting(dump, exitPatterns)) {
    628                 final WindowTask task =
    629                         WindowTask.create(dump, sTaskIdPattern, taskExitPatternsArray);
    630 
    631                 if (task != null) {
    632                     mTasks.add(task);
    633                     continue;
    634                 }
    635 
    636                 final String line = dump.pop().trim();
    637 
    638                 if (extractFullscreen(line)) {
    639                     continue;
    640                 }
    641 
    642                 if (extractBounds(line)) {
    643                     continue;
    644                 }
    645 
    646                 if (extractWindowAnimationBackgroundSurface(line)) {
    647                     continue;
    648                 }
    649             }
    650         }
    651 
    652         boolean extractWindowAnimationBackgroundSurface(String line) {
    653             if (sWindowAnimationBackgroundSurfacePattern.matcher(line).matches()) {
    654                 log(line);
    655                 mWindowAnimationBackgroundSurfaceShowing = true;
    656                 return true;
    657             }
    658             return false;
    659         }
    660 
    661         WindowTask getTask(int taskId) {
    662             for (WindowTask task : mTasks) {
    663                 if (taskId == task.mTaskId) {
    664                     return task;
    665                 }
    666             }
    667             return null;
    668         }
    669 
    670         boolean isWindowAnimationBackgroundSurfaceShowing() {
    671             return mWindowAnimationBackgroundSurfaceShowing;
    672         }
    673     }
    674 
    675     static class WindowTask extends WindowContainer {
    676         private static final Pattern sTempInsetBoundsPattern =
    677                 Pattern.compile("mTempInsetBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
    678 
    679         private static final Pattern sAppTokenPattern = Pattern.compile(
    680                 "Activity #(\\d+) AppWindowToken\\{(\\S+) token=Token\\{(\\S+) "
    681                 + "ActivityRecord\\{(\\S+) u(\\d+) (\\S+) t(\\d+)\\}\\}\\}");
    682 
    683 
    684         int mTaskId;
    685         Rectangle mTempInsetBounds;
    686         List<String> mAppTokens = new ArrayList();
    687 
    688         private WindowTask() {
    689         }
    690 
    691         static WindowTask create(
    692                 LinkedList<String> dump, Pattern taskIdPattern, Pattern[] exitPatterns) {
    693             final String line = dump.peek().trim();
    694 
    695             final Matcher matcher = taskIdPattern.matcher(line);
    696             if (!matcher.matches()) {
    697                 // Not a task.
    698                 return null;
    699             }
    700             // For the task Id line we just read.
    701             dump.pop();
    702 
    703             final WindowTask task = new WindowTask();
    704             log(line);
    705             final String taskId = matcher.group(1);
    706             log(taskId);
    707             task.mTaskId = Integer.parseInt(taskId);
    708             task.extract(dump, exitPatterns);
    709             return task;
    710         }
    711 
    712         private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
    713             while (!doneExtracting(dump, exitPatterns)) {
    714                 final String line = dump.pop().trim();
    715 
    716                 if (extractFullscreen(line)) {
    717                     continue;
    718                 }
    719 
    720                 if (extractBounds(line)) {
    721                     continue;
    722                 }
    723 
    724                 Matcher matcher = sTempInsetBoundsPattern.matcher(line);
    725                 if (matcher.matches()) {
    726                     log(line);
    727                     mTempInsetBounds = extractBounds(matcher);
    728                 }
    729 
    730                 matcher = sAppTokenPattern.matcher(line);
    731                 if (matcher.matches()) {
    732                     log(line);
    733                     final String appToken = matcher.group(6);
    734                     log(appToken);
    735                     mAppTokens.add(appToken);
    736                     continue;
    737                 }
    738             }
    739         }
    740     }
    741 
    742     static abstract class WindowContainer {
    743         protected static final Pattern sFullscreenPattern = Pattern.compile("mFillsParent=(\\S+)");
    744         protected static final Pattern sBoundsPattern =
    745                 Pattern.compile("mBounds=\\[(-?\\d+),(-?\\d+)\\]\\[(-?\\d+),(-?\\d+)\\]");
    746 
    747         protected boolean mFullscreen;
    748         protected Rectangle mBounds;
    749 
    750         static boolean doneExtracting(LinkedList<String> dump, Pattern[] exitPatterns) {
    751             if (dump.isEmpty()) {
    752                 return true;
    753             }
    754             final String line = dump.peek().trim();
    755 
    756             for (Pattern pattern : exitPatterns) {
    757                 if (pattern.matcher(line).matches()) {
    758                     return true;
    759                 }
    760             }
    761             return false;
    762         }
    763 
    764         boolean extractFullscreen(String line) {
    765             final Matcher matcher = sFullscreenPattern.matcher(line);
    766             if (!matcher.matches()) {
    767                 return false;
    768             }
    769             log(line);
    770             final String fullscreen = matcher.group(1);
    771             log(fullscreen);
    772             mFullscreen = Boolean.valueOf(fullscreen);
    773             return true;
    774         }
    775 
    776         boolean extractBounds(String line) {
    777             final Matcher matcher = sBoundsPattern.matcher(line);
    778             if (!matcher.matches()) {
    779                 return false;
    780             }
    781             log(line);
    782             mBounds = extractBounds(matcher);
    783             return true;
    784         }
    785 
    786         static Rectangle extractBounds(Matcher matcher) {
    787             final int left = Integer.valueOf(matcher.group(1));
    788             final int top = Integer.valueOf(matcher.group(2));
    789             final int right = Integer.valueOf(matcher.group(3));
    790             final int bottom = Integer.valueOf(matcher.group(4));
    791             final Rectangle rect = new Rectangle(left, top, right - left, bottom - top);
    792 
    793             log(rect.toString());
    794             return rect;
    795         }
    796 
    797         static void extractMultipleBounds(Matcher matcher, int groupIndex, Rectangle... rectList) {
    798             for (Rectangle rect : rectList) {
    799                 if (rect == null) {
    800                     return;
    801                 }
    802                 final int left = Integer.valueOf(matcher.group(groupIndex++));
    803                 final int top = Integer.valueOf(matcher.group(groupIndex++));
    804                 final int right = Integer.valueOf(matcher.group(groupIndex++));
    805                 final int bottom = Integer.valueOf(matcher.group(groupIndex++));
    806                 rect.setBounds(left, top, right - left, bottom - top);
    807             }
    808         }
    809 
    810         Rectangle getBounds() {
    811             return mBounds;
    812         }
    813 
    814         boolean isFullscreen() {
    815             return mFullscreen;
    816         }
    817     }
    818 
    819     static class Display extends WindowContainer {
    820         private static final String TAG = "[Display] ";
    821 
    822         private static final Pattern sDisplayInfoPattern =
    823                 Pattern.compile("(.+) (\\d+)dpi cur=(\\d+)x(\\d+) app=(\\d+)x(\\d+) (.+)");
    824 
    825         private final int mDisplayId;
    826         private Rectangle mDisplayRect = new Rectangle();
    827         private Rectangle mAppRect = new Rectangle();
    828         private int mDpi;
    829 
    830         private Display(int displayId) {
    831             mDisplayId = displayId;
    832         }
    833 
    834         int getDisplayId() {
    835             return mDisplayId;
    836         }
    837 
    838         int getDpi() {
    839             return mDpi;
    840         }
    841 
    842         Rectangle getDisplayRect() {
    843             return mDisplayRect;
    844         }
    845 
    846         Rectangle getAppRect() {
    847             return mAppRect;
    848         }
    849 
    850         static Display create(LinkedList<String> dump, Pattern[] exitPatterns) {
    851             // TODO: exit pattern for displays?
    852             final String line = dump.peek().trim();
    853 
    854             Matcher matcher = sDisplayIdPattern.matcher(line);
    855             if (!matcher.matches()) {
    856                 return null;
    857             }
    858 
    859             log(TAG + "DISPLAY_ID: " + line);
    860             dump.pop();
    861 
    862             final int displayId = Integer.valueOf(matcher.group(1));
    863             final Display display = new Display(displayId);
    864             display.extract(dump, exitPatterns);
    865             return display;
    866         }
    867 
    868         private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
    869             while (!doneExtracting(dump, exitPatterns)) {
    870                 final String line = dump.pop().trim();
    871 
    872                 final Matcher matcher = sDisplayInfoPattern.matcher(line);
    873                 if (matcher.matches()) {
    874                     log(TAG + "DISPLAY_INFO: " + line);
    875                     mDpi = Integer.valueOf(matcher.group(2));
    876 
    877                     final int displayWidth = Integer.valueOf(matcher.group(3));
    878                     final int displayHeight = Integer.valueOf(matcher.group(4));
    879                     mDisplayRect.setBounds(0, 0, displayWidth, displayHeight);
    880 
    881                     final int appWidth = Integer.valueOf(matcher.group(5));
    882                     final int appHeight = Integer.valueOf(matcher.group(6));
    883                     mAppRect.setBounds(0, 0, appWidth, appHeight);
    884 
    885                     // break as we don't need other info for now
    886                     break;
    887                 }
    888                 // Extract other info here if needed
    889             }
    890         }
    891 
    892         @Override
    893         public String toString() {
    894             return "Display #" + mDisplayId + ": mDisplayRect=" + mDisplayRect
    895                     + " mAppRect=" + mAppRect;
    896         }
    897     }
    898 
    899     public static class WindowState extends WindowContainer {
    900         private static final String TAG = "[WindowState] ";
    901 
    902         public static final int TYPE_WALLPAPER = 2013;
    903 
    904         private static final int WINDOW_TYPE_NORMAL   = 0;
    905         private static final int WINDOW_TYPE_STARTING = 1;
    906         private static final int WINDOW_TYPE_EXITING  = 2;
    907         private static final int WINDOW_TYPE_DEBUGGER = 3;
    908 
    909         private static final String RECT_STR = "\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]";
    910         private static final String NEGATIVE_VALUES_ALLOWED_RECT_STR =
    911                 "\\[([-\\d]+),([-\\d]+)\\]\\[([-\\d]+),([-\\d]+)\\]";
    912         private static final Pattern sMainFramePattern = Pattern.compile("mFrame=" + RECT_STR + ".+");
    913         private static final Pattern sFramePattern =
    914                 Pattern.compile("Frames: containing=" + RECT_STR + " parent=" + RECT_STR);
    915         private static final Pattern sContentFramePattern =
    916             Pattern.compile("content=" + RECT_STR + " .+");
    917         private static final Pattern sWindowAssociationPattern =
    918                 Pattern.compile("mDisplayId=(\\d+) stackId=(\\d+) (.+)");
    919         private static final Pattern sSurfaceInsetsPattern =
    920             Pattern.compile("Cur insets.+surface=" + RECT_STR + ".+");
    921         private static final Pattern sContentInsetsPattern =
    922                 Pattern.compile("Cur insets.+content=" + NEGATIVE_VALUES_ALLOWED_RECT_STR + ".+");
    923         private static final Pattern sGivenContentInsetsPattern =
    924                 Pattern.compile("mGivenContentInsets=" + RECT_STR + ".+");
    925         private static final Pattern sCropPattern =
    926             Pattern.compile(".+mLastClipRect=" + RECT_STR + ".*");
    927         private static final Pattern sSurfacePattern =
    928                 Pattern.compile("Surface: shown=(\\S+) layer=(\\d+) alpha=[\\d.]+ rect=\\([\\d.-]+,[\\d.-]+\\) [\\d.]+ x [\\d.]+.*");
    929         private static final Pattern sAttrsPattern=
    930                 Pattern.compile("mAttrs=WM\\.LayoutParams\\{.*ty=(\\d+).*\\}");
    931 
    932 
    933         private final String mName;
    934         private final String mAppToken;
    935         private final int mWindowType;
    936         private int mType;
    937         private int mDisplayId;
    938         private int mStackId;
    939         private int mLayer;
    940         private boolean mShown;
    941         private Rectangle mContainingFrame = new Rectangle();
    942         private Rectangle mParentFrame = new Rectangle();
    943         private Rectangle mContentFrame = new Rectangle();
    944         private Rectangle mFrame = new Rectangle();
    945         private Rectangle mSurfaceInsets = new Rectangle();
    946         private Rectangle mContentInsets = new Rectangle();
    947         private Rectangle mGivenContentInsets = new Rectangle();
    948         private Rectangle mCrop = new Rectangle();
    949 
    950 
    951         private WindowState(Matcher matcher, int windowType) {
    952             mName = matcher.group(4);
    953             mAppToken = matcher.group(2);
    954             mWindowType = windowType;
    955         }
    956 
    957         public String getName() {
    958             return mName;
    959         }
    960 
    961         String getToken() {
    962             return mAppToken;
    963         }
    964 
    965         boolean isStartingWindow() {
    966             return mWindowType == WINDOW_TYPE_STARTING;
    967         }
    968 
    969         boolean isExitingWindow() {
    970             return mWindowType == WINDOW_TYPE_EXITING;
    971         }
    972 
    973         boolean isDebuggerWindow() {
    974             return mWindowType == WINDOW_TYPE_DEBUGGER;
    975         }
    976 
    977         int getDisplayId() {
    978             return mDisplayId;
    979         }
    980 
    981         int getStackId() {
    982             return mStackId;
    983         }
    984 
    985         int getLayer() {
    986             return mLayer;
    987         }
    988 
    989         Rectangle getContainingFrame() {
    990             return mContainingFrame;
    991         }
    992 
    993         Rectangle getFrame() {
    994             return mFrame;
    995         }
    996 
    997         Rectangle getSurfaceInsets() {
    998             return mSurfaceInsets;
    999         }
   1000 
   1001         Rectangle getContentInsets() {
   1002             return mContentInsets;
   1003         }
   1004 
   1005         Rectangle getGivenContentInsets() {
   1006             return mGivenContentInsets;
   1007         }
   1008 
   1009         Rectangle getContentFrame() {
   1010             return mContentFrame;
   1011         }
   1012 
   1013         Rectangle getParentFrame() {
   1014             return mParentFrame;
   1015         }
   1016 
   1017         Rectangle getCrop() {
   1018             return mCrop;
   1019         }
   1020 
   1021         boolean isShown() {
   1022             return mShown;
   1023         }
   1024 
   1025         int getType() {
   1026             return mType;
   1027         }
   1028 
   1029         static WindowState create(LinkedList<String> dump, Pattern[] exitPatterns) {
   1030             final String line = dump.peek().trim();
   1031 
   1032             Matcher matcher = sWindowPattern.matcher(line);
   1033             if (!matcher.matches()) {
   1034                 return null;
   1035             }
   1036 
   1037             log(TAG + "WINDOW: " + line);
   1038             dump.pop();
   1039 
   1040             final WindowState window;
   1041             Matcher specialMatcher;
   1042             if ((specialMatcher = sStartingWindowPattern.matcher(line)).matches()) {
   1043                 log(TAG + "STARTING: " + line);
   1044                 window = new WindowState(specialMatcher, WINDOW_TYPE_STARTING);
   1045             } else if ((specialMatcher = sExitingWindowPattern.matcher(line)).matches()) {
   1046                 log(TAG + "EXITING: " + line);
   1047                 window = new WindowState(specialMatcher, WINDOW_TYPE_EXITING);
   1048             } else if ((specialMatcher = sDebuggerWindowPattern.matcher(line)).matches()) {
   1049                 log(TAG + "DEBUGGER: " + line);
   1050                 window = new WindowState(specialMatcher, WINDOW_TYPE_DEBUGGER);
   1051             } else {
   1052                 window = new WindowState(matcher, WINDOW_TYPE_NORMAL);
   1053             }
   1054 
   1055             window.extract(dump, exitPatterns);
   1056             return window;
   1057         }
   1058 
   1059         private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
   1060             while (!doneExtracting(dump, exitPatterns)) {
   1061                 final String line = dump.pop().trim();
   1062 
   1063                 Matcher matcher = sWindowAssociationPattern.matcher(line);
   1064                 if (matcher.matches()) {
   1065                     log(TAG + "WINDOW_ASSOCIATION: " + line);
   1066                     mDisplayId = Integer.valueOf(matcher.group(1));
   1067                     mStackId = Integer.valueOf(matcher.group(2));
   1068                     continue;
   1069                 }
   1070 
   1071                 matcher = sMainFramePattern.matcher(line);
   1072                 if (matcher.matches()) {
   1073                     log(TAG + "MAIN WINDOW FRAME: " + line);
   1074                     mFrame = extractBounds(matcher);
   1075                     continue;
   1076                 }
   1077 
   1078                 matcher = sFramePattern.matcher(line);
   1079                 if (matcher.matches()) {
   1080                     log(TAG + "FRAME: " + line);
   1081                     extractMultipleBounds(matcher, 1, mContainingFrame, mParentFrame);
   1082                     continue;
   1083                 }
   1084 
   1085                 matcher = sContentFramePattern.matcher(line);
   1086                 if (matcher.matches()) {
   1087                     log(TAG + "CONTENT FRAME: " + line);
   1088                     mContentFrame = extractBounds(matcher);
   1089                 }
   1090 
   1091                 matcher = sSurfaceInsetsPattern.matcher(line);
   1092                 if (matcher.matches()) {
   1093                     log(TAG + "INSETS: " + line);
   1094                     mSurfaceInsets = extractBounds(matcher);
   1095                 }
   1096 
   1097                 matcher = sContentInsetsPattern.matcher(line);
   1098                 if (matcher.matches()) {
   1099                     log(TAG + "CONTENT INSETS: " + line);
   1100                     mContentInsets = extractBounds(matcher);
   1101                 }
   1102 
   1103                 matcher = sCropPattern.matcher(line);
   1104                 if (matcher.matches()) {
   1105                     log(TAG + "CROP: " + line);
   1106                     mCrop = extractBounds(matcher);
   1107                 }
   1108 
   1109                 matcher = sSurfacePattern.matcher(line);
   1110                 if (matcher.matches()) {
   1111                     log(TAG + "SURFACE: " + line);
   1112                     mShown = Boolean.valueOf(matcher.group(1));
   1113                     mLayer = Integer.valueOf(matcher.group(2));
   1114                 }
   1115 
   1116                 matcher = sAttrsPattern.matcher(line);
   1117                 if (matcher.matches()) {
   1118                     log(TAG + "ATTRS: " + line);
   1119                     mType = Integer.valueOf(matcher.group(1));
   1120                 }
   1121 
   1122                 matcher = sGivenContentInsetsPattern.matcher(line);
   1123                 if (matcher.matches()) {
   1124                     log(TAG + "GIVEN CONTENT INSETS: " + line);
   1125                     mGivenContentInsets = extractBounds(matcher);
   1126                 }
   1127 
   1128                 // Extract other info here if needed
   1129             }
   1130         }
   1131 
   1132         private static String getWindowTypeSuffix(int windowType) {
   1133             switch (windowType) {
   1134             case WINDOW_TYPE_STARTING: return " STARTING";
   1135             case WINDOW_TYPE_EXITING: return " EXITING";
   1136             case WINDOW_TYPE_DEBUGGER: return " DEBUGGER";
   1137             default: break;
   1138             }
   1139             return "";
   1140         }
   1141 
   1142         @Override
   1143         public String toString() {
   1144             return "WindowState: {" + mAppToken + " " + mName
   1145                     + getWindowTypeSuffix(mWindowType) + "}" + " type=" + mType
   1146                     + " cf=" + mContainingFrame + " pf=" + mParentFrame;
   1147         }
   1148     }
   1149 }
   1150