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