xref: /aosp_15_r20/platform_testing/libraries/flicker/utils/src/android/tools/traces/surfaceflinger/Layer.kt (revision dd0948b35e70be4c0246aabd6c72554a5eb8b22a)
1 /*
2  * 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.Color
20 import android.graphics.RectF
21 import android.graphics.Region
22 import android.tools.datatypes.ActiveBuffer
23 import android.tools.datatypes.containsWithThreshold
24 import android.tools.datatypes.crop
25 import android.tools.traces.component.ComponentName
26 import androidx.core.graphics.toRect
27 import com.android.internal.annotations.VisibleForTesting
28 
29 /**
30  * Represents a single layer with links to its parent and child layers.
31  *
32  * This is a generic object that is reused by both Flicker and Winscope and cannot access internal
33  * Java/Android functionality
34  */
35 class Layer
36 @VisibleForTesting
37 public constructor(
38     val name: String,
39     val id: Int,
40     val parentId: Int,
41     val z: Int,
42     val currFrame: Long,
43     properties: ILayerProperties,
44 ) : ILayerProperties by properties {
45     val stableId: String = "$id $name"
46     var parent: Layer? = null
47     var zOrderRelativeOf: Layer? = null
48     var zOrderRelativeParentOf: Int = 0
49     val packageName = ComponentName.fromLayerName(name).packageName
50 
51     /**
52      * Checks if the [Layer] is a root layer in the hierarchy
53      *
54      * @return
55      */
56     val isRootLayer: Boolean
57         get() = parent == null
58 
59     private val _children = mutableListOf<Layer>()
60     private val _occludedBy = mutableListOf<Layer>()
61     private val _partiallyOccludedBy = mutableListOf<Layer>()
62     private val _coveredBy = mutableListOf<Layer>()
63     val children: Collection<Layer>
64         get() = _children
65 
66     val occludedBy: Collection<Layer>
67         get() = _occludedBy
68 
69     val partiallyOccludedBy: Collection<Layer>
70         get() = _partiallyOccludedBy
71 
72     val coveredBy: Collection<Layer>
73         get() = _coveredBy
74 
75     var isMissing: Boolean = false
76         internal set
77 
78     /**
79      * Checks if the layer is hidden, that is, if its flags contain Flag.HIDDEN
80      *
81      * @return
82      */
83     val isHiddenByPolicy: Boolean
84         get() {
85             return (flags and Flag.HIDDEN.value) != 0x0 ||
86                 // offscreen layer root has a unique layer id
87                 id == 0x7FFFFFFD
88         }
89 
90     /**
91      * Checks if the layer is visible.
92      *
93      * A layer is visible if:
94      * - it has an active buffer or has effects
95      * - is not hidden
96      * - is not transparent
97      * - not occluded by other layers
98      *
99      * @return
100      */
101     val isVisible: Boolean
102         get() {
103             val visibleRegion =
104                 if (excludesCompositionState) {
105                     // Doesn't include state sent during composition like visible region and
106                     // composition type, so we fall back on the bounds as the visible region
107                     Region(this.bounds.toRect())
108                 } else {
109                     this.visibleRegion ?: Region()
110                 }
111             return when {
112                 isHiddenByParent -> false
113                 isHiddenByPolicy -> false
114                 hasZeroAlpha -> false
115                 isActiveBufferEmpty && !hasEffects -> false
116                 occludedBy.isNotEmpty() -> false
117                 else -> !visibleRegion.isEmpty
118             }
119         }
120 
121     /**
122      * Checks if the [Layer] is hidden by its parent
123      *
124      * @return
125      */
126     val isHiddenByParent: Boolean
127         get() =
128             !isRootLayer && (parent?.isHiddenByPolicy == true || parent?.isHiddenByParent == true)
129 
130     /**
131      * Gets a description of why the layer is (in)visible
132      *
133      * @return
134      */
135     val visibilityReason: Collection<String>
136         get() {
137             if (isVisible) {
138                 return emptyList()
139             }
140             val reasons = mutableListOf<String>()
141             if (isHiddenByPolicy) reasons.add("Flag is hidden")
142             if (isHiddenByParent) reasons.add("Hidden by parent ${parent?.name}")
143             if (isActiveBufferEmpty) reasons.add("Buffer is empty")
144             if (color.alpha() == 0.0f) reasons.add("Alpha is 0")
145             if (bounds.isEmpty) reasons.add("Bounds is 0x0")
146             if (bounds.isEmpty && crop.isEmpty) reasons.add("Crop is 0x0")
147             if (!transform.isValid) reasons.add("Transform is invalid")
148             if (isRelativeOf && zOrderRelativeOf == null) {
149                 reasons.add("RelativeOf layer has been removed")
150             }
151             if (isActiveBufferEmpty && !fillsColor && !drawsShadows && !hasBlur) {
152                 reasons.add("does not have color fill, shadow or blur")
153             }
154             if (_occludedBy.isNotEmpty()) {
<lambda>null155                 val occludedByLayers = _occludedBy.joinToString(", ") { "${it.name} (${it.id})" }
156                 reasons.add("Layer is occluded by: $occludedByLayers")
157             }
158             if (visibleRegion?.isEmpty == true) {
159                 reasons.add("Visible region calculated by Composition Engine is empty")
160             }
161             if (reasons.isEmpty()) reasons.add("Unknown")
162             return reasons
163         }
164 
165     val zOrderPath: Collection<Int>
166         get() {
167             val zOrderRelativeOf = zOrderRelativeOf
168             val zOrderPath =
169                 when {
170                     zOrderRelativeOf != null -> zOrderRelativeOf.zOrderPath.toMutableList()
171                     parent != null -> parent?.zOrderPath?.toMutableList() ?: mutableListOf()
172                     else -> mutableListOf()
173                 }
174             zOrderPath.add(z)
175             return zOrderPath
176         }
177 
178     val isTask: Boolean
179         get() = name.startsWith("Task=")
180 
181     /**
182      * Returns true iff the [innerLayer] screen bounds are inside or equal to this layer's
183      * [screenBounds] and neither layers are rotating.
184      */
containsnull185     fun contains(innerLayer: Layer, crop: RectF = RectF()): Boolean {
186         return if (!this.transform.isSimpleRotation || !innerLayer.transform.isSimpleRotation) {
187             false
188         } else {
189             val thisBounds: RectF
190             val innerLayerBounds: RectF
191             if (!crop.isEmpty) {
192                 thisBounds = this.screenBounds.crop(crop)
193                 innerLayerBounds = innerLayer.screenBounds.crop(crop)
194             } else {
195                 thisBounds = this.screenBounds
196                 innerLayerBounds = innerLayer.screenBounds
197             }
198             thisBounds.containsWithThreshold(innerLayerBounds)
199         }
200     }
201 
addChildnull202     fun addChild(childLayer: Layer) {
203         _children.add(childLayer)
204     }
205 
addOccludedBynull206     fun addOccludedBy(layers: Collection<Layer>) {
207         _occludedBy.addAll(layers)
208     }
209 
addPartiallyOccludedBynull210     fun addPartiallyOccludedBy(layers: Collection<Layer>) {
211         _partiallyOccludedBy.addAll(layers)
212     }
213 
addCoveredBynull214     fun addCoveredBy(layers: Collection<Layer>) {
215         _coveredBy.addAll(layers)
216     }
217 
overlapsnull218     fun overlaps(other: Layer, crop: RectF = RectF()): Boolean {
219         val thisBounds: RectF
220         val otherBounds: RectF
221         if (!crop.isEmpty) {
222             thisBounds = this.screenBounds.crop(crop)
223             otherBounds = other.screenBounds.crop(crop)
224         } else {
225             thisBounds = this.screenBounds
226             otherBounds = other.screenBounds
227         }
228         return thisBounds.intersect(otherBounds)
229     }
230 
toStringnull231     override fun toString(): String {
232         return buildString {
233             append(name)
234 
235             if (!activeBuffer.isEmpty) {
236                 append(" buffer:$activeBuffer")
237                 append(" frame#$currFrame")
238             }
239 
240             if (isVisible) {
241                 append(" visible:$visibleRegion")
242             }
243         }
244     }
245 
equalsnull246     override fun equals(other: Any?): Boolean {
247         if (this === other) return true
248         if (other !is Layer) return false
249 
250         if (name != other.name) return false
251         if (id != other.id) return false
252         if (parentId != other.parentId) return false
253         if (z != other.z) return false
254         if (currFrame != other.currFrame) return false
255         if (stableId != other.stableId) return false
256         if (zOrderRelativeOf != other.zOrderRelativeOf) return false
257         if (zOrderRelativeParentOf != other.zOrderRelativeParentOf) return false
258         if (_occludedBy != other._occludedBy) return false
259         if (_partiallyOccludedBy != other._partiallyOccludedBy) return false
260         if (_coveredBy != other._coveredBy) return false
261         if (isMissing != other.isMissing) return false
262         if (visibleRegion != other.visibleRegion) return false
263         if (activeBuffer != other.activeBuffer) return false
264         if (flags != other.flags) return false
265         if (bounds != other.bounds) return false
266         if (color != other.color) return false
267         if (shadowRadius != other.shadowRadius) return false
268         if (cornerRadius != other.cornerRadius) return false
269         if (transform != other.transform) return false
270         if (effectiveScalingMode != other.effectiveScalingMode) return false
271         if (bufferTransform != other.bufferTransform) return false
272         if (hwcCompositionType != other.hwcCompositionType) return false
273         if (backgroundBlurRadius != other.backgroundBlurRadius) return false
274         if (crop != other.crop) return false
275         if (isRelativeOf != other.isRelativeOf) return false
276         if (zOrderRelativeOfId != other.zOrderRelativeOfId) return false
277         if (stackId != other.stackId) return false
278         if (screenBounds != other.screenBounds) return false
279         if (isOpaque != other.isOpaque) return false
280         if (excludesCompositionState != other.excludesCompositionState) return false
281 
282         return true
283     }
284 
hashCodenull285     override fun hashCode(): Int {
286         var result = visibleRegion?.hashCode() ?: 0
287         result = 31 * result + activeBuffer.hashCode()
288         result = 31 * result + flags
289         result = 31 * result + bounds.hashCode()
290         result = 31 * result + color.hashCode()
291         result = 31 * result + shadowRadius.hashCode()
292         result = 31 * result + cornerRadius.hashCode()
293         result = 31 * result + transform.hashCode()
294         result = 31 * result + effectiveScalingMode
295         result = 31 * result + bufferTransform.hashCode()
296         result = 31 * result + hwcCompositionType.hashCode()
297         result = 31 * result + backgroundBlurRadius
298         result = 31 * result + crop.hashCode()
299         result = 31 * result + isRelativeOf.hashCode()
300         result = 31 * result + zOrderRelativeOfId
301         result = 31 * result + stackId
302         result = 31 * result + screenBounds.hashCode()
303         result = 31 * result + isOpaque.hashCode()
304         result = 31 * result + name.hashCode()
305         result = 31 * result + id
306         result = 31 * result + parentId
307         result = 31 * result + z
308         result = 31 * result + currFrame.hashCode()
309         result = 31 * result + stableId.hashCode()
310         result = 31 * result + (zOrderRelativeOf?.hashCode() ?: 0)
311         result = 31 * result + zOrderRelativeParentOf
312         result = 31 * result + _children.hashCode()
313         result = 31 * result + _occludedBy.hashCode()
314         result = 31 * result + _partiallyOccludedBy.hashCode()
315         result = 31 * result + _coveredBy.hashCode()
316         result = 31 * result + isMissing.hashCode()
317         result = 31 * result + excludesCompositionState.hashCode()
318         return result
319     }
320 
321     companion object {
322         @JvmStatic
fromnull323         fun from(
324             name: String,
325             id: Int,
326             parentId: Int,
327             z: Int,
328             visibleRegion: Region,
329             activeBuffer: ActiveBuffer,
330             flags: Int,
331             bounds: RectF,
332             color: Color,
333             isOpaque: Boolean,
334             shadowRadius: Float,
335             cornerRadius: Float,
336             screenBounds: RectF,
337             transform: Transform,
338             currFrame: Long,
339             effectiveScalingMode: Int,
340             bufferTransform: Transform,
341             hwcCompositionType: HwcCompositionType,
342             backgroundBlurRadius: Int,
343             crop: RectF?,
344             isRelativeOf: Boolean,
345             zOrderRelativeOfId: Int,
346             stackId: Int,
347             excludesCompositionState: Boolean,
348         ): Layer {
349             val properties =
350                 LayerProperties.from(
351                     visibleRegion,
352                     activeBuffer,
353                     flags,
354                     bounds,
355                     color,
356                     isOpaque,
357                     shadowRadius,
358                     cornerRadius,
359                     screenBounds,
360                     transform,
361                     effectiveScalingMode,
362                     bufferTransform,
363                     hwcCompositionType,
364                     backgroundBlurRadius,
365                     crop,
366                     isRelativeOf,
367                     zOrderRelativeOfId,
368                     stackId,
369                     excludesCompositionState,
370                 )
371             return Layer(name, id, parentId, z, currFrame, properties)
372         }
373     }
374 }
375