blob: ad1af6297e5c6f25e61f0847b95d29050ed3e8a6 [file] [log] [blame]
/*
* Copyright (C) 2022 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 com.android.server.wm.traces.common
import com.android.server.wm.traces.common.layers.Layer
import com.android.server.wm.traces.common.layers.Transform
import com.android.server.wm.traces.common.layers.Transform.Companion.isFlagSet
import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.server.wm.traces.common.windowmanager.WindowManagerState
import com.android.server.wm.traces.common.windowmanager.windows.WindowState
import kotlin.js.JsName
object WindowManagerConditionsFactory {
private fun getNavBarComponent(wmState: WindowManagerState) =
if (wmState.isTablet) ComponentNameMatcher.TASK_BAR else ComponentNameMatcher.NAV_BAR
/**
* Condition to check if the [ComponentNameMatcher.NAV_BAR] or [ComponentNameMatcher.TASK_BAR]
* windows are visible
*/
@JsName("isNavOrTaskBarVisible")
fun isNavOrTaskBarVisible(): Condition<DeviceStateDump> =
ConditionList(
listOf(
isNavOrTaskBarWindowVisible(),
isNavOrTaskBarLayerVisible(),
isNavOrTaskBarLayerOpaque()
)
)
/**
* Condition to check if the [ComponentNameMatcher.NAV_BAR] or [ComponentNameMatcher.TASK_BAR]
* windows are visible
*/
@JsName("isNavOrTaskBarWindowVisible")
fun isNavOrTaskBarWindowVisible(): Condition<DeviceStateDump> =
Condition("isNavBarOrTaskBarWindowVisible") {
val component = getNavBarComponent(it.wmState)
it.wmState.isWindowSurfaceShown(component)
}
/**
* Condition to check if the [ComponentNameMatcher.NAV_BAR] or [ComponentNameMatcher.TASK_BAR]
* layers are visible
*/
@JsName("isNavOrTaskBarLayerVisible")
fun isNavOrTaskBarLayerVisible(): Condition<DeviceStateDump> =
Condition("isNavBarOrTaskBarLayerVisible") {
val component = getNavBarComponent(it.wmState)
it.layerState.isVisible(component)
}
/** Condition to check if the [ComponentNameMatcher.NAV_BAR] layer is opaque */
@JsName("isNavOrTaskBarLayerOpaque")
fun isNavOrTaskBarLayerOpaque(): Condition<DeviceStateDump> =
Condition("isNavOrTaskBarLayerOpaque") {
val component = getNavBarComponent(it.wmState)
it.layerState.getLayerWithBuffer(component)?.color?.isOpaque ?: false
}
/** Condition to check if the [ComponentNameMatcher.NAV_BAR] window is visible */
@JsName("isNavBarVisible")
fun isNavBarVisible(): Condition<DeviceStateDump> =
ConditionList(
listOf(isNavBarWindowVisible(), isNavBarLayerVisible(), isNavBarLayerOpaque())
)
/** Condition to check if the [ComponentNameMatcher.NAV_BAR] window is visible */
@JsName("isNavBarWindowVisible")
fun isNavBarWindowVisible(): Condition<DeviceStateDump> =
Condition("isNavBarWindowVisible") {
it.wmState.isWindowSurfaceShown(ComponentNameMatcher.NAV_BAR)
}
/** Condition to check if the [ComponentNameMatcher.NAV_BAR] layer is visible */
@JsName("isNavBarLayerVisible")
fun isNavBarLayerVisible(): Condition<DeviceStateDump> =
isLayerVisible(ComponentNameMatcher.NAV_BAR)
/** Condition to check if the [ComponentNameMatcher.NAV_BAR] layer is opaque */
@JsName("isNavBarLayerOpaque")
fun isNavBarLayerOpaque(): Condition<DeviceStateDump> =
Condition("isNavBarLayerOpaque") {
it.layerState.getLayerWithBuffer(ComponentNameMatcher.NAV_BAR)?.color?.isOpaque ?: false
}
/** Condition to check if the [ComponentNameMatcher.TASK_BAR] window is visible */
@JsName("isTaskBarVisible")
fun isTaskBarVisible(): Condition<DeviceStateDump> =
ConditionList(
listOf(isTaskBarWindowVisible(), isTaskBarLayerVisible(), isTaskBarLayerOpaque())
)
/** Condition to check if the [ComponentNameMatcher.TASK_BAR] window is visible */
@JsName("isTaskBarWindowVisible")
fun isTaskBarWindowVisible(): Condition<DeviceStateDump> =
Condition("isTaskBarWindowVisible") {
it.wmState.isWindowSurfaceShown(ComponentNameMatcher.TASK_BAR)
}
/** Condition to check if the [ComponentNameMatcher.TASK_BAR] layer is visible */
@JsName("isTaskBarLayerVisible")
fun isTaskBarLayerVisible(): Condition<DeviceStateDump> =
isLayerVisible(ComponentNameMatcher.TASK_BAR)
/** Condition to check if the [ComponentNameMatcher.TASK_BAR] layer is opaque */
@JsName("isTaskBarLayerOpaque")
fun isTaskBarLayerOpaque(): Condition<DeviceStateDump> =
Condition("isTaskBarLayerOpaque") {
it.layerState.getLayerWithBuffer(ComponentNameMatcher.TASK_BAR)?.color?.isOpaque
?: false
}
/** Condition to check if the [ComponentNameMatcher.STATUS_BAR] window is visible */
@JsName("isStatusBarVisible")
fun isStatusBarVisible(): Condition<DeviceStateDump> =
ConditionList(
listOf(isStatusBarWindowVisible(), isStatusBarLayerVisible(), isStatusBarLayerOpaque())
)
/** Condition to check if the [ComponentNameMatcher.STATUS_BAR] window is visible */
@JsName("isStatusBarWindowVisible")
fun isStatusBarWindowVisible(): Condition<DeviceStateDump> =
Condition("isStatusBarWindowVisible") {
it.wmState.isWindowSurfaceShown(ComponentNameMatcher.STATUS_BAR)
}
/** Condition to check if the [ComponentNameMatcher.STATUS_BAR] layer is visible */
@JsName("isStatusBarLayerVisible")
fun isStatusBarLayerVisible(): Condition<DeviceStateDump> =
isLayerVisible(ComponentNameMatcher.STATUS_BAR)
/** Condition to check if the [ComponentNameMatcher.STATUS_BAR] layer is opaque */
@JsName("isStatusBarLayerOpaque")
fun isStatusBarLayerOpaque(): Condition<DeviceStateDump> =
Condition("isStatusBarLayerOpaque") {
it.layerState.getLayerWithBuffer(ComponentNameMatcher.STATUS_BAR)?.color?.isOpaque
?: false
}
@JsName("isHomeActivityVisible")
fun isHomeActivityVisible(): Condition<DeviceStateDump> =
Condition("isHomeActivityVisible") { it.wmState.isHomeActivityVisible }
@JsName("isRecentsActivityVisible")
fun isRecentsActivityVisible(): Condition<DeviceStateDump> =
Condition("isRecentsActivityVisible") {
it.wmState.isHomeActivityVisible || it.wmState.isRecentsActivityVisible
}
@JsName("isLauncherLayerVisible")
fun isLauncherLayerVisible(): Condition<DeviceStateDump> =
Condition("isLauncherLayerVisible") {
it.layerState.isVisible(ComponentNameMatcher.LAUNCHER) ||
it.layerState.isVisible(ComponentNameMatcher.AOSP_LAUNCHER)
}
/**
* Condition to check if WM app transition is idle
*
* Because in shell transitions, active recents animation is running transition (never idle)
* this method always assumed recents are idle
*/
@JsName("isAppTransitionIdle")
fun isAppTransitionIdle(displayId: Int): Condition<DeviceStateDump> =
Condition("isAppTransitionIdle[$displayId]") {
(it.wmState.isHomeRecentsComponent && it.wmState.isHomeActivityVisible) ||
it.wmState.isRecentsActivityVisible ||
it.wmState.getDisplay(displayId)?.appTransitionState ==
WindowManagerState.APP_STATE_IDLE
}
@JsName("containsActivity")
fun containsActivity(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> =
Condition("containsActivity[${componentMatcher.toActivityIdentifier()}]") {
it.wmState.containsActivity(componentMatcher)
}
@JsName("containsWindow")
fun containsWindow(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> =
Condition("containsWindow[${componentMatcher.toWindowIdentifier()}]") {
it.wmState.containsWindow(componentMatcher)
}
@JsName("isWindowSurfaceShown")
fun isWindowSurfaceShown(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> =
Condition("isWindowSurfaceShown[${componentMatcher.toWindowIdentifier()}]") {
it.wmState.isWindowSurfaceShown(componentMatcher)
}
@JsName("isActivityVisible")
fun isActivityVisible(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> =
Condition("isActivityVisible[${componentMatcher.toActivityIdentifier()}]") {
it.wmState.isActivityVisible(componentMatcher)
}
@JsName("isWMStateComplete")
fun isWMStateComplete(): Condition<DeviceStateDump> =
Condition("isWMStateComplete") { it.wmState.isComplete() }
@JsName("hasRotation")
fun hasRotation(
expectedRotation: PlatformConsts.Rotation,
displayId: Int
): Condition<DeviceStateDump> {
val hasRotationCondition =
Condition<DeviceStateDump>("hasRotation[$expectedRotation, display=$displayId]") {
val currRotation = it.wmState.getRotation(displayId)
currRotation == expectedRotation
}
return ConditionList(
listOf(
hasRotationCondition,
isLayerVisible(ComponentNameMatcher.ROTATION).negate(),
isLayerVisible(ComponentNameMatcher.BACK_SURFACE).negate(),
hasLayersAnimating().negate()
)
)
}
@JsName("isWindowVisible")
fun isWindowVisible(
componentMatcher: IComponentMatcher,
displayId: Int = 0
): Condition<DeviceStateDump> =
ConditionList(
containsActivity(componentMatcher),
containsWindow(componentMatcher),
isActivityVisible(componentMatcher),
isWindowSurfaceShown(componentMatcher),
isAppTransitionIdle(displayId)
)
@JsName("isLayerVisible")
fun isLayerVisible(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> =
Condition("isLayerVisible[${componentMatcher.toLayerIdentifier()}]") {
it.layerState.isVisible(componentMatcher)
}
@JsName("isLayerVisibleForLayerId")
fun isLayerVisible(layerId: Int): Condition<DeviceStateDump> =
Condition("isLayerVisible[layerId=$layerId]") {
it.layerState.getLayerById(layerId)?.isVisible ?: false
}
@JsName("isLayerColorAlphaOne")
fun isLayerColorAlphaOne(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> =
Condition("isLayerColorAlphaOne[${componentMatcher.toLayerIdentifier()}]") {
it.layerState.visibleLayers
.filter { layer -> componentMatcher.layerMatchesAnyOf(layer) }
.any { layer -> layer.color.isOpaque }
}
@JsName("isLayerColorAlphaOneForLayerId")
fun isLayerColorAlphaOne(layerId: Int): Condition<DeviceStateDump> =
Condition("isLayerColorAlphaOne[$layerId]") {
val layer = it.layerState.getLayerById(layerId)
layer?.color?.a == 1.0f
}
@JsName("isLayerTransformFlagSet")
fun isLayerTransformFlagSet(
componentMatcher: IComponentMatcher,
transform: Int
): Condition<DeviceStateDump> =
Condition(
"isLayerTransformFlagSet[" +
"${componentMatcher.toLayerIdentifier()}," +
"transform=$transform]"
) {
it.layerState.visibleLayers
.filter { layer -> componentMatcher.layerMatchesAnyOf(layer) }
.any { layer -> isTransformFlagSet(layer, transform) }
}
@JsName("isLayerTransformFlagSetForLayerId")
fun isLayerTransformFlagSet(layerId: Int, transform: Int): Condition<DeviceStateDump> =
Condition("isLayerTransformFlagSet[$layerId, $transform]") {
val layer = it.layerState.getLayerById(layerId)
layer?.transform?.type?.isFlagSet(transform) ?: false
}
@JsName("isLayerTransformIdentity")
fun isLayerTransformIdentity(layerId: Int): Condition<DeviceStateDump> =
ConditionList(
listOf(
isLayerTransformFlagSet(layerId, Transform.SCALE_VAL).negate(),
isLayerTransformFlagSet(layerId, Transform.TRANSLATE_VAL).negate(),
isLayerTransformFlagSet(layerId, Transform.ROTATE_VAL).negate()
)
)
@JsName("isTransformFlagSet")
private fun isTransformFlagSet(layer: Layer, transform: Int): Boolean =
layer.transform.type?.isFlagSet(transform) ?: false
@JsName("hasLayersAnimating")
fun hasLayersAnimating(): Condition<DeviceStateDump> {
var prevState: DeviceStateDump? = null
return ConditionList(
Condition("hasLayersAnimating") {
val result = it.layerState.isAnimating(prevState?.layerState)
prevState = it
result
},
isLayerVisible(ComponentNameMatcher.SNAPSHOT).negate(),
isLayerVisible(ComponentNameMatcher.SPLASH_SCREEN).negate()
)
}
@JsName("isPipWindowLayerSizeMatch")
fun isPipWindowLayerSizeMatch(layerId: Int): Condition<DeviceStateDump> =
Condition("isPipWindowLayerSizeMatch[layerId=$layerId]") {
val pipWindow =
it.wmState.pinnedWindows.firstOrNull { pinnedWindow ->
pinnedWindow.layerId == layerId
}
?: error("Unable to find window with layerId $layerId")
val windowHeight = pipWindow.frame.height.toFloat()
val windowWidth = pipWindow.frame.width.toFloat()
val pipLayer = it.layerState.getLayerById(layerId)
val layerHeight =
pipLayer?.sourceBounds?.height ?: error("Unable to find layer with id $layerId")
val layerWidth = pipLayer.sourceBounds.width
windowHeight == layerHeight && windowWidth == layerWidth
}
@JsName("hasPipWindow")
fun hasPipWindow(): Condition<DeviceStateDump> =
Condition("hasPipWindow") { it.wmState.hasPipWindow() }
@JsName("isImeShown")
fun isImeShown(displayId: Int): Condition<DeviceStateDump> =
ConditionList(
listOf(
isImeOnDisplay(displayId),
isLayerVisible(ComponentNameMatcher.IME),
isImeSurfaceShown(),
isWindowSurfaceShown(ComponentNameMatcher.IME)
)
)
@JsName("isImeOnDisplay")
private fun isImeOnDisplay(displayId: Int): Condition<DeviceStateDump> =
Condition("isImeOnDisplay[$displayId]") {
it.wmState.inputMethodWindowState?.displayId == displayId
}
@JsName("isImeSurfaceShown")
private fun isImeSurfaceShown(): Condition<DeviceStateDump> =
Condition("isImeSurfaceShown") { it.wmState.inputMethodWindowState?.isSurfaceShown == true }
@JsName("isAppLaunchEnded")
fun isAppLaunchEnded(taskId: Int): Condition<DeviceStateDump> =
Condition("containsVisibleAppLaunchWindow[taskId=$taskId]") { dump ->
val windowStates =
dump.wmState.getRootTask(taskId)?.activities?.flatMap {
it.children.filterIsInstance<WindowState>()
}
windowStates != null &&
windowStates.none {
it.attributes.type == PlatformConsts.TYPE_APPLICATION_STARTING && it.isVisible
}
}
}