1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.pm;
18 
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Objects;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24 
25 /**
26  * A utility class to parse the window dump.
27  */
28 class WindowDumpParser {
29     private static final String WINDOW_TYPE_APPLICATION_STARTING = "APPLICATION_STARTING";
30 
31     /**
32      * Parses the provided window dump and returns the list of windows only for a particular app.
33      *
34      * @param dump the window dump in string format as returned from `dumpsys window
35      *         windows`.
36      * @param appPackageName the package name of the app, which the windows are being
37      *         requested for.
38      * @return a list of parsed {@link Window} objects.
39      */
getParsedAppWindows(String dump, String appPackageName)40     public static List<Window> getParsedAppWindows(String dump, String appPackageName) {
41         Pattern dumpSplitter = Pattern.compile("(Window #)|\\n\\n");
42         // \\n\\n to separate out the Global dump from the windows list.
43 
44         Pattern windowDetailsPattern = Pattern.compile("\\d*.*\\n"
45                         + ".*mDisplayId=(\\S*).*\\n"
46                         + ".*package=(\\S*).*\\n"
47                         + ".*ty=(\\S*)"
48                         + "((.*ActivityRecord\\{(.*?)\\}.*\\n)|(.*\\n))*"
49                 // (.*\\n) is required for skipping the lines before the line containing
50                 // ActivityRecord{}.
51         );
52         List<Window> windows = new ArrayList<>();
53 
54         String[] windowDumps = dumpSplitter.split(dump);
55         for (int i = 1; i < windowDumps.length - 1; i++) {
56             Matcher m = windowDetailsPattern.matcher(windowDumps[i]);
57             if (m.find()) {
58                 // Only consider windows for the given appPackageName which are not the splash
59                 // screen windows.
60                 // TODO(b/192355798): Revisit this logic as window type can be changed.
61                 if (Objects.equals(m.group(2), appPackageName)
62                         && !Objects.equals(m.group(3), WINDOW_TYPE_APPLICATION_STARTING)) {
63                     windows.add(new Window(
64                             /* packageName = */ m.group(2),
65                             /* displayId = */ Integer.parseInt(m.group(1)),
66                             /* activityRecord = */ m.group(6)
67                     ));
68                 }
69             }
70         }
71         return windows;
72     }
73 
74     /**
75      * A holder class that represents an app's window.
76      */
77     static class Window {
78         private final String mPackageName;
79         private final int mDisplayId;
80         private final String mActivityRecord;
81 
Window(String packageName, int displayId, String activityRecord)82         Window(String packageName, int displayId, String activityRecord) {
83             mPackageName = packageName;
84             mDisplayId = displayId;
85             mActivityRecord = activityRecord;
86         }
87 
getPackageName()88         public String getPackageName() {
89             return mPackageName;
90         }
91 
getDisplayId()92         public int getDisplayId() {
93             return mDisplayId;
94         }
95 
getActivityRecord()96         public String getActivityRecord() {
97             return mActivityRecord;
98         }
99 
100         @Override
equals(Object o)101         public boolean equals(Object o) {
102             if (this == o) return true;
103             if (!(o instanceof Window)) return false;
104             Window window = (Window) o;
105             return mDisplayId == window.mDisplayId
106                     && mPackageName.equals(window.mPackageName)
107                     && Objects.equals(mActivityRecord, window.mActivityRecord);
108         }
109 
110         @Override
hashCode()111         public int hashCode() {
112             return Objects.hash(mPackageName, mDisplayId, mActivityRecord);
113         }
114 
115         @Override
toString()116         public String toString() {
117             return "Window{"
118                     + "mPackageName=" + mPackageName
119                     + ", mDisplayId=" + mDisplayId
120                     + ", mActivityRecord={" + mActivityRecord + "}"
121                     + "}";
122         }
123     }
124 }
125