1 /* <lambda>null2 * Copyright (C) 2021 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 /** 20 * Builder for LayerTraceEntries 21 */ 22 class LayerTraceEntryBuilder( 23 timestamp: Any, 24 layers: Array<Layer>, 25 private val displays: Array<Display>, 26 private val hwcBlob: String = "", 27 private val where: String = "" 28 ) { 29 // Necessary for compatibility with JS number type 30 private val timestamp: Long = "$timestamp".toLong() 31 private var orphanLayerCallback: ((Layer) -> Boolean)? = null 32 private val orphans = mutableListOf<Layer>() 33 private val layers = setLayers(layers) 34 private var ignoreVirtualDisplay = false 35 private var ignoreLayersStackMatchNoDisplay = false 36 37 private fun setLayers(layers: Array<Layer>): Map<Int, Layer> { 38 val result = mutableMapOf<Int, Layer>() 39 layers.forEach { layer -> 40 val id = layer.id 41 if (result.containsKey(id)) { 42 throw RuntimeException("Duplicate layer id found: $id") 43 } 44 result[id] = layer 45 } 46 47 return result 48 } 49 50 fun setOrphanLayerCallback(value: ((Layer) -> Boolean)?): LayerTraceEntryBuilder = apply { 51 this.orphanLayerCallback = value 52 } 53 54 private fun notifyOrphansLayers() { 55 val callback = this.orphanLayerCallback ?: return 56 57 // Fail if we find orphan layers. 58 orphans.forEach { orphan -> 59 // Workaround for b/141326137, ignore the existence of an orphan layer 60 if (callback.invoke(orphan)) { 61 return@forEach 62 } 63 throw RuntimeException( 64 ("Failed to parse layers trace. Found orphan layer with id = ${orphan.id}" + 65 " with parentId = ${orphan.parentId}")) 66 } 67 } 68 69 /** 70 * Update the parent layers or each trace 71 * 72 * @return root layer 73 */ 74 private fun updateParents() { 75 for (layer in layers.values) { 76 val parentId = layer.parentId 77 78 val parentLayer = layers[parentId] 79 if (parentLayer == null) { 80 orphans.add(layer) 81 continue 82 } 83 parentLayer.addChild(layer) 84 layer.parent = parentLayer 85 } 86 } 87 88 /** 89 * Update the parent layers or each trace 90 * 91 * @return root layer 92 */ 93 private fun updateRelZParents() { 94 for (layer in layers.values) { 95 val parentId = layer.zOrderRelativeOfId 96 97 val parentLayer = layers[parentId] 98 if (parentLayer == null) { 99 layer.zOrderRelativeParentOf = parentId 100 continue 101 } 102 layer.zOrderRelativeOf = parentLayer 103 } 104 } 105 106 private fun computeRootLayers(): List<Layer> { 107 updateParents() 108 updateRelZParents() 109 110 // Find all root layers (any sibling of the root layer is considered a root layer in the trace) 111 val rootLayers = mutableListOf<Layer>() 112 113 // Getting the first orphan works because when dumping the layers, the root layer comes 114 // first, and given that orphans are added in the same order as the layers are provided 115 // in the first orphan layer should be the root layer. 116 if (orphans.isNotEmpty()) { 117 val firstRoot = orphans.first() 118 orphans.remove(firstRoot) 119 rootLayers.add(firstRoot) 120 121 val remainingRoots = orphans.filter { it.parentId == firstRoot.parentId } 122 rootLayers.addAll(remainingRoots) 123 124 // Remove RootLayers from orphans 125 orphans.removeAll(rootLayers) 126 } 127 128 return rootLayers 129 } 130 131 private fun filterOutLayersInVirtualDisplays(roots: List<Layer>): List<Layer> { 132 val physicalDisplays = displays 133 .filterNot { it.isVirtual } 134 .map { it.layerStackId } 135 136 return roots.filter { physicalDisplays.contains(it.stackId) } 137 } 138 139 private fun filterOutVirtualDisplays(displays: List<Display>): List<Display> { 140 return displays.filterNot { it.isVirtual } 141 } 142 143 private fun filterOutOffDisplays(displays: List<Display>): List<Display> { 144 return displays.filterNot { it.isOff } 145 } 146 147 private fun filterOutLayersStackMatchNoDisplay(roots: List<Layer>): List<Layer> { 148 val displayStacks = displays.map { it.layerStackId } 149 return roots.filter { displayStacks.contains(it.stackId) } 150 } 151 152 /** 153 * Defines if virtual displays and the layers belonging to virtual displays (e.g., Screen Recording) should be 154 * ignored while parsing the entry 155 * 156 * @param ignore If the layers from virtual displays should be ignored or not 157 */ 158 fun ignoreVirtualDisplay(ignore: Boolean): LayerTraceEntryBuilder = apply { 159 this.ignoreVirtualDisplay = ignore 160 } 161 162 /** 163 * Ignore layers whose stack ID doesn't match any display. This is the case, for example, 164 * when the device screen is off, or for layers that have not yet been removed after a 165 * display change (e.g., virtual screen recording display removed) 166 * 167 * @param ignore If the layers not matching any stack id should be removed or not 168 */ 169 fun ignoreLayersStackMatchNoDisplay(ignore: Boolean): LayerTraceEntryBuilder = apply { 170 this.ignoreLayersStackMatchNoDisplay = ignore 171 } 172 173 /** Constructs the layer hierarchy from a flattened list of layers. */ 174 fun build(): LayerTraceEntry { 175 val allRoots = computeRootLayers() 176 var filteredRoots = allRoots 177 var filteredDisplays = displays.toList() 178 179 if (ignoreLayersStackMatchNoDisplay) { 180 filteredRoots = filterOutLayersStackMatchNoDisplay(filteredRoots) 181 } 182 183 if (ignoreVirtualDisplay) { 184 filteredRoots = filterOutLayersInVirtualDisplays(filteredRoots) 185 filteredDisplays = filterOutVirtualDisplays(filteredDisplays) 186 } 187 188 filteredDisplays = filterOutOffDisplays(filteredDisplays) 189 190 // Fail if we find orphan layers. 191 notifyOrphansLayers() 192 193 return LayerTraceEntry(timestamp, hwcBlob, where, filteredDisplays.toTypedArray(), 194 filteredRoots.toTypedArray()) 195 } 196 }