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 
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: Collection<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: Collection<WindowContainer>): Boolean =
49         windows.any { windowNameFilter.invoke(it.title) }
50 
51     /** {@inheritDoc} */
activityMatchesAnyOfnull52     override fun activityMatchesAnyOf(activities: Collection<Activity>): Boolean =
53         activities.any { activityNameFilter.invoke(it.name) }
54 
55     /** {@inheritDoc} */
layerMatchesAnyOfnull56     override fun layerMatchesAnyOf(layers: Collection<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 PIP_MENU_OVERLAY = ComponentNameMatcher("", "PipMenuView")
116 
117         val EDGE_BACK_GESTURE_HANDLER = ComponentNameMatcher("", "EdgeBackGestureHandler0")
118 
119         val COLOR_FADE = ComponentNameMatcher("", "ColorFade")
120 
121         val WALLPAPER_WINDOW_TOKEN = ComponentNameMatcher("", "WallpaperWindowToken")
122 
123         val NOTIFICATION_SHADE = ComponentNameMatcher("", "NotificationShade")
124 
125         val VOLUME_DIALOG = ComponentNameMatcher("", "VolumeDialog")
126 
127         val FLOATING_ROTATION_BUTTON = ComponentNameMatcher("", "FloatingRotationButton")
128 
129         val SCREEN_DECOR_OVERLAY = ComponentNameMatcher("", "ScreenDecorHwcOverlay#")
130 
131         val POINTER_LOCATION = ComponentNameMatcher("", "PointerLocation")
132 
133         val WIRED_CHARGING_ANIMATION = ComponentNameMatcher("", "Wired Charging Animation#")
134 
135         val SCREEN_RECORDING_OVERLAYS = SCREEN_DECOR_OVERLAY.or(POINTER_LOCATION)
136 
137         val LAUNCHER =
138             ComponentNameMatcher(
139                 "com.google.android.apps.nexuslauncher",
140                 "com.google.android.apps.nexuslauncher.NexusLauncherActivity"
141             )
142 
143         val AOSP_LAUNCHER =
144             ComponentNameMatcher(
145                 "com.android.launcher3",
146                 "com.android.launcher3.uioverrides.QuickstepLauncher"
147             )
148 
149         val SPLIT_DIVIDER = ComponentNameMatcher("", "StageCoordinatorSplitDivider")
150 
151         val DEFAULT_TASK_DISPLAY_AREA = ComponentNameMatcher("", "DefaultTaskDisplayArea")
152 
153         /**
154          * Creates a component matcher from a window or layer name.
155          *
156          * Requires the [str] to contain both the package and class name (with a / separator)
157          *
158          * @param str Value to parse
159          */
unflattenFromStringnull160         fun unflattenFromString(str: String): ComponentNameMatcher {
161             val sep = str.indexOf('/')
162             if (sep < 0 || sep + 1 >= str.length) {
163                 error("Missing package/class separator")
164             }
165             val pkg = str.substring(0, sep)
166             var cls = str.substring(sep + 1)
167             if (cls.isNotEmpty() && cls[0] == '.') {
168                 cls = pkg + cls
169             }
170             return ComponentNameMatcher(pkg, cls)
171         }
172 
173         /**
174          * Creates a component matcher from a window or layer name. The name might contain junk,
175          * which will be removed to only extract package and class name (e.g. other words before
176          * package name, separated by spaces, #id in the end after the class name)
177          *
178          * Requires the [str] to contain both the package and class name (with a / separator)
179          *
180          * @param str Value to parse
181          */
unflattenFromStringWithJunknull182         fun unflattenFromStringWithJunk(str: String): ComponentNameMatcher {
183             val sep = str.indexOf('/')
184             if (sep < 0 || sep + 1 >= str.length) {
185                 error("Missing package/class separator")
186             }
187 
188             var pkg = str.substring(0, sep)
189             var pkgSep: Int = -1
190             val pkgCharArr = pkg.toCharArray()
191             for (index in (0..pkgCharArr.lastIndex).reversed()) {
192                 val currentChar = pkgCharArr[index]
193                 if (currentChar !in 'A'..'Z' && currentChar !in 'a'..'z' && currentChar != '.') {
194                     pkgSep = index
195                     break
196                 }
197             }
198             if (!(pkgSep < 0 || pkgSep + 1 >= pkg.length)) {
199                 pkg = pkg.substring(pkgSep, pkg.length)
200             }
201 
202             var cls = str.substring(sep + 1)
203             var clsSep = -1 // cls.indexOf('#')
204             val clsCharArr = cls.toCharArray()
205             for (index in (0..clsCharArr.lastIndex)) {
206                 val currentChar = clsCharArr[index]
207                 if (currentChar !in 'A'..'Z' && currentChar !in 'a'..'z' && currentChar != '.') {
208                     clsSep = index
209                     break
210                 }
211             }
212             if (!(clsSep < 0 || clsSep + 1 >= cls.length)) {
213                 cls = cls.substring(0, clsSep)
214             }
215 
216             if (cls.isNotEmpty() && cls[0] == '.') {
217                 cls = pkg + cls
218             }
219             return ComponentNameMatcher(pkg, cls)
220         }
221     }
222 }
223