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