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