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 
computeState(ITestDevice device)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 
parseSysDump(String sysDump)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 
getMatchingWindowTokens(final String windowName, List<String> tokenList)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 
getMatchingVisibleWindowState(final String windowName, List<WindowState> windowList)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 
getPrefixMatchingVisibleWindowState(final String windowName, List<WindowState> windowList)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 
getWindowByPackageName(String packageName, int windowType)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 
getWindowsByPackageName(String packageName, List<Integer> restrictToTypeList, List<WindowState> outWindowList)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 
sortWindowsByLayer(List<WindowState> windows)418     void sortWindowsByLayer(List<WindowState> windows) {
419         windows.sort(Comparator.comparingInt(WindowState::getLayer));
420     }
421 
getWindowStateForAppToken(String appToken)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 
getDisplay(int displayId)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 
getFrontWindow()440     String getFrontWindow() {
441         if (mWindowStates == null || mWindowStates.isEmpty()) {
442             return null;
443         }
444         return mWindowStates.get(0).getName();
445     }
446 
getFocusedWindow()447     String getFocusedWindow() {
448         return mFocusedWindow;
449     }
450 
getFocusedApp()451     String getFocusedApp() {
452         return mFocusedApp;
453     }
454 
getLastTransition()455     String getLastTransition() {
456         return mLastTransition;
457     }
458 
getAppTransitionState()459     String getAppTransitionState() {
460         return mAppTransitionState;
461     }
462 
getFrontStackId(int displayId)463     int getFrontStackId(int displayId) {
464         return mDisplayStacks.get(displayId).get(0).mStackId;
465     }
466 
getRotation()467     public int getRotation() {
468         return mRotation;
469     }
470 
getLastOrientation()471     int getLastOrientation() {
472         return mLastOrientation;
473     }
474 
containsStack(int stackId)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. */
containsWindow(String 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. */
isWindowVisible(String windowName)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 
allWindowsVisible(String windowName)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 
getStack(int stackId)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 
getStackPosition(int stackId)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 
getInputMethodWindowState()540     WindowState getInputMethodWindowState() {
541         return getWindowStateForAppToken(mInputMethodWindowAppToken);
542     }
543 
getStableBounds()544     Rectangle getStableBounds() {
545         return mStableBounds;
546     }
547 
getDefaultPinnedStackBounds()548     Rectangle getDefaultPinnedStackBounds() {
549         return mDefaultPinnedStackBounds;
550     }
551 
getPinnedStackMomentBounds()552     Rectangle getPinnedStackMomentBounds() {
553         return mPinnedStackMovementBounds;
554     }
555 
findFirstWindowWithType(int type)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 
isDisplayFrozen()565     public boolean isDisplayFrozen() {
566         return mDisplayFrozen;
567     }
568 
isDockedStackMinimized()569     public boolean isDockedStackMinimized() {
570         return mIsDockedStackMinimized;
571     }
572 
reset()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 
WindowStack()593         private WindowStack() {
594 
595         }
596 
create( LinkedList<String> dump, Pattern stackIdPattern, Pattern[] exitPatterns)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 
extract(LinkedList<String> dump, Pattern[] exitPatterns)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 
extractWindowAnimationBackgroundSurface(String line)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 
getTask(int taskId)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 
isWindowAnimationBackgroundSurfaceShowing()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 
WindowTask()688         private WindowTask() {
689         }
690 
create( LinkedList<String> dump, Pattern taskIdPattern, Pattern[] exitPatterns)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 
extract(LinkedList<String> dump, Pattern[] exitPatterns)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 
doneExtracting(LinkedList<String> dump, Pattern[] exitPatterns)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 
extractFullscreen(String line)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 
extractBounds(String line)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 
extractBounds(Matcher matcher)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 
extractMultipleBounds(Matcher matcher, int groupIndex, Rectangle... rectList)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 
getBounds()810         Rectangle getBounds() {
811             return mBounds;
812         }
813 
isFullscreen()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 
Display(int displayId)830         private Display(int displayId) {
831             mDisplayId = displayId;
832         }
833 
getDisplayId()834         int getDisplayId() {
835             return mDisplayId;
836         }
837 
getDpi()838         int getDpi() {
839             return mDpi;
840         }
841 
getDisplayRect()842         Rectangle getDisplayRect() {
843             return mDisplayRect;
844         }
845 
getAppRect()846         Rectangle getAppRect() {
847             return mAppRect;
848         }
849 
create(LinkedList<String> dump, Pattern[] exitPatterns)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 
extract(LinkedList<String> dump, Pattern[] exitPatterns)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
toString()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 
WindowState(Matcher matcher, int windowType)951         private WindowState(Matcher matcher, int windowType) {
952             mName = matcher.group(4);
953             mAppToken = matcher.group(2);
954             mWindowType = windowType;
955         }
956 
getName()957         public String getName() {
958             return mName;
959         }
960 
getToken()961         String getToken() {
962             return mAppToken;
963         }
964 
isStartingWindow()965         boolean isStartingWindow() {
966             return mWindowType == WINDOW_TYPE_STARTING;
967         }
968 
isExitingWindow()969         boolean isExitingWindow() {
970             return mWindowType == WINDOW_TYPE_EXITING;
971         }
972 
isDebuggerWindow()973         boolean isDebuggerWindow() {
974             return mWindowType == WINDOW_TYPE_DEBUGGER;
975         }
976 
getDisplayId()977         int getDisplayId() {
978             return mDisplayId;
979         }
980 
getStackId()981         int getStackId() {
982             return mStackId;
983         }
984 
getLayer()985         int getLayer() {
986             return mLayer;
987         }
988 
getContainingFrame()989         Rectangle getContainingFrame() {
990             return mContainingFrame;
991         }
992 
getFrame()993         Rectangle getFrame() {
994             return mFrame;
995         }
996 
getSurfaceInsets()997         Rectangle getSurfaceInsets() {
998             return mSurfaceInsets;
999         }
1000 
getContentInsets()1001         Rectangle getContentInsets() {
1002             return mContentInsets;
1003         }
1004 
getGivenContentInsets()1005         Rectangle getGivenContentInsets() {
1006             return mGivenContentInsets;
1007         }
1008 
getContentFrame()1009         Rectangle getContentFrame() {
1010             return mContentFrame;
1011         }
1012 
getParentFrame()1013         Rectangle getParentFrame() {
1014             return mParentFrame;
1015         }
1016 
getCrop()1017         Rectangle getCrop() {
1018             return mCrop;
1019         }
1020 
isShown()1021         boolean isShown() {
1022             return mShown;
1023         }
1024 
getType()1025         int getType() {
1026             return mType;
1027         }
1028 
create(LinkedList<String> dump, Pattern[] exitPatterns)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 
extract(LinkedList<String> dump, Pattern[] exitPatterns)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 
getWindowTypeSuffix(int windowType)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
toString()1143         public String toString() {
1144             return "WindowState: {" + mAppToken + " " + mName
1145                     + getWindowTypeSuffix(mWindowType) + "}" + " type=" + mType
1146                     + " cf=" + mContainingFrame + " pf=" + mParentFrame;
1147         }
1148     }
1149 }
1150