• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ActiveBuffer
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.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: ActiveBuffer,
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     val stackId: Int
61 ) {
62     val stableId: String = "$type $id $name"
63     var parent: Layer? = null
64     var zOrderRelativeOf: Layer? = null
65     var zOrderRelativeParentOf: Int = 0
66 
67     /**
68      * Checks if the [Layer] is a root layer in the hierarchy
69      *
70      * @return
71      */
72     val isRootLayer: Boolean get() = parent == null
73 
74     private val _children = mutableListOf<Layer>()
75     private val _occludedBy = mutableListOf<Layer>()
76     private val _partiallyOccludedBy = mutableListOf<Layer>()
77     private val _coveredBy = mutableListOf<Layer>()
78     val children: Array<Layer>
79         get() = _children.toTypedArray()
80     val occludedBy: Array<Layer>
81         get() = _occludedBy.toTypedArray()
82     val partiallyOccludedBy: Array<Layer>
83         get() = _partiallyOccludedBy.toTypedArray()
84     val coveredBy: Array<Layer>
85         get() = _coveredBy.toTypedArray()
86     var isMissing: Boolean = false
87         internal set
88 
89     val isScaling: Boolean
90         get() = isTransformFlagSet(Transform.SCALE_VAL)
91     val isTranslating: Boolean
92         get() = isTransformFlagSet(Transform.TRANSLATE_VAL)
93     val isRotating: Boolean
94         get() = isTransformFlagSet(Transform.ROTATE_VAL)
95 
isTransformFlagSetnull96     private fun isTransformFlagSet(transform: Int): Boolean =
97             this.transform.type?.isFlagSet(transform) ?: false
98 
99     /**
100      * Checks if the layer's active buffer is empty
101      *
102      * An active buffer is empty if it is not in the proto or if its height or width are 0
103      *
104      * @return
105      */
106     val isActiveBufferEmpty: Boolean get() = activeBuffer.isEmpty
107 
108     /**
109      * Checks if the layer is hidden, that is, if its flags contain 0x1 (FLAG_HIDDEN)
110      *
111      * @return
112      */
113     val isHiddenByPolicy: Boolean
114         get() {
115             return (flags and /* FLAG_HIDDEN */0x1) != 0x0 ||
116                 // offscreen layer root has a unique layer id
117                 id == 0x7FFFFFFD
118         }
119 
120     /**
121      * Checks if the layer is visible.
122      *
123      * A layer is visible if:
124      * - it has an active buffer or has effects
125      * - is not hidden
126      * - is not transparent
127      * - not occluded by other layers
128      *
129      * @return
130      */
131     val isVisible: Boolean
132         get() {
133             return when {
134                 isHiddenByParent -> false
135                 isHiddenByPolicy -> false
136                 isActiveBufferEmpty && !hasEffects -> false
137                 !fillsColor -> false
138                 occludedBy.isNotEmpty() -> false
139                 visibleRegion?.isEmpty ?: false -> false
140                 else -> !bounds.isEmpty
141             }
142         }
143 
144     val isOpaque: Boolean = if (color.a != 1.0f) false else _isOpaque
145 
146     /**
147      * Checks if the [Layer] has a color
148      *
149      * @return
150      */
151     val fillsColor: Boolean get() = color.isNotEmpty
152 
153     /**
154      * Checks if the [Layer] draws a shadow
155      *
156      * @return
157      */
158     val drawsShadows: Boolean get() = shadowRadius > 0
159 
160     /**
161      * Checks if the [Layer] has blur
162      *
163      * @return
164      */
165     val hasBlur: Boolean get() = backgroundBlurRadius > 0
166 
167     /**
168      * Checks if the [Layer] has rounded corners
169      *
170      * @return
171      */
172     val hasRoundedCorners: Boolean get() = cornerRadius > 0
173 
174     /**
175      * Checks if the [Layer] draws has effects, which include:
176      * - is a color layer
177      * - is an effects layers which [fillsColor] or [drawsShadows]
178      *
179      * @return
180      */
181     val hasEffects: Boolean
182         get() {
183             // Support previous color layer
184             if (isColorLayer) {
185                 return true
186             }
187 
188             // Support newer effect layer
189             return isEffectLayer && (fillsColor || drawsShadows)
190         }
191 
192     /**
193      * Checks if the [Layer] type is BufferStateLayer or BufferQueueLayer
194      *
195      * @return
196      */
197     val isBufferLayer: Boolean
198         get() = type == "BufferStateLayer" || type == "BufferQueueLayer"
199 
200     /**
201      * Checks if the [Layer] type is ColorLayer
202      *
203      * @return
204      */
205     val isColorLayer: Boolean get() = type == "ColorLayer"
206 
207     /**
208      * Checks if the [Layer] type is ContainerLayer
209      *
210      * @return
211      */
212     val isContainerLayer: Boolean get() = type == "ContainerLayer"
213 
214     /**
215      * Checks if the [Layer] type is EffectLayer
216      *
217      * @return
218      */
219     val isEffectLayer: Boolean get() = type == "EffectLayer"
220 
221     /**
222      * Checks if the [Layer] is hidden by its parent
223      *
224      * @return
225      */
226     val isHiddenByParent: Boolean
227         get() = !isRootLayer &&
228             (parent?.isHiddenByPolicy == true || parent?.isHiddenByParent == true)
229 
230     /**
231      * Gets a description of why the layer is (in)visible
232      *
233      * @return
234      */
235     val visibilityReason: Array<String>
236         get() {
237             if (isVisible) {
238                 return emptyArray()
239             }
240             val reasons = mutableListOf<String>()
241             if (isContainerLayer) reasons.add("ContainerLayer")
242             if (isHiddenByPolicy) reasons.add("Flag is hidden")
243             if (isHiddenByParent) reasons.add("Hidden by parent ${parent?.name}")
244             if (isBufferLayer && isActiveBufferEmpty) reasons.add("Buffer is empty")
245             if (color.isEmpty) reasons.add("Alpha is 0")
246             if (crop?.isEmpty == true) reasons.add("Crop is 0x0")
247             if (bounds.isEmpty) reasons.add("Bounds is 0x0")
248             if (!transform.isValid) reasons.add("Transform is invalid")
249             if (isRelativeOf && zOrderRelativeOf == null) {
250                 reasons.add("RelativeOf layer has been removed")
251             }
252             if (isEffectLayer && !fillsColor && !drawsShadows && !hasBlur) {
253                 reasons.add("Effect layer does not have color fill, shadow or blur")
254             }
255             if (_occludedBy.isNotEmpty()) {
<lambda>null256                 val occludedByIds = _occludedBy.joinToString(", ") { it.id.toString() }
257                 reasons.add("Layer is occluded by: $occludedByIds")
258             }
259             if (visibleRegion?.isEmpty == true) {
260                 reasons.add("Visible region calculated by Composition Engine is empty")
261             }
262             if (reasons.isEmpty()) reasons.add("Unknown")
263             return reasons.toTypedArray()
264         }
265 
266     val screenBounds: RectF = when {
267         visibleRegion?.isNotEmpty == true -> visibleRegion.toRectF()
268         _screenBounds != null -> _screenBounds
269         else -> transform.apply(bounds)
270     }
271 
272     val absoluteZ: String
273         get() {
274             val zOrderRelativeOf = zOrderRelativeOf
<lambda>null275             return buildString {
276                 when {
277                     zOrderRelativeOf != null -> append(zOrderRelativeOf.absoluteZ).append(",")
278                     parent != null -> append(parent?.absoluteZ).append(",")
279                 }
280                 append(z)
281             }
282         }
283 
284     /**
285      * Returns true iff the [innerLayer] screen bounds are inside or equal to this layer's
286      * [screenBounds] and neither layers are rotating.
287      */
containsnull288     fun contains(innerLayer: Layer, crop: RectF = RectF.EMPTY): Boolean {
289         return if (!this.transform.isSimpleRotation || !innerLayer.transform.isSimpleRotation) {
290             false
291         } else {
292             val thisBounds: RectF
293             val innerLayerBounds: RectF
294             if (crop.isNotEmpty) {
295                 thisBounds = this.screenBounds.crop(crop)
296                 innerLayerBounds = innerLayer.screenBounds.crop(crop)
297             } else {
298                 thisBounds = this.screenBounds
299                 innerLayerBounds = innerLayer.screenBounds
300             }
301             thisBounds.contains(innerLayerBounds)
302         }
303     }
304 
addChildnull305     fun addChild(childLayer: Layer) {
306         _children.add(childLayer)
307     }
308 
addOccludedBynull309     fun addOccludedBy(layers: Array<Layer>) {
310         _occludedBy.addAll(layers)
311     }
312 
addPartiallyOccludedBynull313     fun addPartiallyOccludedBy(layers: Array<Layer>) {
314         _partiallyOccludedBy.addAll(layers)
315     }
316 
addCoveredBynull317     fun addCoveredBy(layers: Array<Layer>) {
318         _coveredBy.addAll(layers)
319     }
320 
overlapsnull321     fun overlaps(other: Layer, crop: RectF = RectF.EMPTY): Boolean {
322         val thisBounds: RectF
323         val otherBounds: RectF
324         if (crop.isNotEmpty) {
325             thisBounds = this.screenBounds.crop(crop)
326             otherBounds = other.screenBounds.crop(crop)
327         } else {
328             thisBounds = this.screenBounds
329             otherBounds = other.screenBounds
330         }
331         return !thisBounds.intersection(otherBounds).isEmpty
332     }
333 
toStringnull334     override fun toString(): String {
335         return buildString {
336             append(name)
337 
338             if (activeBuffer.isNotEmpty) {
339                 append(" buffer:$activeBuffer")
340                 append(" frame#$currFrame")
341             }
342 
343             if (isVisible) {
344                 append(" visible:$visibleRegion")
345             }
346         }
347     }
348 
equalsnull349     override fun equals(other: Any?): Boolean {
350         if (this === other) return true
351         if (other !is Layer) return false
352 
353         if (name != other.name) return false
354         if (id != other.id) return false
355         if (parentId != other.parentId) return false
356         if (z != other.z) return false
357         if (visibleRegion != other.visibleRegion) return false
358         if (activeBuffer != other.activeBuffer) return false
359         if (flags != other.flags) return false
360         if (bounds != other.bounds) return false
361         if (color != other.color) return false
362         if (shadowRadius != other.shadowRadius) return false
363         if (cornerRadius != other.cornerRadius) return false
364         if (type != other.type) return false
365         if (transform != other.transform) return false
366         if (sourceBounds != other.sourceBounds) return false
367         if (currFrame != other.currFrame) return false
368         if (effectiveScalingMode != other.effectiveScalingMode) return false
369         if (bufferTransform != other.bufferTransform) return false
370         if (hwcCompositionType != other.hwcCompositionType) return false
371         if (hwcCrop != other.hwcCrop) return false
372         if (hwcFrame != other.hwcFrame) return false
373         if (backgroundBlurRadius != other.backgroundBlurRadius) return false
374         if (crop != other.crop) return false
375         if (isRelativeOf != other.isRelativeOf) return false
376         if (zOrderRelativeOfId != other.zOrderRelativeOfId) return false
377         if (stableId != other.stableId) return false
378         if (parent != other.parent) return false
379         if (zOrderRelativeOf != other.zOrderRelativeOf) return false
380         if (zOrderRelativeParentOf != other.zOrderRelativeParentOf) return false
381         if (isMissing != other.isMissing) return false
382         if (isOpaque != other.isOpaque) return false
383         if (screenBounds != other.screenBounds) return false
384 
385         return true
386     }
387 
hashCodenull388     override fun hashCode(): Int {
389         var result = name.hashCode()
390         result = 31 * result + id
391         result = 31 * result + parentId
392         result = 31 * result + z
393         result = 31 * result + (visibleRegion?.hashCode() ?: 0)
394         result = 31 * result + activeBuffer.hashCode()
395         result = 31 * result + flags
396         result = 31 * result + bounds.hashCode()
397         result = 31 * result + color.hashCode()
398         result = 31 * result + shadowRadius.hashCode()
399         result = 31 * result + cornerRadius.hashCode()
400         result = 31 * result + type.hashCode()
401         result = 31 * result + transform.hashCode()
402         result = 31 * result + sourceBounds.hashCode()
403         result = 31 * result + currFrame.hashCode()
404         result = 31 * result + effectiveScalingMode
405         result = 31 * result + bufferTransform.hashCode()
406         result = 31 * result + hwcCompositionType
407         result = 31 * result + hwcCrop.hashCode()
408         result = 31 * result + hwcFrame.hashCode()
409         result = 31 * result + backgroundBlurRadius
410         result = 31 * result + (crop?.hashCode() ?: 0)
411         result = 31 * result + isRelativeOf.hashCode()
412         result = 31 * result + zOrderRelativeOfId
413         result = 31 * result + stableId.hashCode()
414         result = 31 * result + (parent?.hashCode() ?: 0)
415         result = 31 * result + (zOrderRelativeOf?.hashCode() ?: 0)
416         result = 31 * result + zOrderRelativeParentOf
417         result = 31 * result + isMissing.hashCode()
418         result = 31 * result + isOpaque.hashCode()
419         result = 31 * result + screenBounds.hashCode()
420         return result
421     }
422 }