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