blob: a2b95ca4ddfc007c656df43ab7bd106467be9980 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.tools.common.traces.component
import android.tools.common.traces.surfaceflinger.Layer
import android.tools.common.traces.wm.Activity
import android.tools.common.traces.wm.WindowContainer
/** ComponentMatcher based on name */
class ComponentNameMatcher(var component: ComponentName) : IComponentNameMatcher {
override val packageName: String
get() = component.packageName
override val className: String
get() = component.className
override fun toActivityName(): String = component.toActivityName()
override fun toWindowName(): String = component.toWindowName()
override fun toLayerName(): String = component.toLayerName()
constructor(
packageName: String,
className: String
) : this(ComponentName(packageName, className))
constructor(className: String) : this("", className)
override fun activityRecordMatchesAnyOf(layers: Array<Layer>): Boolean =
layers.any { activityRecordFilter.invoke(it.name) }
override fun componentNameMatcherToString(): String {
return "ComponentNameMatcher(\"${this.packageName}\", " + "\"${this.className}\")"
}
/** {@inheritDoc} */
override fun windowMatchesAnyOf(windows: Array<WindowContainer>): Boolean =
windows.any { windowNameFilter.invoke(it.title) }
/** {@inheritDoc} */
override fun activityMatchesAnyOf(activities: Array<Activity>): Boolean =
activities.any { activityNameFilter.invoke(it.name) }
/** {@inheritDoc} */
override fun layerMatchesAnyOf(layers: Array<Layer>): Boolean =
layers.any { layerNameFilter.invoke(it.name) }
/** {@inheritDoc} */
override fun check(
layers: Collection<Layer>,
condition: (Collection<Layer>) -> Boolean
): Boolean = condition(layers.filter { layerMatchesAnyOf(it) })
/** {@inheritDoc} */
override fun toActivityIdentifier(): String = component.toActivityName()
/** {@inheritDoc} */
override fun toWindowIdentifier(): String = component.toWindowName()
/** {@inheritDoc} */
override fun toLayerIdentifier(): String = component.toLayerName()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ComponentNameMatcher) return false
return component == other.component
}
override fun hashCode(): Int = component.hashCode()
override fun toString(): String = component.toString()
private val activityRecordFilter: (String) -> Boolean
get() = { it.startsWith("ActivityRecord{") && it.contains(component.toShortWindowName()) }
private val activityNameFilter: (String) -> Boolean
get() = { it.contains(component.toActivityName()) }
private val windowNameFilter: (String) -> Boolean
get() = { it.contains(component.toWindowName()) }
private val layerNameFilter: (String) -> Boolean
get() = { it.contains(component.toLayerName()) }
companion object {
val NAV_BAR = ComponentNameMatcher("", "NavigationBar0")
val TASK_BAR = ComponentNameMatcher("", "Taskbar")
val STATUS_BAR = ComponentNameMatcher("", "StatusBar")
val ROTATION = ComponentNameMatcher("", "RotationLayer")
val BACK_SURFACE = ComponentNameMatcher("", "BackColorSurface")
val IME = ComponentNameMatcher("", "InputMethod")
val IME_SNAPSHOT = ComponentNameMatcher("", "IME-snapshot-surface")
val SPLASH_SCREEN = ComponentNameMatcher("", "Splash Screen")
val SNAPSHOT = ComponentNameMatcher("", "SnapshotStartingWindow")
val TRANSITION_SNAPSHOT = ComponentNameMatcher("", "transition snapshot")
val LETTERBOX = ComponentNameMatcher("", "Letterbox")
val WALLPAPER_BBQ_WRAPPER = ComponentNameMatcher("", "Wallpaper BBQ wrapper")
val PIP_CONTENT_OVERLAY = ComponentNameMatcher("", "PipContentOverlay")
val EDGE_BACK_GESTURE_HANDLER = ComponentNameMatcher("", "EdgeBackGestureHandler")
val COLOR_FADE_BLAST = ComponentNameMatcher("", "ColorFade BLAST")
val LAUNCHER =
ComponentNameMatcher(
"com.google.android.apps.nexuslauncher",
"com.google.android.apps.nexuslauncher.NexusLauncherActivity"
)
val AOSP_LAUNCHER =
ComponentNameMatcher(
"com.android.launcher3",
"com.android.launcher3.uioverrides.QuickstepLauncher"
)
val SPLIT_DIVIDER = ComponentNameMatcher("", "StageCoordinatorSplitDivider")
val DEFAULT_TASK_DISPLAY_AREA = ComponentNameMatcher("", "DefaultTaskDisplayArea")
/**
* Creates a component matcher from a window or layer name.
*
* Requires the [str] to contain both the package and class name (with a / separator)
*
* @param str Value to parse
*/
fun unflattenFromString(str: String): ComponentNameMatcher {
val sep = str.indexOf('/')
if (sep < 0 || sep + 1 >= str.length) {
error("Missing package/class separator")
}
val pkg = str.substring(0, sep)
var cls = str.substring(sep + 1)
if (cls.isNotEmpty() && cls[0] == '.') {
cls = pkg + cls
}
return ComponentNameMatcher(pkg, cls)
}
/**
* Creates a component matcher from a window or layer name. The name might contain junk,
* which will be removed to only extract package and class name (e.g. other words before
* package name, separated by spaces, #id in the end after the class name)
*
* Requires the [str] to contain both the package and class name (with a / separator)
*
* @param str Value to parse
*/
fun unflattenFromStringWithJunk(str: String): ComponentNameMatcher {
val sep = str.indexOf('/')
if (sep < 0 || sep + 1 >= str.length) {
error("Missing package/class separator")
}
var pkg = str.substring(0, sep)
var pkgSep: Int = -1
val pkgCharArr = pkg.toCharArray()
for (index in (0..pkgCharArr.lastIndex).reversed()) {
val currentChar = pkgCharArr[index]
if (currentChar !in 'A'..'Z' && currentChar !in 'a'..'z' && currentChar != '.') {
pkgSep = index
break
}
}
if (!(pkgSep < 0 || pkgSep + 1 >= pkg.length)) {
pkg = pkg.substring(pkgSep, pkg.length)
}
var cls = str.substring(sep + 1)
var clsSep = -1 // cls.indexOf('#')
val clsCharArr = cls.toCharArray()
for (index in (0..clsCharArr.lastIndex)) {
val currentChar = clsCharArr[index]
if (currentChar !in 'A'..'Z' && currentChar !in 'a'..'z' && currentChar != '.') {
clsSep = index
break
}
}
if (!(clsSep < 0 || clsSep + 1 >= cls.length)) {
cls = cls.substring(0, clsSep)
}
if (cls.isNotEmpty() && cls[0] == '.') {
cls = pkg + cls
}
return ComponentNameMatcher(pkg, cls)
}
}
}