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