• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }