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 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 registerDumpablenull50 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 * Unregisters a previously-registered dumpable. 60 */ 61 @Synchronized unregisterDumpablenull62 fun unregisterDumpable(name: String) { 63 dumpables.remove(name) 64 } 65 66 /** 67 * Register a [LogBuffer] to be dumped during a bug report. 68 */ 69 @Synchronized registerBuffernull70 fun registerBuffer(name: String, buffer: LogBuffer) { 71 if (!canAssignToNameLocked(name, buffer)) { 72 throw IllegalArgumentException("'$name' is already registered") 73 } 74 buffers[name] = RegisteredDumpable(name, buffer) 75 } 76 77 /** 78 * Dumps the first dumpable or buffer whose registered name ends with [target] 79 */ 80 @Synchronized dumpTargetnull81 fun dumpTarget( 82 target: String, 83 fd: FileDescriptor, 84 pw: PrintWriter, 85 args: Array<String>, 86 tailLength: Int 87 ) { 88 for (dumpable in dumpables.values) { 89 if (dumpable.name.endsWith(target)) { 90 dumpDumpable(dumpable, fd, pw, args) 91 return 92 } 93 } 94 95 for (buffer in buffers.values) { 96 if (buffer.name.endsWith(target)) { 97 dumpBuffer(buffer, pw, tailLength) 98 return 99 } 100 } 101 } 102 103 /** 104 * Dumps all registered dumpables to [pw] 105 */ 106 @Synchronized dumpDumpablesnull107 fun dumpDumpables(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { 108 for (module in dumpables.values) { 109 dumpDumpable(module, fd, pw, args) 110 } 111 } 112 113 /** 114 * Dumps the names of all registered dumpables (one per line) 115 */ 116 @Synchronized listDumpablesnull117 fun listDumpables(pw: PrintWriter) { 118 for (module in dumpables.values) { 119 pw.println(module.name) 120 } 121 } 122 123 /** 124 * Dumps all registered [LogBuffer]s to [pw] 125 */ 126 @Synchronized dumpBuffersnull127 fun dumpBuffers(pw: PrintWriter, tailLength: Int) { 128 for (buffer in buffers.values) { 129 dumpBuffer(buffer, pw, tailLength) 130 } 131 } 132 133 /** 134 * Dumps the names of all registered buffers (one per line) 135 */ 136 @Synchronized listBuffersnull137 fun listBuffers(pw: PrintWriter) { 138 for (buffer in buffers.values) { 139 pw.println(buffer.name) 140 } 141 } 142 143 @Synchronized freezeBuffersnull144 fun freezeBuffers() { 145 for (buffer in buffers.values) { 146 buffer.dumpable.freeze() 147 } 148 } 149 150 @Synchronized unfreezeBuffersnull151 fun unfreezeBuffers() { 152 for (buffer in buffers.values) { 153 buffer.dumpable.unfreeze() 154 } 155 } 156 dumpDumpablenull157 private fun dumpDumpable( 158 dumpable: RegisteredDumpable<Dumpable>, 159 fd: FileDescriptor, 160 pw: PrintWriter, 161 args: Array<String> 162 ) { 163 pw.println() 164 pw.println("${dumpable.name}:") 165 pw.println("----------------------------------------------------------------------------") 166 dumpable.dumpable.dump(fd, pw, args) 167 } 168 dumpBuffernull169 private fun dumpBuffer( 170 buffer: RegisteredDumpable<LogBuffer>, 171 pw: PrintWriter, 172 tailLength: Int 173 ) { 174 pw.println() 175 pw.println() 176 pw.println("BUFFER ${buffer.name}:") 177 pw.println("============================================================================") 178 buffer.dumpable.dump(pw, tailLength) 179 } 180 canAssignToNameLockednull181 private fun canAssignToNameLocked(name: String, newDumpable: Any): Boolean { 182 val existingDumpable = dumpables[name]?.dumpable ?: buffers[name]?.dumpable 183 return existingDumpable == null || newDumpable == existingDumpable 184 } 185 } 186 187 private data class RegisteredDumpable<T>( 188 val name: String, 189 val dumpable: T 190 ) 191 192 private const val TAG = "DumpManager"