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.systemui.dump
18 
19 import com.android.systemui.Dumpable
20 import com.android.systemui.ProtoDumpable
21 import com.android.systemui.dump.DumpsysEntry.DumpableEntry
22 import com.android.systemui.dump.DumpsysEntry.LogBufferEntry
23 import com.android.systemui.dump.DumpsysEntry.TableLogBufferEntry
24 import com.android.systemui.log.LogBuffer
25 import com.android.systemui.log.table.TableLogBuffer
26 import java.util.TreeMap
27 import javax.inject.Inject
28 import javax.inject.Singleton
29 
30 /**
31  * Maintains a registry of things that should be dumped when a bug report is taken
32  *
33  * When a bug report is taken, SystemUI dumps various diagnostic information that we hope will be
34  * useful for the eventual readers of the bug report. Code that wishes to participate in this dump
35  * should register itself here.
36  *
37  * See [DumpHandler] for more information on how and when this information is dumped.
38  */
39 @Singleton
40 open class DumpManager @Inject constructor() {
41     // NOTE: Using TreeMap ensures that iteration is in a predictable & alphabetical order.
42     private val dumpables: MutableMap<String, DumpableEntry> = TreeMap()
43     private val buffers: MutableMap<String, LogBufferEntry> = TreeMap()
44     private val tableLogBuffers: MutableMap<String, TableLogBufferEntry> = TreeMap()
45 
46     /** See [registerCriticalDumpable]. */
registerCriticalDumpablenull47     fun registerCriticalDumpable(module: Dumpable) {
48         registerCriticalDumpable(module::class.java.canonicalName, module)
49     }
50 
51     /**
52      * Registers a dumpable to be called during the CRITICAL section of the bug report.
53      *
54      * The CRITICAL section gets very high priority during a dump, but also a very limited amount of
55      * time to do the dumping. So, please don't dump an excessive amount of stuff using CRITICAL.
56      *
57      * See [registerDumpable].
58      */
registerCriticalDumpablenull59     fun registerCriticalDumpable(name: String, module: Dumpable) {
60         registerDumpable(name, module, DumpPriority.CRITICAL)
61     }
62 
63     /** See [registerNormalDumpable]. */
registerNormalDumpablenull64     fun registerNormalDumpable(module: Dumpable) {
65         registerNormalDumpable(module::class.java.canonicalName, module)
66     }
67 
68     /**
69      * Registers a dumpable to be called during the NORMAL section of the bug report.
70      *
71      * The NORMAL section gets a lower priority during a dump, but also more time. This should be
72      * used by [LogBuffer] instances, [ProtoDumpable] instances, and any [Dumpable] instances that
73      * dump a lot of information.
74      */
registerNormalDumpablenull75     fun registerNormalDumpable(name: String, module: Dumpable) {
76         registerDumpable(name, module, DumpPriority.NORMAL)
77     }
78 
79     /**
80      * Register a dumpable to be called during a bug report.
81      *
82      * @param name The name to register the dumpable under. This is typically the qualified class
83      *   name of the thing being dumped (getClass().getName()), but can be anything as long as it
84      *   doesn't clash with an existing registration.
85      * @param priority the priority level of this dumpable, which affects at what point in the bug
86      *   report this gets dump. By default, the dumpable will be called during the CRITICAL section
87      *   of the bug report, so don't dump an excessive amount of stuff here.
88      *
89      * TODO(b/259973758): Replace all calls to this method with calls to [registerCriticalDumpable]
90      *   or [registerNormalDumpable] instead.
91      */
92     @Synchronized
93     @JvmOverloads
94     @Deprecated("Use registerCriticalDumpable or registerNormalDumpable instead")
registerDumpablenull95     fun registerDumpable(
96         name: String,
97         module: Dumpable,
98         priority: DumpPriority = DumpPriority.CRITICAL,
99     ) {
100         if (!canAssignToNameLocked(name, module)) {
101             throw IllegalArgumentException("'$name' is already registered")
102         }
103 
104         dumpables[name] = DumpableEntry(module, name, priority)
105     }
106 
107     /**
108      * Same as the above override, but automatically uses the canonical class name as the dumpable
109      * name.
110      */
111     @Synchronized
registerDumpablenull112     fun registerDumpable(module: Dumpable) {
113         registerDumpable(module::class.java.canonicalName, module)
114     }
115 
116     /** Unregisters a previously-registered dumpable. */
117     @Synchronized
unregisterDumpablenull118     fun unregisterDumpable(name: String) {
119         dumpables.remove(name)
120     }
121 
122     /** Register a [LogBuffer] to be dumped during a bug report. */
123     @Synchronized
registerBuffernull124     fun registerBuffer(name: String, buffer: LogBuffer) {
125         if (!canAssignToNameLocked(name, buffer)) {
126             throw IllegalArgumentException("'$name' is already registered")
127         }
128 
129         buffers[name] = LogBufferEntry(buffer, name)
130     }
131 
132     /** Register a [TableLogBuffer] to be dumped during a bugreport */
133     @Synchronized
registerTableLogBuffernull134     fun registerTableLogBuffer(name: String, buffer: TableLogBuffer) {
135         if (!canAssignToNameLocked(name, buffer)) {
136             throw IllegalArgumentException("'$name' is already registered")
137         }
138 
139         // All buffers must be priority NORMAL, not CRITICAL, because they often contain a lot of
140         // data.
141         tableLogBuffers[name] = TableLogBufferEntry(buffer, name)
142     }
143 
getDumpablesnull144     @Synchronized fun getDumpables(): Collection<DumpableEntry> = dumpables.values.toList()
145 
146     @Synchronized fun getLogBuffers(): Collection<LogBufferEntry> = buffers.values.toList()
147 
148     @Synchronized
149     fun getTableLogBuffers(): Collection<TableLogBufferEntry> = tableLogBuffers.values.toList()
150 
151     @Synchronized
152     fun freezeBuffers() {
153         for (buffer in buffers.values) {
154             buffer.buffer.freeze()
155         }
156     }
157 
158     @Synchronized
unfreezeBuffersnull159     fun unfreezeBuffers() {
160         for (buffer in buffers.values) {
161             buffer.buffer.unfreeze()
162         }
163     }
164 
canAssignToNameLockednull165     private fun canAssignToNameLocked(name: String, newDumpable: Any): Boolean {
166         val existingDumpable =
167             dumpables[name]?.dumpable ?: buffers[name]?.buffer ?: tableLogBuffers[name]?.table
168         return existingDumpable == null || newDumpable == existingDumpable
169     }
170 }
171 
172 /**
173  * The priority level for a given dumpable, which affects at what point in the bug report this gets
174  * dumped.
175  */
176 enum class DumpPriority {
177     CRITICAL,
178     NORMAL,
179 }
180