• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.common.traces.surfaceflinger
18 
19 import android.tools.common.datatypes.ActiveBuffer
20 import android.tools.common.datatypes.Color
21 import android.tools.common.datatypes.Rect
22 import android.tools.common.datatypes.RectF
23 import android.tools.common.datatypes.Region
24 import android.tools.common.traces.component.ComponentName
25 import kotlin.js.JsExport
26 import kotlin.js.JsName
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 @JsExport
35 class Layer
36 private constructor(
37     val name: String,
38     val id: Int,
39     val parentId: Int,
40     val z: Int,
41     val currFrame: Long,
42     properties: ILayerProperties,
43 ) : ILayerProperties by properties {
44     val stableId: String = "$type $id $name"
45     var parent: Layer? = null
46     var zOrderRelativeOf: Layer? = null
47     var zOrderRelativeParentOf: Int = 0
48     val packageName = ComponentName.fromLayerName(name).packageName
49 
50     /**
51      * Checks if the [Layer] is a root layer in the hierarchy
52      *
53      * @return
54      */
55     val isRootLayer: Boolean
56         get() = parent == null
57 
58     private val _children = mutableListOf<Layer>()
59     private val _occludedBy = mutableListOf<Layer>()
60     private val _partiallyOccludedBy = mutableListOf<Layer>()
61     private val _coveredBy = mutableListOf<Layer>()
62     val children: Array<Layer>
63         get() = _children.toTypedArray()
64     val occludedBy: Array<Layer>
65         get() = _occludedBy.toTypedArray()
66     val partiallyOccludedBy: Array<Layer>
67         get() = _partiallyOccludedBy.toTypedArray()
68     val coveredBy: Array<Layer>
69         get() = _coveredBy.toTypedArray()
70     var isMissing: Boolean = false
71         internal set
72 
73     /**
74      * Checks if the layer is hidden, that is, if its flags contain Flag.HIDDEN
75      *
76      * @return
77      */
78     val isHiddenByPolicy: Boolean
79         get() {
80             return (flags and Flag.HIDDEN.value) != 0x0 ||
81                 // offscreen layer root has a unique layer id
82                 id == 0x7FFFFFFD
83         }
84 
85     /**
86      * Checks if the layer is visible.
87      *
88      * A layer is visible if:
89      * - it has an active buffer or has effects
90      * - is not hidden
91      * - is not transparent
92      * - not occluded by other layers
93      *
94      * @return
95      */
96     val isVisible: Boolean
97         get() {
98             val visibleRegion =
99                 if (excludesCompositionState) {
100                     // Doesn't include state sent during composition like visible region and
101                     // composition type, so we fallback on the bounds as the visible region
102                     Region.from(this.bounds)
103                 } else {
104                     this.visibleRegion ?: Region.EMPTY
105                 }
106             return when {
107                 isHiddenByParent -> false
108                 isHiddenByPolicy -> false
109                 isActiveBufferEmpty && !hasEffects -> false
110                 occludedBy.isNotEmpty() -> false
111                 else -> visibleRegion.isNotEmpty
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: Array<String>
130         get() {
131             if (isVisible) {
132                 return emptyArray()
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.a == 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.toTypedArray()
157         }
158 
159     val zOrderPath: Array<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.toTypedArray()
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.EMPTY): 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.isNotEmpty) {
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.contains(innerLayerBounds)
193         }
194     }
195 
addChildnull196     fun addChild(childLayer: Layer) {
197         _children.add(childLayer)
198     }
199 
addOccludedBynull200     fun addOccludedBy(layers: Array<Layer>) {
201         _occludedBy.addAll(layers)
202     }
203 
addPartiallyOccludedBynull204     fun addPartiallyOccludedBy(layers: Array<Layer>) {
205         _partiallyOccludedBy.addAll(layers)
206     }
207 
addCoveredBynull208     fun addCoveredBy(layers: Array<Layer>) {
209         _coveredBy.addAll(layers)
210     }
211 
overlapsnull212     fun overlaps(other: Layer, crop: RectF = RectF.EMPTY): Boolean {
213         val thisBounds: RectF
214         val otherBounds: RectF
215         if (crop.isNotEmpty) {
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.intersection(otherBounds).isEmpty
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 (type != other.type) return false
264         if (transform != other.transform) return false
265         if (sourceBounds != other.sourceBounds) return false
266         if (effectiveScalingMode != other.effectiveScalingMode) return false
267         if (bufferTransform != other.bufferTransform) return false
268         if (hwcCompositionType != other.hwcCompositionType) return false
269         if (hwcCrop != other.hwcCrop) return false
270         if (hwcFrame != other.hwcFrame) return false
271         if (backgroundBlurRadius != other.backgroundBlurRadius) return false
272         if (crop != other.crop) return false
273         if (isRelativeOf != other.isRelativeOf) return false
274         if (zOrderRelativeOfId != other.zOrderRelativeOfId) return false
275         if (stackId != other.stackId) return false
276         if (requestedTransform != other.requestedTransform) return false
277         if (requestedColor != other.requestedColor) return false
278         if (cornerRadiusCrop != other.cornerRadiusCrop) return false
279         if (inputTransform != other.inputTransform) return false
280         if (inputRegion != other.inputRegion) return false
281         if (screenBounds != other.screenBounds) return false
282         if (isOpaque != other.isOpaque) return false
283         if (excludesCompositionState != other.excludesCompositionState) return false
284 
285         return true
286     }
287 
hashCodenull288     override fun hashCode(): Int {
289         var result = visibleRegion?.hashCode() ?: 0
290         result = 31 * result + activeBuffer.hashCode()
291         result = 31 * result + flags
292         result = 31 * result + bounds.hashCode()
293         result = 31 * result + color.hashCode()
294         result = 31 * result + shadowRadius.hashCode()
295         result = 31 * result + cornerRadius.hashCode()
296         result = 31 * result + type.hashCode()
297         result = 31 * result + transform.hashCode()
298         result = 31 * result + sourceBounds.hashCode()
299         result = 31 * result + effectiveScalingMode
300         result = 31 * result + bufferTransform.hashCode()
301         result = 31 * result + hwcCompositionType.hashCode()
302         result = 31 * result + hwcCrop.hashCode()
303         result = 31 * result + hwcFrame.hashCode()
304         result = 31 * result + backgroundBlurRadius
305         result = 31 * result + crop.hashCode()
306         result = 31 * result + isRelativeOf.hashCode()
307         result = 31 * result + zOrderRelativeOfId
308         result = 31 * result + stackId
309         result = 31 * result + requestedTransform.hashCode()
310         result = 31 * result + requestedColor.hashCode()
311         result = 31 * result + cornerRadiusCrop.hashCode()
312         result = 31 * result + inputTransform.hashCode()
313         result = 31 * result + (inputRegion?.hashCode() ?: 0)
314         result = 31 * result + screenBounds.hashCode()
315         result = 31 * result + isOpaque.hashCode()
316         result = 31 * result + name.hashCode()
317         result = 31 * result + id
318         result = 31 * result + parentId
319         result = 31 * result + z
320         result = 31 * result + currFrame.hashCode()
321         result = 31 * result + stableId.hashCode()
322         result = 31 * result + (zOrderRelativeOf?.hashCode() ?: 0)
323         result = 31 * result + zOrderRelativeParentOf
324         result = 31 * result + _children.hashCode()
325         result = 31 * result + _occludedBy.hashCode()
326         result = 31 * result + _partiallyOccludedBy.hashCode()
327         result = 31 * result + _coveredBy.hashCode()
328         result = 31 * result + isMissing.hashCode()
329         result = 31 * result + excludesCompositionState.hashCode()
330         return result
331     }
332 
333     companion object {
334         @JsName("from")
fromnull335         fun from(
336             name: String,
337             id: Int,
338             parentId: Int,
339             z: Int,
340             visibleRegion: Region,
341             activeBuffer: ActiveBuffer,
342             flags: Int,
343             bounds: RectF,
344             color: Color,
345             isOpaque: Boolean,
346             shadowRadius: Float,
347             cornerRadius: Float,
348             type: String,
349             screenBounds: RectF,
350             transform: Transform,
351             sourceBounds: RectF,
352             currFrame: Long,
353             effectiveScalingMode: Int,
354             bufferTransform: Transform,
355             hwcCompositionType: HwcCompositionType,
356             hwcCrop: RectF,
357             hwcFrame: Rect,
358             backgroundBlurRadius: Int,
359             crop: Rect?,
360             isRelativeOf: Boolean,
361             zOrderRelativeOfId: Int,
362             stackId: Int,
363             requestedTransform: Transform,
364             requestedColor: Color,
365             cornerRadiusCrop: RectF,
366             inputTransform: Transform,
367             inputRegion: Region?,
368             excludesCompositionState: Boolean
369         ): Layer {
370             val properties =
371                 LayerProperties.from(
372                     visibleRegion,
373                     activeBuffer,
374                     flags,
375                     bounds,
376                     color,
377                     isOpaque,
378                     shadowRadius,
379                     cornerRadius,
380                     type,
381                     screenBounds,
382                     transform,
383                     sourceBounds,
384                     effectiveScalingMode,
385                     bufferTransform,
386                     hwcCompositionType,
387                     hwcCrop,
388                     hwcFrame,
389                     backgroundBlurRadius,
390                     crop,
391                     isRelativeOf,
392                     zOrderRelativeOfId,
393                     stackId,
394                     requestedTransform,
395                     requestedColor,
396                     cornerRadiusCrop,
397                     inputTransform,
398                     inputRegion,
399                     excludesCompositionState
400                 )
401             return Layer(name, id, parentId, z, currFrame, properties)
402         }
403     }
404 }
405