1 /* <lambda>null2 * 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 18 19 import android.tools.common.PlatformConsts 20 import android.tools.common.Rotation 21 import android.tools.common.traces.component.ComponentNameMatcher 22 import android.tools.common.traces.component.IComponentMatcher 23 import android.tools.common.traces.surfaceflinger.Layer 24 import android.tools.common.traces.surfaceflinger.Transform 25 import android.tools.common.traces.surfaceflinger.Transform.Companion.isFlagSet 26 import android.tools.common.traces.wm.WindowManagerState 27 import android.tools.common.traces.wm.WindowState 28 29 object ConditionsFactory { 30 private fun getNavBarComponent(wmState: WindowManagerState) = 31 if (wmState.isTablet) ComponentNameMatcher.TASK_BAR else ComponentNameMatcher.NAV_BAR 32 33 /** 34 * Condition to check if the [ComponentNameMatcher.NAV_BAR] or [ComponentNameMatcher.TASK_BAR] 35 * windows are visible 36 */ 37 fun isNavOrTaskBarVisible(): Condition<DeviceStateDump> = 38 ConditionList( 39 listOf( 40 isNavOrTaskBarWindowVisible(), 41 isNavOrTaskBarLayerVisible(), 42 isNavOrTaskBarLayerOpaque() 43 ) 44 ) 45 46 /** 47 * Condition to check if the [ComponentNameMatcher.NAV_BAR] or [ComponentNameMatcher.TASK_BAR] 48 * windows are visible 49 */ 50 fun isNavOrTaskBarWindowVisible(): Condition<DeviceStateDump> = 51 Condition("isNavBarOrTaskBarWindowVisible") { 52 val component = getNavBarComponent(it.wmState) 53 it.wmState.isWindowSurfaceShown(component) 54 } 55 56 /** 57 * Condition to check if the [ComponentNameMatcher.NAV_BAR] or [ComponentNameMatcher.TASK_BAR] 58 * layers are visible 59 */ 60 fun isNavOrTaskBarLayerVisible(): Condition<DeviceStateDump> = 61 Condition("isNavBarOrTaskBarLayerVisible") { 62 val component = getNavBarComponent(it.wmState) 63 it.layerState.isVisible(component) 64 } 65 66 /** Condition to check if the [ComponentNameMatcher.NAV_BAR] layer is opaque */ 67 fun isNavOrTaskBarLayerOpaque(): Condition<DeviceStateDump> = 68 Condition("isNavOrTaskBarLayerOpaque") { 69 val component = getNavBarComponent(it.wmState) 70 it.layerState.getLayerWithBuffer(component)?.color?.isOpaque ?: false 71 } 72 73 /** Condition to check if the [ComponentNameMatcher.NAV_BAR] window is visible */ 74 fun isNavBarVisible(): Condition<DeviceStateDump> = 75 ConditionList( 76 listOf(isNavBarWindowVisible(), isNavBarLayerVisible(), isNavBarLayerOpaque()) 77 ) 78 79 /** Condition to check if the [ComponentNameMatcher.NAV_BAR] window is visible */ 80 fun isNavBarWindowVisible(): Condition<DeviceStateDump> = 81 Condition("isNavBarWindowVisible") { 82 it.wmState.isWindowSurfaceShown(ComponentNameMatcher.NAV_BAR) 83 } 84 85 /** Condition to check if the [ComponentNameMatcher.NAV_BAR] layer is visible */ 86 fun isNavBarLayerVisible(): Condition<DeviceStateDump> = 87 isLayerVisible(ComponentNameMatcher.NAV_BAR) 88 89 /** Condition to check if the [ComponentNameMatcher.NAV_BAR] layer is opaque */ 90 fun isNavBarLayerOpaque(): Condition<DeviceStateDump> = 91 Condition("isNavBarLayerOpaque") { 92 it.layerState.getLayerWithBuffer(ComponentNameMatcher.NAV_BAR)?.color?.isOpaque ?: false 93 } 94 95 /** Condition to check if the [ComponentNameMatcher.TASK_BAR] window is visible */ 96 fun isTaskBarVisible(): Condition<DeviceStateDump> = 97 ConditionList( 98 listOf(isTaskBarWindowVisible(), isTaskBarLayerVisible(), isTaskBarLayerOpaque()) 99 ) 100 101 /** Condition to check if the [ComponentNameMatcher.TASK_BAR] window is visible */ 102 fun isTaskBarWindowVisible(): Condition<DeviceStateDump> = 103 Condition("isTaskBarWindowVisible") { 104 it.wmState.isWindowSurfaceShown(ComponentNameMatcher.TASK_BAR) 105 } 106 107 /** Condition to check if the [ComponentNameMatcher.TASK_BAR] layer is visible */ 108 fun isTaskBarLayerVisible(): Condition<DeviceStateDump> = 109 isLayerVisible(ComponentNameMatcher.TASK_BAR) 110 111 /** Condition to check if the [ComponentNameMatcher.TASK_BAR] layer is opaque */ 112 fun isTaskBarLayerOpaque(): Condition<DeviceStateDump> = 113 Condition("isTaskBarLayerOpaque") { 114 it.layerState.getLayerWithBuffer(ComponentNameMatcher.TASK_BAR)?.color?.isOpaque 115 ?: false 116 } 117 118 /** Condition to check if the [ComponentNameMatcher.STATUS_BAR] window is visible */ 119 fun isStatusBarVisible(): Condition<DeviceStateDump> = 120 ConditionList( 121 listOf(isStatusBarWindowVisible(), isStatusBarLayerVisible(), isStatusBarLayerOpaque()) 122 ) 123 124 /** Condition to check if the [ComponentNameMatcher.STATUS_BAR] window is visible */ 125 fun isStatusBarWindowVisible(): Condition<DeviceStateDump> = 126 Condition("isStatusBarWindowVisible") { 127 it.wmState.isWindowSurfaceShown(ComponentNameMatcher.STATUS_BAR) 128 } 129 130 /** Condition to check if the [ComponentNameMatcher.STATUS_BAR] layer is visible */ 131 fun isStatusBarLayerVisible(): Condition<DeviceStateDump> = 132 isLayerVisible(ComponentNameMatcher.STATUS_BAR) 133 134 /** Condition to check if the [ComponentNameMatcher.STATUS_BAR] layer is opaque */ 135 fun isStatusBarLayerOpaque(): Condition<DeviceStateDump> = 136 Condition("isStatusBarLayerOpaque") { 137 it.layerState.getLayerWithBuffer(ComponentNameMatcher.STATUS_BAR)?.color?.isOpaque 138 ?: false 139 } 140 141 fun isHomeActivityVisible(): Condition<DeviceStateDump> = 142 Condition("isHomeActivityVisible") { it.wmState.isHomeActivityVisible } 143 144 fun isRecentsActivityVisible(): Condition<DeviceStateDump> = 145 Condition("isRecentsActivityVisible") { 146 it.wmState.isHomeActivityVisible || it.wmState.isRecentsActivityVisible 147 } 148 149 fun isLauncherLayerVisible(): Condition<DeviceStateDump> = 150 Condition("isLauncherLayerVisible") { 151 it.layerState.isVisible(ComponentNameMatcher.LAUNCHER) || 152 it.layerState.isVisible(ComponentNameMatcher.AOSP_LAUNCHER) 153 } 154 155 /** 156 * Condition to check if WM app transition is idle 157 * 158 * Because in shell transitions, active recents animation is running transition (never idle) 159 * this method always assumed recents are idle 160 */ 161 fun isAppTransitionIdle(displayId: Int): Condition<DeviceStateDump> = 162 Condition("isAppTransitionIdle[$displayId]") { 163 (it.wmState.isHomeRecentsComponent && it.wmState.isHomeActivityVisible) || 164 it.wmState.isRecentsActivityVisible || 165 it.wmState.getDisplay(displayId)?.appTransitionState == 166 PlatformConsts.APP_STATE_IDLE 167 } 168 169 fun containsActivity(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> = 170 Condition("containsActivity[${componentMatcher.toActivityIdentifier()}]") { 171 it.wmState.containsActivity(componentMatcher) 172 } 173 174 fun containsWindow(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> = 175 Condition("containsWindow[${componentMatcher.toWindowIdentifier()}]") { 176 it.wmState.containsWindow(componentMatcher) 177 } 178 179 fun isWindowSurfaceShown(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> = 180 Condition("isWindowSurfaceShown[${componentMatcher.toWindowIdentifier()}]") { 181 it.wmState.isWindowSurfaceShown(componentMatcher) 182 } 183 184 fun isActivityVisible(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> = 185 Condition("isActivityVisible[${componentMatcher.toActivityIdentifier()}]") { 186 it.wmState.isActivityVisible(componentMatcher) 187 } 188 189 fun isWMStateComplete(): Condition<DeviceStateDump> = 190 Condition("isWMStateComplete") { it.wmState.isComplete() } 191 192 fun hasRotation(expectedRotation: Rotation, displayId: Int): Condition<DeviceStateDump> { 193 val hasRotationCondition = 194 Condition<DeviceStateDump>("hasRotation[$expectedRotation, display=$displayId]") { 195 val currRotation = it.wmState.getRotation(displayId) 196 currRotation == expectedRotation 197 } 198 return ConditionList( 199 listOf( 200 hasRotationCondition, 201 isLayerVisible(ComponentNameMatcher.ROTATION).negate(), 202 isLayerVisible(ComponentNameMatcher.BACK_SURFACE).negate(), 203 hasLayersAnimating().negate() 204 ) 205 ) 206 } 207 208 fun isWindowVisible( 209 componentMatcher: IComponentMatcher, 210 displayId: Int = 0 211 ): Condition<DeviceStateDump> = 212 ConditionList( 213 containsActivity(componentMatcher), 214 containsWindow(componentMatcher), 215 isActivityVisible(componentMatcher), 216 isWindowSurfaceShown(componentMatcher), 217 isAppTransitionIdle(displayId) 218 ) 219 220 fun isLayerVisible(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> = 221 Condition("isLayerVisible[${componentMatcher.toLayerIdentifier()}]") { 222 it.layerState.isVisible(componentMatcher) 223 } 224 225 fun isLayerVisible(layerId: Int): Condition<DeviceStateDump> = 226 Condition("isLayerVisible[layerId=$layerId]") { 227 it.layerState.getLayerById(layerId)?.isVisible ?: false 228 } 229 230 fun isLayerColorAlphaOne(componentMatcher: IComponentMatcher): Condition<DeviceStateDump> = 231 Condition("isLayerColorAlphaOne[${componentMatcher.toLayerIdentifier()}]") { 232 it.layerState.visibleLayers 233 .filter { layer -> componentMatcher.layerMatchesAnyOf(layer) } 234 .any { layer -> layer.color.isOpaque } 235 } 236 237 fun isLayerColorAlphaOne(layerId: Int): Condition<DeviceStateDump> = 238 Condition("isLayerColorAlphaOne[$layerId]") { 239 val layer = it.layerState.getLayerById(layerId) 240 layer?.color?.a == 1.0f 241 } 242 243 fun isLayerTransformFlagSet( 244 componentMatcher: IComponentMatcher, 245 transform: Int 246 ): Condition<DeviceStateDump> = 247 Condition( 248 "isLayerTransformFlagSet[" + 249 "${componentMatcher.toLayerIdentifier()}," + 250 "transform=$transform]" 251 ) { 252 it.layerState.visibleLayers 253 .filter { layer -> componentMatcher.layerMatchesAnyOf(layer) } 254 .any { layer -> isTransformFlagSet(layer, transform) } 255 } 256 257 fun isLayerTransformFlagSet(layerId: Int, transform: Int): Condition<DeviceStateDump> = 258 Condition("isLayerTransformFlagSet[$layerId, $transform]") { 259 val layer = it.layerState.getLayerById(layerId) 260 layer?.transform?.type?.isFlagSet(transform) ?: false 261 } 262 263 fun isLayerTransformIdentity(layerId: Int): Condition<DeviceStateDump> = 264 ConditionList( 265 listOf( 266 isLayerTransformFlagSet(layerId, Transform.SCALE_VAL).negate(), 267 isLayerTransformFlagSet(layerId, Transform.TRANSLATE_VAL).negate(), 268 isLayerTransformFlagSet(layerId, Transform.ROTATE_VAL).negate() 269 ) 270 ) 271 272 private fun isTransformFlagSet(layer: Layer, transform: Int): Boolean = 273 layer.transform.type?.isFlagSet(transform) ?: false 274 275 fun hasLayersAnimating(): Condition<DeviceStateDump> { 276 var prevState: DeviceStateDump? = null 277 return ConditionList( 278 Condition("hasLayersAnimating") { 279 val result = it.layerState.isAnimating(prevState?.layerState) 280 prevState = it 281 result 282 }, 283 isLayerVisible(ComponentNameMatcher.SNAPSHOT).negate(), 284 isLayerVisible(ComponentNameMatcher.SPLASH_SCREEN).negate() 285 ) 286 } 287 288 fun isPipWindowLayerSizeMatch(layerId: Int): Condition<DeviceStateDump> = 289 Condition("isPipWindowLayerSizeMatch[layerId=$layerId]") { 290 val pipWindow = 291 it.wmState.pinnedWindows.firstOrNull { pinnedWindow -> 292 pinnedWindow.layerId == layerId 293 } 294 ?: error("Unable to find window with layerId $layerId") 295 val windowHeight = pipWindow.frame.height.toFloat() 296 val windowWidth = pipWindow.frame.width.toFloat() 297 298 val pipLayer = it.layerState.getLayerById(layerId) 299 val layerHeight = 300 pipLayer?.sourceBounds?.height ?: error("Unable to find layer with id $layerId") 301 val layerWidth = pipLayer.sourceBounds.width 302 303 windowHeight == layerHeight && windowWidth == layerWidth 304 } 305 306 fun hasPipWindow(): Condition<DeviceStateDump> = 307 Condition("hasPipWindow") { it.wmState.hasPipWindow() } 308 309 fun isImeShown(displayId: Int): Condition<DeviceStateDump> = 310 ConditionList( 311 listOf( 312 isImeOnDisplay(displayId), 313 isLayerVisible(ComponentNameMatcher.IME), 314 isImeSurfaceShown(), 315 isWindowSurfaceShown(ComponentNameMatcher.IME) 316 ) 317 ) 318 319 private fun isImeOnDisplay(displayId: Int): Condition<DeviceStateDump> = 320 Condition("isImeOnDisplay[$displayId]") { 321 it.wmState.inputMethodWindowState?.displayId == displayId 322 } 323 324 private fun isImeSurfaceShown(): Condition<DeviceStateDump> = 325 Condition("isImeSurfaceShown") { it.wmState.inputMethodWindowState?.isSurfaceShown == true } 326 327 fun isAppLaunchEnded(taskId: Int): Condition<DeviceStateDump> = 328 Condition("containsVisibleAppLaunchWindow[taskId=$taskId]") { dump -> 329 val windowStates = 330 dump.wmState.getRootTask(taskId)?.activities?.flatMap { 331 it.children.filterIsInstance<WindowState>() 332 } 333 windowStates != null && 334 windowStates.none { 335 it.attributes.type == PlatformConsts.TYPE_APPLICATION_STARTING && it.isVisible 336 } 337 } 338 } 339