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