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 }