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.surfaceflinger 18 19 import android.graphics.Rect 20 import android.graphics.RectF 21 import android.tools.Timestamps 22 import android.tools.TraceEntry 23 import android.tools.traces.component.ComponentNameMatcher 24 import android.tools.traces.component.IComponentMatcher 25 import androidx.core.graphics.toRectF 26 27 /** 28 * Represents a single Layer trace entry. 29 * 30 * This is a generic object that is reused by both Flicker and Winscope and cannot access internal 31 * Java/Android functionality 32 */ 33 class LayerTraceEntry( 34 val elapsedTimestamp: Long, 35 val clockTimestamp: Long?, 36 val hwcBlob: String, 37 val where: String, 38 val displays: Collection<Display>, 39 val vSyncId: Long, 40 _rootLayers: Collection<Layer> 41 ) : TraceEntry { 42 override val timestamp = 43 Timestamps.from(systemUptimeNanos = elapsedTimestamp, unixNanos = clockTimestamp) 44 45 val stableId: String = this::class.simpleName ?: error("Unable to determine class") 46 47 val flattenedLayers: Collection<Layer> = fillFlattenedLayers(_rootLayers) 48 49 // for winscope 50 val isVisible: Boolean = true 51 52 val visibleLayers: Collection<Layer> 53 get() = flattenedLayers.filter { it.isVisible } 54 55 val children: Collection<Layer> 56 get() = flattenedLayers.filter { it.isRootLayer } 57 58 val physicalDisplay: Display? 59 get() = displays.firstOrNull { !it.isVirtual && it.isOn } 60 61 val physicalDisplayBounds: Rect? 62 get() = physicalDisplay?.layerStackSpace 63 64 /** 65 * @param componentMatcher Components to search 66 * @return A [Layer] matching [componentMatcher] with a non-empty active buffer, or null if no 67 * layer matches [componentMatcher] or if the matching layer's buffer is empty 68 */ 69 fun getLayerWithBuffer(componentMatcher: IComponentMatcher): Layer? { 70 return flattenedLayers.firstOrNull { 71 componentMatcher.layerMatchesAnyOf(it) && !it.activeBuffer.isEmpty 72 } 73 } 74 75 /** @return The [Layer] with [layerId], or null if the layer is not found */ 76 fun getLayerById(layerId: Int): Layer? = this.flattenedLayers.firstOrNull { it.id == layerId } 77 78 /** 79 * Checks if any layer matching [componentMatcher] in the screen is animating. 80 * 81 * The screen is animating when a layer is not simple rotation, of when the pip overlay layer is 82 * visible 83 * 84 * @param componentMatcher Components to search 85 */ 86 fun isAnimating( 87 prevState: LayerTraceEntry?, 88 componentMatcher: IComponentMatcher? = null 89 ): Boolean { 90 val curLayers = 91 visibleLayers.filter { 92 componentMatcher == null || componentMatcher.layerMatchesAnyOf(it) 93 } 94 val currIds = visibleLayers.map { it.id } 95 val prevStateLayers = 96 prevState?.visibleLayers?.filter { currIds.contains(it.id) } ?: emptyList() 97 val layersAnimating = 98 curLayers.any { currLayer -> 99 val prevLayer = prevStateLayers.firstOrNull { it.id == currLayer.id } 100 currLayer.isAnimating(prevLayer) 101 } 102 val pipAnimating = isVisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY) 103 return layersAnimating || pipAnimating 104 } 105 106 /** 107 * Check if at least one window matching [componentMatcher] is visible. 108 * 109 * @param componentMatcher Components to search 110 */ 111 fun isVisible(componentMatcher: IComponentMatcher): Boolean = 112 componentMatcher.layerMatchesAnyOf(visibleLayers) 113 114 /** @return A [LayersTrace] object containing this state as its only entry */ 115 fun asTrace(): LayersTrace = LayersTrace(listOf(this)) 116 117 override fun toString(): String = timestamp.toString() 118 119 override fun equals(other: Any?): Boolean { 120 return other is LayerTraceEntry && other.timestamp == this.timestamp 121 } 122 123 override fun hashCode(): Int { 124 var result = timestamp.hashCode() 125 result = 31 * result + hwcBlob.hashCode() 126 result = 31 * result + where.hashCode() 127 result = 31 * result + displays.hashCode() 128 result = 31 * result + isVisible.hashCode() 129 result = 31 * result + flattenedLayers.hashCode() 130 return result 131 } 132 133 private fun fillFlattenedLayers(rootLayers: Collection<Layer>): Collection<Layer> { 134 val layers = mutableListOf<Layer>() 135 val roots = rootLayers.fillOcclusionState().toMutableList() 136 while (roots.isNotEmpty()) { 137 val layer = roots.removeAt(0) 138 layers.add(layer) 139 roots.addAll(layer.children) 140 } 141 return layers 142 } 143 144 private fun Collection<Layer>.topDownTraversal(): List<Layer> { 145 return this.sortedBy { it.z }.flatMap { it.topDownTraversal() } 146 } 147 148 private fun Layer.topDownTraversal(): List<Layer> { 149 val traverseList = mutableListOf(this) 150 151 this.children 152 .sortedBy { it.z } 153 .forEach { childLayer -> traverseList.addAll(childLayer.topDownTraversal()) } 154 155 return traverseList 156 } 157 158 private fun Collection<Layer>.fillOcclusionState(): Collection<Layer> { 159 val traversalList = topDownTraversal().reversed() 160 161 val opaqueLayers = mutableListOf<Layer>() 162 val transparentLayers = mutableListOf<Layer>() 163 164 traversalList.forEach { layer -> 165 val visible = layer.isVisible 166 val displaySize = 167 displays 168 .firstOrNull { it.layerStackId == layer.stackId } 169 ?.layerStackSpace 170 ?.toRectF() 171 ?: RectF() 172 173 if (visible) { 174 val occludedBy = 175 opaqueLayers.filter { 176 it.stackId == layer.stackId && 177 it.contains(layer, displaySize) && 178 (!it.hasRoundedCorners || (layer.cornerRadius == it.cornerRadius)) 179 } 180 layer.addOccludedBy(occludedBy) 181 val partiallyOccludedBy = 182 opaqueLayers.filter { 183 it.stackId == layer.stackId && 184 it.overlaps(layer, displaySize) && 185 it !in layer.occludedBy 186 } 187 layer.addPartiallyOccludedBy(partiallyOccludedBy) 188 val coveredBy = 189 transparentLayers.filter { 190 it.stackId == layer.stackId && it.overlaps(layer, displaySize) 191 } 192 layer.addCoveredBy(coveredBy) 193 194 if (layer.isOpaque) { 195 opaqueLayers.add(layer) 196 } else { 197 transparentLayers.add(layer) 198 } 199 } 200 } 201 202 return this 203 } 204 } 205