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