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