1 /* 2 * Copyright (C) 2023 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.tools.common.traces.component 18 19 import android.tools.common.traces.surfaceflinger.Layer 20 import android.tools.common.traces.wm.Activity 21 import android.tools.common.traces.wm.IWindowContainer 22 23 /** ComponentMatcher based on name */ 24 class ComponentNameMatcher(var component: ComponentName) : IComponentNameMatcher { 25 override val packageName: String 26 get() = component.packageName 27 override val className: String 28 get() = component.className toActivityNamenull29 override fun toActivityName(): String = component.toActivityName() 30 override fun toWindowName(): String = component.toWindowName() 31 override fun toLayerName(): String = component.toLayerName() 32 33 constructor( 34 packageName: String, 35 className: String 36 ) : this(ComponentName(packageName, className)) 37 38 constructor(className: String) : this("", className) 39 40 override fun activityRecordMatchesAnyOf(layers: Array<Layer>): Boolean = 41 layers.any { activityRecordFilter.invoke(it.name) } 42 componentNameMatcherToStringnull43 override fun componentNameMatcherToString(): String { 44 return "ComponentNameMatcher(\"${this.packageName}\", " + "\"${this.className}\")" 45 } 46 47 /** {@inheritDoc} */ windowMatchesAnyOfnull48 override fun windowMatchesAnyOf(windows: Array<IWindowContainer>): Boolean = 49 windows.any { windowNameFilter.invoke(it.title) } 50 51 /** {@inheritDoc} */ activityMatchesAnyOfnull52 override fun activityMatchesAnyOf(activities: Array<Activity>): Boolean = 53 activities.any { activityNameFilter.invoke(it.name) } 54 55 /** {@inheritDoc} */ layerMatchesAnyOfnull56 override fun layerMatchesAnyOf(layers: Array<Layer>): Boolean = 57 layers.any { layerNameFilter.invoke(it.name) } 58 59 /** {@inheritDoc} */ checknull60 override fun check( 61 layers: Collection<Layer>, 62 condition: (Collection<Layer>) -> Boolean 63 ): Boolean = condition(layers.filter { layerMatchesAnyOf(it) }) 64 65 /** {@inheritDoc} */ toActivityIdentifiernull66 override fun toActivityIdentifier(): String = component.toActivityName() 67 68 /** {@inheritDoc} */ 69 override fun toWindowIdentifier(): String = component.toWindowName() 70 71 /** {@inheritDoc} */ 72 override fun toLayerIdentifier(): String = component.toLayerName() 73 74 override fun equals(other: Any?): Boolean { 75 if (this === other) return true 76 if (other !is ComponentNameMatcher) return false 77 return component == other.component 78 } 79 hashCodenull80 override fun hashCode(): Int = component.hashCode() 81 82 override fun toString(): String = component.toString() 83 84 private val activityRecordFilter: (String) -> Boolean 85 get() = { it.startsWith("ActivityRecord{") && it.contains(component.toShortWindowName()) } 86 87 private val activityNameFilter: (String) -> Boolean <lambda>null88 get() = { it.contains(component.toActivityName()) } 89 90 private val windowNameFilter: (String) -> Boolean <lambda>null91 get() = { it.contains(component.toWindowName()) } 92 93 private val layerNameFilter: (String) -> Boolean <lambda>null94 get() = { it.contains(component.toLayerName()) } 95 96 companion object { 97 val NAV_BAR = ComponentNameMatcher("", "NavigationBar0") 98 val TASK_BAR = ComponentNameMatcher("", "Taskbar") 99 val STATUS_BAR = ComponentNameMatcher("", "StatusBar") 100 val ROTATION = ComponentNameMatcher("", "RotationLayer") 101 val BACK_SURFACE = ComponentNameMatcher("", "BackColorSurface") 102 val IME = ComponentNameMatcher("", "InputMethod") 103 val IME_SNAPSHOT = ComponentNameMatcher("", "IME-snapshot-surface") 104 val SPLASH_SCREEN = ComponentNameMatcher("", "Splash Screen") 105 val SNAPSHOT = ComponentNameMatcher("", "SnapshotStartingWindow") 106 val SECONDARY_HOME_HANDLE = ComponentNameMatcher("", "SecondaryHomeHandle") 107 108 val TRANSITION_SNAPSHOT = ComponentNameMatcher("", "transition snapshot") 109 val LETTERBOX = ComponentNameMatcher("", "Letterbox") 110 111 val WALLPAPER_BBQ_WRAPPER = ComponentNameMatcher("", "Wallpaper BBQ wrapper") 112 113 val PIP_CONTENT_OVERLAY = ComponentNameMatcher("", "PipContentOverlay") 114 115 val EDGE_BACK_GESTURE_HANDLER = ComponentNameMatcher("", "EdgeBackGestureHandler") 116 117 val COLOR_FADE = ComponentNameMatcher("", "ColorFade") 118 119 val WALLPAPER_WINDOW_TOKEN = ComponentNameMatcher("", "WallpaperWindowToken") 120 121 val NOTIFICATION_SHADE = ComponentNameMatcher("", "NotificationShade") 122 123 val VOLUME_DIALOG = ComponentNameMatcher("", "VolumeDialog") 124 125 val LAUNCHER = 126 ComponentNameMatcher( 127 "com.google.android.apps.nexuslauncher", 128 "com.google.android.apps.nexuslauncher.NexusLauncherActivity" 129 ) 130 131 val AOSP_LAUNCHER = 132 ComponentNameMatcher( 133 "com.android.launcher3", 134 "com.android.launcher3.uioverrides.QuickstepLauncher" 135 ) 136 137 val SPLIT_DIVIDER = ComponentNameMatcher("", "StageCoordinatorSplitDivider") 138 139 val DEFAULT_TASK_DISPLAY_AREA = ComponentNameMatcher("", "DefaultTaskDisplayArea") 140 141 /** 142 * Creates a component matcher from a window or layer name. 143 * 144 * Requires the [str] to contain both the package and class name (with a / separator) 145 * 146 * @param str Value to parse 147 */ unflattenFromStringnull148 fun unflattenFromString(str: String): ComponentNameMatcher { 149 val sep = str.indexOf('/') 150 if (sep < 0 || sep + 1 >= str.length) { 151 error("Missing package/class separator") 152 } 153 val pkg = str.substring(0, sep) 154 var cls = str.substring(sep + 1) 155 if (cls.isNotEmpty() && cls[0] == '.') { 156 cls = pkg + cls 157 } 158 return ComponentNameMatcher(pkg, cls) 159 } 160 161 /** 162 * Creates a component matcher from a window or layer name. The name might contain junk, 163 * which will be removed to only extract package and class name (e.g. other words before 164 * package name, separated by spaces, #id in the end after the class name) 165 * 166 * Requires the [str] to contain both the package and class name (with a / separator) 167 * 168 * @param str Value to parse 169 */ unflattenFromStringWithJunknull170 fun unflattenFromStringWithJunk(str: String): ComponentNameMatcher { 171 val sep = str.indexOf('/') 172 if (sep < 0 || sep + 1 >= str.length) { 173 error("Missing package/class separator") 174 } 175 176 var pkg = str.substring(0, sep) 177 var pkgSep: Int = -1 178 val pkgCharArr = pkg.toCharArray() 179 for (index in (0..pkgCharArr.lastIndex).reversed()) { 180 val currentChar = pkgCharArr[index] 181 if (currentChar !in 'A'..'Z' && currentChar !in 'a'..'z' && currentChar != '.') { 182 pkgSep = index 183 break 184 } 185 } 186 if (!(pkgSep < 0 || pkgSep + 1 >= pkg.length)) { 187 pkg = pkg.substring(pkgSep, pkg.length) 188 } 189 190 var cls = str.substring(sep + 1) 191 var clsSep = -1 // cls.indexOf('#') 192 val clsCharArr = cls.toCharArray() 193 for (index in (0..clsCharArr.lastIndex)) { 194 val currentChar = clsCharArr[index] 195 if (currentChar !in 'A'..'Z' && currentChar !in 'a'..'z' && currentChar != '.') { 196 clsSep = index 197 break 198 } 199 } 200 if (!(clsSep < 0 || clsSep + 1 >= cls.length)) { 201 cls = cls.substring(0, clsSep) 202 } 203 204 if (cls.isNotEmpty() && cls[0] == '.') { 205 cls = pkg + cls 206 } 207 return ComponentNameMatcher(pkg, cls) 208 } 209 } 210 } 211