1 /*
2  * Copyright (C) 2020 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.layers
18 
19 import com.android.server.wm.traces.common.Buffer
20 import com.android.server.wm.traces.common.Color
21 import com.android.server.wm.traces.common.Rect
22 import com.android.server.wm.traces.common.RectF
23 import com.android.server.wm.traces.common.Region
24 import com.android.server.wm.traces.common.layers.Transform.Companion.isFlagSet
25 
26 /**
27  * Represents a single layer with links to its parent and child layers.
28  *
29  * This is a generic object that is reused by both Flicker and Winscope and cannot
30  * access internal Java/Android functionality
31  *
32  **/
33 data class Layer(
34     val name: String,
35     val id: Int,
36     val parentId: Int,
37     val z: Int,
38     val visibleRegion: Region?,
39     val activeBuffer: Buffer,
40     val flags: Int,
41     val bounds: RectF,
42     val color: Color,
43     private val _isOpaque: Boolean,
44     val shadowRadius: Float,
45     val cornerRadius: Float,
46     val type: String,
47     private val _screenBounds: RectF?,
48     val transform: Transform,
49     val sourceBounds: RectF,
50     val currFrame: Long,
51     val effectiveScalingMode: Int,
52     val bufferTransform: Transform,
53     val hwcCompositionType: Int,
54     val hwcCrop: RectF,
55     val hwcFrame: Rect,
56     val backgroundBlurRadius: Int,
57     val crop: Rect?,
58     val isRelativeOf: Boolean,
59     val zOrderRelativeOfId: Int
60 ) {
61     val stableId: String = "$type $id $name"
62     var parent: Layer? = null
63     var zOrderRelativeOf: Layer? = null
64     var zOrderRelativeParentOf: Int = 0
65 
66     /**
67      * Checks if the [Layer] is a root layer in the hierarchy
68      *
69      * @return
70      */
71     val isRootLayer: Boolean get() = parent == null
72 
73     private val _children = mutableListOf<Layer>()
74     private val _occludedBy = mutableListOf<Layer>()
75     private val _partiallyOccludedBy = mutableListOf<Layer>()
76     private val _coveredBy = mutableListOf<Layer>()
77     val children: Array<Layer>
78         get() = _children.toTypedArray()
79     val occludedBy: Array<Layer>
80         get() = _occludedBy.toTypedArray()
81     val partiallyOccludedBy: Array<Layer>
82         get() = _partiallyOccludedBy.toTypedArray()
83     val coveredBy: Array<Layer>
84         get() = _coveredBy.toTypedArray()
85     var isMissing: Boolean = false
86         internal set
87 
88     val isScaling: Boolean
89         get() = isTransformFlagSet(Transform.SCALE_VAL)
90     val isTranslating: Boolean
91         get() = isTransformFlagSet(Transform.TRANSLATE_VAL)
92     val isRotating: Boolean
93         get() = isTransformFlagSet(Transform.ROTATE_VAL)
94 
isTransformFlagSetnull95     private fun isTransformFlagSet(transform: Int): Boolean =
96             this.transform.type?.isFlagSet(transform) ?: false
97 
98     /**
99      * Checks if the layer's active buffer is empty
100      *
101      * An active buffer is empty if it is not in the proto or if its height or width are 0
102      *
103      * @return
104      */
105     val isActiveBufferEmpty: Boolean get() = activeBuffer.isEmpty
106 
107     /**
108      * Checks if the layer is hidden, that is, if its flags contain 0x1 (FLAG_HIDDEN)
109      *
110      * @return
111      */
112     val isHiddenByPolicy: Boolean
113         get() {
114             return (flags and /* FLAG_HIDDEN */0x1) != 0x0 ||
115                 // offscreen layer root has a unique layer id
116                 id == 0x7FFFFFFD
117         }
118 
119     /**
120      * Checks if the layer is visible.
121      *
122      * A layer is visible if:
123      * - it has an active buffer or has effects
124      * - is not hidden
125      * - is not transparent
126      * - not occluded by other layers
127      *
128      * @return
129      */
130     val isVisible: Boolean
131         get() {
132             return when {
133                 isHiddenByParent -> false
134                 isHiddenByPolicy -> false
135                 isActiveBufferEmpty && !hasEffects -> false
136                 !fillsColor -> false
137                 occludedBy.isNotEmpty() -> false
138                 visibleRegion?.isEmpty ?: false -> false
139                 else -> !bounds.isEmpty
140             }
141         }
142 
143     val isOpaque: Boolean = if (color.a != 1.0f) false else _isOpaque
144 
145     /**
146      * Checks if the [Layer] has a color
147      *
148      * @return
149      */
150     val fillsColor: Boolean get() = color.isNotEmpty
151 
152     /**
153      * Checks if the [Layer] draws a shadow
154      *
155      * @return
156      */
157     val drawsShadows: Boolean get() = shadowRadius > 0
158 
159     /**
160      * Checks if the [Layer] has blur
161      *
162      * @return
163      */
164     val hasBlur: Boolean get() = backgroundBlurRadius > 0
165 
166     /**
167      * Checks if the [Layer] has rounded corners
168      *
169      * @return
170      */
171     val hasRoundedCorners: Boolean get() = cornerRadius > 0
172 
173     /**
174      * Checks if the [Layer] draws has effects, which include:
175      * - is a color layer
176      * - is an effects layers which [fillsColor] or [drawsShadows]
177      *
178      * @return
179      */
180     val hasEffects: Boolean
181         get() {
182             // Support previous color layer
183             if (isColorLayer) {
184                 return true
185             }
186 
187             // Support newer effect layer
188             return isEffectLayer && (fillsColor || drawsShadows)
189         }
190 
191     /**
192      * Checks if the [Layer] type is BufferStateLayer or BufferQueueLayer
193      *
194      * @return
195      */
196     val isBufferLayer: Boolean
197         get() = type == "BufferStateLayer" || type == "BufferQueueLayer"
198 
199     /**
200      * Checks if the [Layer] type is ColorLayer
201      *
202      * @return
203      */
204     val isColorLayer: Boolean get() = type == "ColorLayer"
205 
206     /**
207      * Checks if the [Layer] type is ContainerLayer
208      *
209      * @return
210      */
211     val isContainerLayer: Boolean get() = type == "ContainerLayer"
212 
213     /**
214      * Checks if the [Layer] type is EffectLayer
215      *
216      * @return
217      */
218     val isEffectLayer: Boolean get() = type == "EffectLayer"
219 
220     /**
221      * Checks if the [Layer] is hidden by its parent
222      *
223      * @return
224      */
225     val isHiddenByParent: Boolean
226         get() = !isRootLayer &&
227             (parent?.isHiddenByPolicy == true || parent?.isHiddenByParent == true)
228 
229     /**
230      * Gets a description of why the layer is (in)visible
231      *
232      * @return
233      */
234     val visibilityReason: String
235         get() {
236             return when {
237                 isVisible -> ""
238                 isContainerLayer -> "ContainerLayer"
239                 isHiddenByPolicy -> "Flag is hidden"
240                 isHiddenByParent -> "Hidden by parent ${parent?.name}"
241                 isBufferLayer && isActiveBufferEmpty -> "Buffer is empty"
242                 color.isEmpty -> "Alpha is 0"
243                 crop?.isEmpty ?: false -> "Crop is 0x0"
244                 bounds.isEmpty -> "Bounds is 0x0"
245                 !transform.isValid -> "Transform is invalid"
246                 isRelativeOf && zOrderRelativeOf == null -> "RelativeOf layer has been removed"
247                 isEffectLayer && !fillsColor && !drawsShadows && !hasBlur ->
248                     "Effect layer does not have color fill, shadow or blur"
249                 _occludedBy.isNotEmpty() -> {
<lambda>null250                     val occludedByIds = _occludedBy.joinToString(", ") { it.id.toString() }
251                     "Layer is occluded by: $occludedByIds"
252                 }
253                 visibleRegion?.isEmpty ?: false ->
254                     "Visible region calculated by Composition Engine is empty"
255                 else -> "Unknown"
256             }
257         }
258 
259     val screenBounds: RectF = when {
260         visibleRegion?.isNotEmpty == true -> visibleRegion.toRectF()
261         _screenBounds != null -> _screenBounds
262         else -> transform.apply(bounds)
263     }
264 
265     val absoluteZ: String
266         get() {
267             val zOrderRelativeOf = zOrderRelativeOf
<lambda>null268             return buildString {
269                 when {
270                     zOrderRelativeOf != null -> append(zOrderRelativeOf.absoluteZ).append(",")
271                     parent != null -> append(parent?.absoluteZ).append(",")
272                 }
273                 append(z)
274             }
275         }
276 
containsnull277     fun contains(innerLayer: Layer): Boolean {
278         return if (!this.transform.isSimpleRotation || !innerLayer.transform.isSimpleRotation) {
279             false
280         } else {
281             this.screenBounds.contains(innerLayer.screenBounds)
282         }
283     }
284 
addChildnull285     fun addChild(childLayer: Layer) {
286         _children.add(childLayer)
287     }
288 
addOccludedBynull289     fun addOccludedBy(layers: Array<Layer>) {
290         _occludedBy.addAll(layers)
291     }
292 
addPartiallyOccludedBynull293     fun addPartiallyOccludedBy(layers: Array<Layer>) {
294         _partiallyOccludedBy.addAll(layers)
295     }
296 
addCoveredBynull297     fun addCoveredBy(layers: Array<Layer>) {
298         _coveredBy.addAll(layers)
299     }
300 
overlapsnull301     fun overlaps(other: Layer): Boolean =
302         !this.screenBounds.intersection(other.screenBounds).isEmpty
303 
304     override fun toString(): String {
305         return buildString {
306             append(name)
307 
308             if (activeBuffer.isNotEmpty) {
309                 append(" buffer:$activeBuffer")
310                 append(" frame#$currFrame")
311             }
312 
313             if (isVisible) {
314                 append(" visible:$visibleRegion")
315             }
316         }
317     }
318 
equalsnull319     override fun equals(other: Any?): Boolean {
320         if (this === other) return true
321         if (other !is Layer) return false
322 
323         if (name != other.name) return false
324         if (id != other.id) return false
325         if (parentId != other.parentId) return false
326         if (z != other.z) return false
327         if (visibleRegion != other.visibleRegion) return false
328         if (activeBuffer != other.activeBuffer) return false
329         if (flags != other.flags) return false
330         if (bounds != other.bounds) return false
331         if (color != other.color) return false
332         if (shadowRadius != other.shadowRadius) return false
333         if (cornerRadius != other.cornerRadius) return false
334         if (type != other.type) return false
335         if (transform != other.transform) return false
336         if (sourceBounds != other.sourceBounds) return false
337         if (currFrame != other.currFrame) return false
338         if (effectiveScalingMode != other.effectiveScalingMode) return false
339         if (bufferTransform != other.bufferTransform) return false
340         if (hwcCompositionType != other.hwcCompositionType) return false
341         if (hwcCrop != other.hwcCrop) return false
342         if (hwcFrame != other.hwcFrame) return false
343         if (backgroundBlurRadius != other.backgroundBlurRadius) return false
344         if (crop != other.crop) return false
345         if (isRelativeOf != other.isRelativeOf) return false
346         if (zOrderRelativeOfId != other.zOrderRelativeOfId) return false
347         if (stableId != other.stableId) return false
348         if (parent != other.parent) return false
349         if (zOrderRelativeOf != other.zOrderRelativeOf) return false
350         if (zOrderRelativeParentOf != other.zOrderRelativeParentOf) return false
351         if (isMissing != other.isMissing) return false
352         if (isOpaque != other.isOpaque) return false
353         if (screenBounds != other.screenBounds) return false
354 
355         return true
356     }
357 
hashCodenull358     override fun hashCode(): Int {
359         var result = name.hashCode()
360         result = 31 * result + id
361         result = 31 * result + parentId
362         result = 31 * result + z
363         result = 31 * result + (visibleRegion?.hashCode() ?: 0)
364         result = 31 * result + activeBuffer.hashCode()
365         result = 31 * result + flags
366         result = 31 * result + bounds.hashCode()
367         result = 31 * result + color.hashCode()
368         result = 31 * result + shadowRadius.hashCode()
369         result = 31 * result + cornerRadius.hashCode()
370         result = 31 * result + type.hashCode()
371         result = 31 * result + transform.hashCode()
372         result = 31 * result + sourceBounds.hashCode()
373         result = 31 * result + currFrame.hashCode()
374         result = 31 * result + effectiveScalingMode
375         result = 31 * result + bufferTransform.hashCode()
376         result = 31 * result + hwcCompositionType
377         result = 31 * result + hwcCrop.hashCode()
378         result = 31 * result + hwcFrame.hashCode()
379         result = 31 * result + backgroundBlurRadius
380         result = 31 * result + (crop?.hashCode() ?: 0)
381         result = 31 * result + isRelativeOf.hashCode()
382         result = 31 * result + zOrderRelativeOfId
383         result = 31 * result + stableId.hashCode()
384         result = 31 * result + (parent?.hashCode() ?: 0)
385         result = 31 * result + (zOrderRelativeOf?.hashCode() ?: 0)
386         result = 31 * result + zOrderRelativeParentOf
387         result = 31 * result + isMissing.hashCode()
388         result = 31 * result + isOpaque.hashCode()
389         result = 31 * result + screenBounds.hashCode()
390         return result
391     }
392 }