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 android.util.ArrayMap 20 import com.android.systemui.Dumpable 21 import com.android.systemui.log.LogBuffer 22 import java.io.FileDescriptor 23 import java.io.PrintWriter 24 import javax.inject.Inject 25 import javax.inject.Singleton 26 27 /** 28 * Maintains a registry of things that should be dumped when a bug report is taken 29 * 30 * When a bug report is taken, SystemUI dumps various diagnostic information that we hope will be 31 * useful for the eventual readers of the bug report. Code that wishes to participate in this dump 32 * should register itself here. 33 * 34 * See [DumpHandler] for more information on how and when this information is dumped. 35 */ 36 @Singleton 37 open class DumpManager @Inject constructor() { 38 private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = ArrayMap() 39 private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = ArrayMap() 40 41 /** 42 * Register a dumpable to be called during a bug report. The dumpable will be called during the 43 * CRITICAL section of the bug report, so don't dump an excessive amount of stuff here. 44 * 45 * @param name The name to register the dumpable under. This is typically the qualified class 46 * name of the thing being dumped (getClass().getName()), but can be anything as long as it 47 * doesn't clash with an existing registration. 48 */ 49 @Synchronized 50 fun registerDumpable(name: String, module: Dumpable) { 51 if (!canAssignToNameLocked(name, module)) { 52 throw IllegalArgumentException("'$name' is already registered") 53 } 54 55 dumpables[name] = RegisteredDumpable(name, module) 56 } 57 58 /** 59 * Same as the above override, but automatically uses the simple class name as the dumpable 60 * name. 61 */ 62 @Synchronized 63 fun registerDumpable(module: Dumpable) { 64 registerDumpable(module::class.java.simpleName, module) 65 } 66 67 /** 68 * Unregisters a previously-registered dumpable. 69 */ 70 @Synchronized 71 fun unregisterDumpable(name: String) { 72 dumpables.remove(name) 73 } 74 75 /** 76 * Register a [LogBuffer] to be dumped during a bug report. 77 */ 78 @Synchronized 79 fun registerBuffer(name: String, buffer: LogBuffer) { 80 if (!canAssignToNameLocked(name, buffer)) { 81 throw IllegalArgumentException("'$name' is already registered") 82 } 83 buffers[name] = RegisteredDumpable(name, buffer) 84 } 85 86 /** 87 * Dumps the first dumpable or buffer whose registered name ends with [target] 88 */ 89 @Synchronized 90 fun dumpTarget( 91 target: String, 92 fd: FileDescriptor, 93 pw: PrintWriter, 94 args: Array<String>, 95 tailLength: Int 96 ) { 97 for (dumpable in dumpables.values) { 98 if (dumpable.name.endsWith(target)) { 99 dumpDumpable(dumpable, fd, pw, args) 100 return 101 } 102 } 103 104 for (buffer in buffers.values) { 105 if (buffer.name.endsWith(target)) { 106 dumpBuffer(buffer, pw, tailLength) 107 return 108 } 109 } 110 } 111 112 /** 113 * Dumps all registered dumpables to [pw] 114 */ 115 @Synchronized 116 fun dumpDumpables(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { 117 for (module in dumpables.values) { 118 dumpDumpable(module, fd, pw, args) 119 } 120 } 121 122 /** 123 * Dumps the names of all registered dumpables (one per line) 124 */ 125 @Synchronized 126 fun listDumpables(pw: PrintWriter) { 127 for (module in dumpables.values) { 128 pw.println(module.name) 129 } 130 } 131 132 /** 133 * Dumps all registered [LogBuffer]s to [pw] 134 */ 135 @Synchronized 136 fun dumpBuffers(pw: PrintWriter, tailLength: Int) { 137 for (buffer in buffers.values) { 138 dumpBuffer(buffer, pw, tailLength) 139 } 140 } 141 142 /** 143 * Dumps the names of all registered buffers (one per line) 144 */ 145 @Synchronized 146 fun listBuffers(pw: PrintWriter) { 147 for (buffer in buffers.values) { 148 pw.println(buffer.name) 149 } 150 } 151 152 @Synchronized 153 fun freezeBuffers() { 154 for (buffer in buffers.values) { 155 buffer.dumpable.freeze() 156 } 157 } 158 159 @Synchronized 160 fun unfreezeBuffers() { 161 for (buffer in buffers.values) { 162 buffer.dumpable.unfreeze() 163 } 164 } 165 166 private fun dumpDumpable( 167 dumpable: RegisteredDumpable<Dumpable>, 168 fd: FileDescriptor, 169 pw: PrintWriter, 170 args: Array<String> 171 ) { 172 pw.println() 173 pw.println("${dumpable.name}:") 174 pw.println("----------------------------------------------------------------------------") 175 dumpable.dumpable.dump(fd, pw, args) 176 } 177 178 private fun dumpBuffer( 179 buffer: RegisteredDumpable<LogBuffer>, 180 pw: PrintWriter, 181 tailLength: Int 182 ) { 183 pw.println() 184 pw.println() 185 pw.println("BUFFER ${buffer.name}:") 186 pw.println("============================================================================") 187 buffer.dumpable.dump(pw, tailLength) 188 } 189 190 private fun canAssignToNameLocked(name: String, newDumpable: Any): Boolean { 191 val existingDumpable = dumpables[name]?.dumpable ?: buffers[name]?.dumpable 192 return existingDumpable == null || newDumpable == existingDumpable 193 } 194 } 195 196 private data class RegisteredDumpable<T>( 197 val name: String, 198 val dumpable: T 199 ) 200 201 private const val TAG = "DumpManager"