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 }