1 /*
2  * Copyright (C) 2021 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 com.android.server.wm.traces.common.service.processors
18 
19 import com.android.server.wm.traces.common.DeviceStateDump
20 import com.android.server.wm.traces.common.FlickerComponentName
21 import com.android.server.wm.traces.common.RectF
22 import com.android.server.wm.traces.common.WindowManagerConditionsFactory
23 import com.android.server.wm.traces.common.layers.LayerTraceEntry
24 import com.android.server.wm.traces.common.tags.Tag
25 import com.android.server.wm.traces.common.tags.Transition
26 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
27 
28 /**
29  * Processor to detect rotations.
30  *
31  * First check the WM state for a rotation change, then wait the SF rotation
32  * to occur and both nav and status bars to appear
33  */
34 class RotationProcessor(logger: (String) -> Unit) : TransitionProcessor(logger) {
35     override val transition = Transition.ROTATION
getInitialStatenull36     override fun getInitialState(tags: MutableMap<Long, MutableList<Tag>>) = InitialState(tags)
37 
38     /**
39      * Initial FSM state, obtains the current display size and start searching
40      * for display size changes
41      */
42     inner class InitialState(
43         tags: MutableMap<Long, MutableList<Tag>>
44     ) : BaseState(tags) {
45         override fun doProcessState(
46             previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
47             current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
48             next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
49         ): FSMState {
50             val currDisplayRect = current.wmState.displaySize()
51             logger.invoke("(${current.wmState.timestamp}) Initial state. " +
52                 "Display size $currDisplayRect")
53             return WaitDisplayRectChange(tags, currDisplayRect)
54         }
55     }
56 
57     /**
58      * FSM state when the display size has not changed since [InitialState]
59      */
60     inner class WaitDisplayRectChange(
61         tags: MutableMap<Long, MutableList<Tag>>,
62         private val currDisplayRect: RectF
63     ) : BaseState(tags) {
doProcessStatenull64         override fun doProcessState(
65             previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
66             current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
67             next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
68         ): FSMState {
69             val newWmDisplayRect = current.wmState.displaySize()
70             val newLayersDisplayRect = current.layerState.screenBounds()
71 
72             return when {
73                 // WM display changed first (Regular rotation)
74                 // SF display changed first (Seamless rotation)
75                 newWmDisplayRect != currDisplayRect || newLayersDisplayRect != currDisplayRect -> {
76                     requireNotNull(previous) { "Should have a previous state" }
77                     val rect = if (newWmDisplayRect != currDisplayRect) {
78                         newWmDisplayRect
79                     } else {
80                         newLayersDisplayRect
81                     }
82                     processDisplaySizeChange(previous, rect)
83                 }
84                 else -> {
85                     logger.invoke("(${current.wmState.timestamp}) No display size change")
86                     this
87                 }
88             }
89         }
90 
processDisplaySizeChangenull91         private fun processDisplaySizeChange(
92             previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
93             newDisplayRect: RectF
94         ): FSMState {
95             logger.invoke("(${previous.wmState.timestamp}) Display size changed " +
96                 "to $newDisplayRect")
97             // tag on the last complete state at the start
98             logger.invoke("(${previous.wmState.timestamp}) Tagging transition start")
99             addStartTransitionTag(previous, transition)
100             return WaitRotationFinished(tags)
101         }
102     }
103 
104     /**
105      * FSM state for when the animation occurs in the SF trace
106      */
107     inner class WaitRotationFinished(tags: MutableMap<Long, MutableList<Tag>>) : BaseState(tags) {
108         private val rotationLayerExists = WindowManagerConditionsFactory
109             .isLayerVisible(FlickerComponentName.ROTATION)
110         private val backSurfaceLayerExists = WindowManagerConditionsFactory
111             .isLayerVisible(FlickerComponentName.BACK_SURFACE)
112         private val areLayersAnimating = WindowManagerConditionsFactory.hasLayersAnimating()
113         private val wmStateIdle = WindowManagerConditionsFactory
114             .isAppTransitionIdle(/* default display */ 0)
115         private val wmStateComplete = WindowManagerConditionsFactory.isWMStateComplete()
116 
doProcessStatenull117         override fun doProcessState(
118             previous: DeviceStateDump<WindowManagerState, LayerTraceEntry>?,
119             current: DeviceStateDump<WindowManagerState, LayerTraceEntry>,
120             next: DeviceStateDump<WindowManagerState, LayerTraceEntry>
121         ): FSMState {
122             val anyLayerAnimating = areLayersAnimating.isSatisfied(current)
123             val rotationLayerExists = rotationLayerExists.isSatisfied(current)
124             val blackSurfaceLayerExists = backSurfaceLayerExists.isSatisfied(current)
125             val wmStateIdle = wmStateIdle.isSatisfied(current)
126             val wmStateComplete = wmStateComplete.isSatisfied(current)
127 
128             val newWmDisplayRect = current.wmState.displaySize()
129             val newLayersDisplayRect = current.layerState.screenBounds()
130             val displaySizeDifferent = newWmDisplayRect != newLayersDisplayRect
131 
132             val inRotation = anyLayerAnimating || rotationLayerExists || blackSurfaceLayerExists ||
133                 displaySizeDifferent || !wmStateIdle || !wmStateComplete
134             logger.invoke("(${current.layerState.timestamp}) " +
135                 "In rotation? $inRotation (" +
136                 "anyLayerAnimating=$anyLayerAnimating, " +
137                 "blackSurfaceLayerExists=$blackSurfaceLayerExists, " +
138                 "rotationLayerExists=$rotationLayerExists, " +
139                 "wmStateIdle=$wmStateIdle, " +
140                 "wmStateComplete=$wmStateComplete, " +
141                 "displaySizeDifferent=$displaySizeDifferent)")
142             return if (inRotation) {
143                 this
144             } else {
145                 // tag on the last complete state at the start
146                 logger.invoke("(${current.layerState.timestamp}) Tagging transition end")
147                 addEndTransitionTag(current, transition)
148                 // return to start to wait for a second rotation
149                 val lastDisplayRect = current.wmState.displaySize()
150                 WaitDisplayRectChange(tags, lastDisplayRect)
151             }
152         }
153     }
154 
155     companion object {
<lambda>null156         private fun LayerTraceEntry.screenBounds() = this.displays.minByOrNull { it.id }
157             ?.layerStackSpace?.toRectF() ?: this.children
<lambda>null158             .sortedBy { it.id }
<lambda>null159             .firstOrNull { it.isRootLayer }
160             ?.screenBounds ?: error("Unable to identify screen bounds (display is empty in proto)")
161 
WindowManagerStatenull162         private fun WindowManagerState.displaySize() = getDefaultDisplay()
163             ?.displayRect?.toRectF() ?: RectF.EMPTY
164     }
165 }