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.traces.component 18 19 import android.tools.traces.surfaceflinger.Layer 20 import android.tools.traces.wm.Activity 21 import android.tools.traces.wm.WindowContainer 22 import java.util.function.Predicate 23 24 /** ComponentMatcher based on name */ 25 class ComponentNameMatcher(var component: ComponentName) : IComponentNameMatcher { 26 override val packageName: String 27 get() = component.packageName 28 29 override val className: String 30 get() = component.className 31 toActivityNamenull32 override fun toActivityName(): String = component.toActivityName() 33 34 override fun toWindowName(): String = component.toWindowName() 35 36 override fun toLayerName(): String = component.toLayerName() 37 38 constructor( 39 packageName: String, 40 className: String, 41 ) : this(ComponentName(packageName, className)) 42 43 constructor(className: String) : this("", className) 44 45 override fun activityRecordMatchesAnyOf(layers: Collection<Layer>): Boolean = 46 layers.any { activityRecordFilter.invoke(it.name) } 47 componentNameMatcherToStringnull48 override fun componentNameMatcherToString(): String { 49 return "ComponentNameMatcher(\"${this.packageName}\", " + "\"${this.className}\")" 50 } 51 52 /** {@inheritDoc} */ windowMatchesAnyOfnull53 override fun windowMatchesAnyOf(windows: Collection<WindowContainer>): Boolean = 54 windows.any { windowNameFilter.invoke(it.title) } 55 56 /** {@inheritDoc} */ activityMatchesAnyOfnull57 override fun activityMatchesAnyOf(activities: Collection<Activity>): Boolean = 58 activities.any { activityNameFilter.invoke(it.name) } 59 60 /** {@inheritDoc} */ layerMatchesAnyOfnull61 override fun layerMatchesAnyOf(layers: Collection<Layer>): Boolean = 62 layers.any { layerNameFilter.invoke(it.name) } 63 64 /** {@inheritDoc} */ checknull65 override fun check( 66 layers: Collection<Layer>, 67 condition: Predicate<Collection<Layer>>, 68 ): Boolean = condition.test(layers.filter { layerMatchesAnyOf(it) }) 69 70 /** {@inheritDoc} */ toActivityIdentifiernull71 override fun toActivityIdentifier(): String = component.toActivityName() 72 73 /** {@inheritDoc} */ 74 override fun toWindowIdentifier(): String = component.toWindowName() 75 76 /** {@inheritDoc} */ 77 override fun toLayerIdentifier(): String = component.toLayerName() 78 79 override fun equals(other: Any?): Boolean { 80 if (this === other) return true 81 if (other !is ComponentNameMatcher) return false 82 return component == other.component 83 } 84 hashCodenull85 override fun hashCode(): Int = component.hashCode() 86 87 override fun toString(): String = component.toString() 88 89 private val activityRecordFilter: (String) -> Boolean 90 get() = { it.startsWith("ActivityRecord{") && it.contains(component.toShortWindowName()) } 91 92 private val activityNameFilter: (String) -> Boolean <lambda>null93 get() = { it.contains(component.toActivityName()) } 94 95 private val windowNameFilter: (String) -> Boolean <lambda>null96 get() = { it.contains(component.toWindowName()) } 97 98 private val layerNameFilter: (String) -> Boolean <lambda>null99 get() = { it.contains(component.toLayerName()) } 100 101 companion object { 102 @JvmField val NAV_BAR = ComponentNameMatcher("", "NavigationBar0") 103 @JvmField val TASK_BAR = ComponentNameMatcher("", "Taskbar") 104 @JvmField val STATUS_BAR = ComponentNameMatcher("", "StatusBar") 105 @JvmField val ROTATION = ComponentNameMatcher("", "RotationLayer") 106 @JvmField val BACK_SURFACE = ComponentNameMatcher("", "BackColorSurface") 107 @JvmField val IME = ComponentNameMatcher("", "InputMethod") 108 @JvmField val IME_SNAPSHOT = ComponentNameMatcher("", "IME-snapshot-surface") 109 @JvmField val SPLASH_SCREEN = ComponentNameMatcher("", "Splash Screen") 110 @JvmField val SNAPSHOT = ComponentNameMatcher("", "SnapshotStartingWindow") 111 @JvmField val SECONDARY_HOME_HANDLE = ComponentNameMatcher("", "SecondaryHomeHandle") 112 113 @JvmField val TRANSITION_SNAPSHOT = ComponentNameMatcher("", "transition snapshot") 114 @JvmField val LETTERBOX = ComponentNameMatcher("", "Letterbox") 115 116 @JvmField val WALLPAPER_BBQ_WRAPPER = ComponentNameMatcher("", "Wallpaper BBQ wrapper") 117 118 @JvmField val PIP_CONTENT_OVERLAY = ComponentNameMatcher("", "PipContentOverlay") 119 120 @JvmField val PIP_MENU_OVERLAY = ComponentNameMatcher("", "PipMenuView") 121 122 @JvmField 123 val EDGE_BACK_GESTURE_HANDLER = ComponentNameMatcher("", "EdgeBackGestureHandler0") 124 125 @JvmField val COLOR_FADE = ComponentNameMatcher("", "ColorFade") 126 127 @JvmField val WALLPAPER_WINDOW_TOKEN = ComponentNameMatcher("", "WallpaperWindowToken") 128 129 @JvmField val NOTIFICATION_SHADE = ComponentNameMatcher("", "NotificationShade") 130 131 @JvmField val VOLUME_DIALOG = ComponentNameMatcher("", "VolumeDialog") 132 133 @JvmField val FLOATING_ROTATION_BUTTON = ComponentNameMatcher("", "FloatingRotationButton") 134 135 @JvmField val SCREEN_DECOR_OVERLAY = ComponentNameMatcher("", "ScreenDecorHwcOverlay#") 136 137 @JvmField val POINTER_LOCATION = ComponentNameMatcher("", "PointerLocation") 138 139 @JvmField 140 val WIRED_CHARGING_ANIMATION = ComponentNameMatcher("", "Wired Charging Animation#") 141 142 @JvmField val SCREEN_RECORDING_OVERLAYS = SCREEN_DECOR_OVERLAY.or(POINTER_LOCATION) 143 144 @JvmField 145 val LAUNCHER = 146 ComponentNameMatcher( 147 "com.google.android.apps.nexuslauncher", 148 "com.google.android.apps.nexuslauncher.NexusLauncherActivity", 149 ) 150 151 @JvmField 152 val AOSP_LAUNCHER = 153 ComponentNameMatcher( 154 "com.android.launcher3", 155 "com.android.launcher3.uioverrides.QuickstepLauncher", 156 ) 157 158 @JvmField val SPLIT_DIVIDER = ComponentNameMatcher("", "StageCoordinatorSplitDivider") 159 160 @JvmField val DEFAULT_TASK_DISPLAY_AREA = ComponentNameMatcher("", "DefaultTaskDisplayArea") 161 162 /** 163 * Creates a component matcher from a window or layer name. 164 * 165 * Requires the [str] to contain both the package and class name (with a / separator) 166 * 167 * @param str Value to parse 168 */ 169 @JvmStatic unflattenFromStringnull170 fun unflattenFromString(str: String): ComponentNameMatcher { 171 val sep = str.indexOf('/') 172 if (sep < 0 || sep + 1 >= str.length) { 173 error("Missing package/class separator") 174 } 175 val pkg = str.substring(0, sep) 176 var cls = str.substring(sep + 1) 177 if (cls.isNotEmpty() && cls[0] == '.') { 178 cls = pkg + cls 179 } 180 return ComponentNameMatcher(pkg, cls) 181 } 182 183 /** 184 * Creates a component matcher from a window or layer name. The name might contain junk, 185 * which will be removed to only extract package and class name (e.g. other words before 186 * package name, separated by spaces, #id in the end after the class name) 187 * 188 * Requires the [str] to contain both the package and class name (with a / separator) 189 * 190 * @param str Value to parse 191 */ 192 @JvmStatic unflattenFromStringWithJunknull193 fun unflattenFromStringWithJunk(str: String): ComponentNameMatcher { 194 val sep = str.indexOf('/') 195 if (sep < 0 || sep + 1 >= str.length) { 196 error("Missing package/class separator") 197 } 198 199 var pkg = str.substring(0, sep) 200 var pkgSep: Int = -1 201 val pkgCharArr = pkg.toCharArray() 202 for (index in (0..pkgCharArr.lastIndex).reversed()) { 203 val currentChar = pkgCharArr[index] 204 if (currentChar !in 'A'..'Z' && currentChar !in 'a'..'z' && currentChar != '.') { 205 pkgSep = index 206 break 207 } 208 } 209 if (!(pkgSep < 0 || pkgSep + 1 >= pkg.length)) { 210 pkg = pkg.substring(pkgSep, pkg.length) 211 } 212 213 var cls = str.substring(sep + 1) 214 var clsSep = -1 // cls.indexOf('#') 215 val clsCharArr = cls.toCharArray() 216 for (index in (0..clsCharArr.lastIndex)) { 217 val currentChar = clsCharArr[index] 218 if (currentChar !in 'A'..'Z' && currentChar !in 'a'..'z' && currentChar != '.') { 219 clsSep = index 220 break 221 } 222 } 223 if (!(clsSep < 0 || clsSep + 1 >= cls.length)) { 224 cls = cls.substring(0, clsSep) 225 } 226 227 if (cls.isNotEmpty() && cls[0] == '.') { 228 cls = pkg + cls 229 } 230 return ComponentNameMatcher(pkg, cls) 231 } 232 } 233 } 234