• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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