summaryrefslogtreecommitdiff
path: root/packages/SystemUI/src
diff options
context:
space:
mode:
authorNed Burns <pixel@google.com>2020-02-12 21:38:50 -0500
committerNed Burns <pixel@google.com>2020-02-20 22:11:19 -0500
commitc7cfa69ffde55126ed3e7648d1f6af651b88a06c (patch)
treeb32a31681999a756e58b6d41bfb88f335e4b5c29 /packages/SystemUI/src
parentb909557790eceab62e08d8f180fb28bb5efbe018 (diff)
Introduce DumpManager
Introduces DumpManager, a unified dumping system that supports dumping at different priority levels. Currently, when a bug report gets taken, SystemUI is only dumped during the CRITICAL section. This has its advantages (we get to go first!) but also imposes a strict limit on how much we can dump. To get around this restriction, we need to *also* dump SystemUI during the NORMAL section, which has much more forgiving constraints. This CL simply creates the mechanism for systemUI to dump at different priority levels, but doesn't actually cause us to participate in the NORMAL section (yet, see later CLs). It introduces the DumpManager, unified replacement for DumpController & various logic in SystemUIService and Dependency.java. See kdoc in DumpManager for usage notes. Migration of current users of DumpController coming in a later CL. Test: atest, manual Change-Id: If4f41ed496c0c64024a83aad812b77f60fe27555
Diffstat (limited to 'packages/SystemUI/src')
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUI.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIService.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt367
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java14
10 files changed, 465 insertions, 135 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 69bc2596d411..362a9e8306c3 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -42,6 +42,7 @@ import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -128,8 +129,6 @@ import com.android.systemui.wm.DisplayController;
import com.android.systemui.wm.DisplayImeController;
import com.android.systemui.wm.SystemWindows;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -211,6 +210,8 @@ public class Dependency {
private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
private final ArrayMap<Object, LazyDependencyCreator> mProviders = new ArrayMap<>();
+ @Inject DumpManager mDumpManager;
+
@Inject Lazy<ActivityStarter> mActivityStarter;
@Inject Lazy<BroadcastDispatcher> mBroadcastDispatcher;
@Inject Lazy<AsyncSensorManager> mAsyncSensorManager;
@@ -534,34 +535,6 @@ public class Dependency {
sDependency = this;
}
- static void staticDump(FileDescriptor fd, PrintWriter pw, String[] args) {
- sDependency.dump(fd, pw, args);
- }
-
- /**
- * {@see SystemUI.dump}
- */
- public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- // Make sure that the DumpController gets added to mDependencies, as they are only added
- // with Dependency#get.
- getDependency(DumpController.class);
- getDependency(BroadcastDispatcher.class);
-
- // If an arg is specified, try to dump the dependency
- String controller = args != null && args.length > 1
- ? args[1].toLowerCase()
- : null;
- if (controller != null) {
- pw.println("Dumping controller=" + controller + ":");
- } else {
- pw.println("Dumping existing controllers:");
- }
- mDependencies.values().stream()
- .filter(obj -> obj instanceof Dumpable && (controller == null
- || obj.getClass().getName().toLowerCase().endsWith(controller)))
- .forEach(o -> ((Dumpable) o).dump(fd, pw, args));
- }
-
protected final <T> T getDependency(Class<T> cls) {
return getDependencyInner(cls);
}
@@ -576,6 +549,11 @@ public class Dependency {
if (obj == null) {
obj = createDependency(key);
mDependencies.put(key, obj);
+
+ // TODO: Get dependencies to register themselves instead
+ if (autoRegisterModulesForDump() && obj instanceof Dumpable) {
+ mDumpManager.registerDumpable(obj.getClass().getName(), (Dumpable) obj);
+ }
}
return obj;
}
@@ -593,6 +571,17 @@ public class Dependency {
return provider.createDependency();
}
+ // Currently, there are situations in tests where we might create more than one instance of a
+ // thing that should be a singleton: the "real" one (created by Dagger, usually as a result of
+ // inflating a view), and a mocked one (injected into Dependency). If we register the mocked
+ // one, the DumpManager will throw an exception complaining (rightly) that we have too many
+ // things registered with that name. So in tests, we disable the auto-registration until the
+ // root cause is fixed, i.e. inflated views in tests with Dagger dependencies.
+ @VisibleForTesting
+ protected boolean autoRegisterModulesForDump() {
+ return true;
+ }
+
private static Dependency sDependency;
/**
@@ -605,6 +594,9 @@ public class Dependency {
private <T> void destroyDependency(Class<T> cls, Consumer<T> destroy) {
T dep = (T) mDependencies.remove(cls);
+ if (dep instanceof Dumpable) {
+ mDumpManager.unregisterDumpable(dep.getClass().getName());
+ }
if (dep != null && destroy != null) {
destroy.accept(dep);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index f795faf30603..e880cc8fd10a 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -21,10 +21,18 @@ import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
+import androidx.annotation.NonNull;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
-public abstract class SystemUI {
+/**
+ * A top-level module of system UI code (sometimes called "system UI services" elsewhere in code).
+ * Which SystemUI modules are loaded can be controlled via a config resource.
+ *
+ * @see SystemUIApplication#startServicesIfNeeded()
+ */
+public abstract class SystemUI implements Dumpable {
protected final Context mContext;
public SystemUI(Context context) {
@@ -36,7 +44,8 @@ public abstract class SystemUI {
protected void onConfigurationChanged(Configuration newConfig) {
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
}
protected void onBootCompleted() {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 13151527cd5f..cbdae4e6fe63 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -33,6 +33,7 @@ import android.util.TimingsTraceLog;
import com.android.systemui.dagger.ContextComponentHelper;
import com.android.systemui.dagger.SystemUIRootComponent;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.NotificationChannels;
import java.lang.reflect.Constructor;
@@ -171,6 +172,8 @@ public class SystemUIApplication extends Application implements
}
}
+ final DumpManager dumpManager = mRootComponent.createDumpManager();
+
Log.v(TAG, "Starting SystemUI services for user " +
Process.myUserHandle().getIdentifier() + ".");
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
@@ -209,6 +212,8 @@ public class SystemUIApplication extends Application implements
if (mBootCompleteCache.isBootComplete()) {
mServices[i].onBootCompleted();
}
+
+ dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
}
mRootComponent.getInitController().executePostInitTasks();
log.traceEnd();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java b/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java
index 2d2d91db4fe1..f4ec6f75b06b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java
@@ -20,9 +20,6 @@ import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
public class SystemUISecondaryUserService extends Service {
@Override
@@ -35,11 +32,4 @@ public class SystemUISecondaryUserService extends Service {
public IBinder onBind(Intent intent) {
return null;
}
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- SystemUIService.dumpServices(
- ((SystemUIApplication) getApplication()).getServices(), fd, pw, args);
- }
}
-
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 41d83148e093..e65fccd9f132 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -16,7 +16,6 @@
package com.android.systemui;
-import android.annotation.NonNull;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
@@ -28,8 +27,7 @@ import android.util.Slog;
import com.android.internal.os.BinderInternal;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.dump.DumpManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -39,11 +37,15 @@ import javax.inject.Inject;
public class SystemUIService extends Service {
private final Handler mMainHandler;
+ private final DumpManager mDumpManager;
@Inject
- public SystemUIService(@Main Handler mainHandler) {
+ public SystemUIService(
+ @Main Handler mainHandler,
+ DumpManager dumpManager) {
super();
mMainHandler = mainHandler;
+ mDumpManager = dumpManager;
}
@Override
@@ -79,62 +81,16 @@ public class SystemUIService extends Service {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args);
-
- if (args == null || args.length == 0 || args[0].equals("--config")) {
- dumpConfig(pw);
- }
- }
-
- static void dumpServices(
- SystemUI[] services, FileDescriptor fd, PrintWriter pw, String[] args) {
- if (args == null || args.length == 0) {
- pw.println("dumping service: " + Dependency.class.getName());
- Dependency.staticDump(fd, pw, args);
- for (SystemUI ui: services) {
- pw.println("dumping service: " + ui.getClass().getName());
- ui.dump(fd, pw, args);
- }
- if (Build.IS_DEBUGGABLE) {
- pw.println("dumping plugins:");
- ((PluginManagerImpl) Dependency.get(PluginManager.class)).dump(fd, pw, args);
- }
- } else {
- String svc = args[0].toLowerCase();
- if (Dependency.class.getName().toLowerCase().endsWith(svc)) {
- Dependency.staticDump(fd, pw, args);
- }
- for (SystemUI ui: services) {
- String name = ui.getClass().getName().toLowerCase();
- if (name.endsWith(svc)) {
- ui.dump(fd, pw, args);
- }
- }
+ // If no args are passed, assume we're being dumped as part of a bug report (sadly, we have
+ // no better way to guess whether this is taking place). Set the appropriate dump priority
+ // (CRITICAL) to reflect that this is taking place.
+ String[] massagedArgs = args;
+ if (args.length == 0) {
+ massagedArgs = new String[] {
+ DumpManager.PRIORITY_ARG,
+ DumpManager.PRIORITY_ARG_CRITICAL};
}
- }
-
- private void dumpConfig(@NonNull PrintWriter pw) {
- pw.println("SystemUiServiceComponents configuration:");
- pw.print("vendor component: ");
- pw.println(getResources().getString(R.string.config_systemUIVendorServiceComponent));
-
- dumpConfig(pw, "global", R.array.config_systemUIServiceComponents);
- dumpConfig(pw, "per-user", R.array.config_systemUIServiceComponentsPerUser);
- }
-
- private void dumpConfig(@NonNull PrintWriter pw, @NonNull String type, int resId) {
- final String[] services = getResources().getStringArray(resId);
- pw.print(type); pw.print(": ");
- if (services == null) {
- pw.println("N/A");
- return;
- }
- pw.print(services.length);
- pw.println(" services");
- for (int i = 0; i < services.length; i++) {
- pw.print(" "); pw.print(i); pw.print(": "); pw.println(services[i]);
- }
+ mDumpManager.dump(fd, pw, massagedArgs);
}
}
-
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index cedf7c354ccc..bd803fa76f13 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -31,6 +31,7 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
import java.io.FileDescriptor
import java.io.PrintWriter
import java.util.concurrent.Executor
@@ -65,12 +66,18 @@ private const val DEBUG = true
open class BroadcastDispatcher @Inject constructor (
private val context: Context,
@Main private val mainHandler: Handler,
- @Background private val bgLooper: Looper
+ @Background private val bgLooper: Looper,
+ dumpManager: DumpManager
) : Dumpable {
// Only modify in BG thread
private val receiversByUser = SparseArray<UserBroadcastDispatcher>(20)
+ init {
+ // TODO: Don't do this in the constructor
+ dumpManager.registerDumpable(javaClass.name, this)
+ }
+
/**
* Register a receiver for broadcast with the dispatcher
*
@@ -112,10 +119,10 @@ open class BroadcastDispatcher @Inject constructor (
*/
@JvmOverloads
fun registerReceiver(
- receiver: BroadcastReceiver,
- filter: IntentFilter,
- executor: Executor? = context.mainExecutor,
- user: UserHandle = context.user
+ receiver: BroadcastReceiver,
+ filter: IntentFilter,
+ executor: Executor? = context.mainExecutor,
+ user: UserHandle = context.user
) {
checkFilter(filter)
this.handler
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index 12b9be11817a..18c3eacbc693 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -25,6 +25,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.SystemUIFactory;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.pip.phone.dagger.PipModule;
@@ -76,6 +77,10 @@ public interface SystemUIRootComponent {
@Singleton
Dependency.DependencyInjector createDependency();
+ /** */
+ @Singleton
+ DumpManager createDumpManager();
+
/**
* FragmentCreator generates all Fragments that need injection.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
new file mode 100644
index 000000000000..59a7a328e9ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dump
+
+import android.content.Context
+import android.os.SystemClock
+import android.os.Trace
+import android.util.ArrayMap
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.dump.DumpManager.Companion.PRIORITY_ARG_CRITICAL
+import com.android.systemui.dump.DumpManager.Companion.PRIORITY_ARG_HIGH
+import com.android.systemui.dump.DumpManager.Companion.PRIORITY_ARG_NORMAL
+import com.android.systemui.log.LogBuffer
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * Oversees SystemUI's output during bug reports (and dumpsys in general)
+ *
+ * When a bug report is taken, SystemUI dumps various diagnostic information that we hope will be
+ * useful for the eventual readers of the bug report. Code that wishes to participate in this dump
+ * should register itself here.
+ *
+ * Dump output is split into two sections, CRITICAL and NORMAL. All dumpables registered via
+ * [registerDumpable] appear in the CRITICAL section, while all [LogBuffer]s appear in the NORMAL
+ * section (due to their length).
+ *
+ * The CRITICAL and NORMAL sections can be found within a bug report by searching for
+ * "SERVICE com.android.systemui/.SystemUIService" and
+ * "SERVICE com.android.systemui/.dump.SystemUIAuxiliaryDumpService", respectively.
+ *
+ * Finally, some or all of the dump can be triggered on-demand via adb (see below).
+ *
+ * ```
+ * # For the following, let <invocation> be:
+ * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService
+ *
+ * # To dump specific target(s), specify one or more registered names:
+ * $ <invocation> NotifCollection
+ * $ <invocation> StatusBar FalsingManager BootCompleteCacheImpl
+ *
+ * # Log buffers can be dumped in the same way (and can even be mixed in with other dump targets,
+ * # although it's not clear why one would want such a thing):
+ * $ <invocation> NotifLog
+ * $ <invocation> StatusBar NotifLog BootCompleteCacheImpl
+ *
+ * # If passing -t or --tail, shows only the last N lines of any log buffers:
+ * $ <invocation> NotifLog --tail 100
+ *
+ * # Dump targets are matched using String.endsWith(), so dumpables that register using their
+ * # fully-qualified class name can still be dumped using their short name:
+ * $ <invocation> com.android.keyguard.KeyguardUpdateMonitor
+ * $ <invocation> keyguard.KeyguardUpdateMonitor
+ * $ <invocation> KeyguardUpdateMonitor
+ *
+ * # To dump all dumpables or all buffers:
+ * $ <invocation> dumpables
+ * $ <invocation> buffers
+ *
+ * Finally, the following will simulate what we dump during the CRITICAL and NORMAL sections of a
+ * bug report:
+ * $ <invocation> bugreport-critical
+ * $ <invocation> bugreport-normal
+ * ```
+ */
+@Singleton
+class DumpManager @Inject constructor(
+ private val context: Context
+) {
+ private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = ArrayMap()
+ private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = ArrayMap()
+
+ /**
+ * Register a dumpable to be called during a bug report. The dumpable will be called during the
+ * CRITICAL section of the bug report, so don't dump an excessive amount of stuff here.
+ *
+ * @param name The name to register the dumpable under. This is typically the qualified class
+ * name of the thing being dumped (getClass().getName()), but can be anything as long as it
+ * doesn't clash with an existing registration.
+ */
+ @Synchronized
+ fun registerDumpable(name: String, module: Dumpable) {
+ if (RESERVED_NAMES.contains(name)) {
+ throw IllegalArgumentException("'$name' is reserved")
+ }
+
+ if (!canAssignToNameLocked(name, module)) {
+ throw IllegalArgumentException("'$name' is already registered")
+ }
+
+ dumpables[name] = RegisteredDumpable(name, module)
+ }
+
+ /**
+ * Unregisters a previously-registered dumpable.
+ */
+ @Synchronized
+ fun unregisterDumpable(name: String) {
+ dumpables.remove(name)
+ }
+
+ /**
+ * Register a [LogBuffer] to be dumped during a bug report.
+ */
+ @Synchronized
+ fun registerBuffer(name: String, buffer: LogBuffer) {
+ if (!canAssignToNameLocked(name, buffer)) {
+ throw IllegalArgumentException("'$name' is already registered")
+ }
+ buffers[name] = RegisteredDumpable(name, buffer)
+ }
+
+ /**
+ * Dump the diagnostics! Behavior can be controlled via [args].
+ */
+ @Synchronized
+ fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
+ Trace.beginSection("DumpManager#dump()")
+ val start = SystemClock.uptimeMillis()
+
+ val parsedArgs = try {
+ parseArgs(args)
+ } catch (e: ArgParseException) {
+ pw.println(e.message)
+ return
+ }
+
+ when (parsedArgs.dumpPriority) {
+ PRIORITY_ARG_CRITICAL -> dumpCriticalLocked(fd, pw, parsedArgs)
+ PRIORITY_ARG_NORMAL -> dumpNormalLocked(pw, parsedArgs)
+ else -> dumpParameterizedLocked(fd, pw, parsedArgs)
+ }
+
+ pw.println()
+ pw.println("Dump took ${SystemClock.uptimeMillis() - start}ms")
+ Trace.endSection()
+ }
+
+ private fun dumpCriticalLocked(fd: FileDescriptor, pw: PrintWriter, args: ParsedArgs) {
+ dumpDumpablesLocked(fd, pw, args)
+ dumpConfig(pw)
+ }
+
+ private fun dumpNormalLocked(pw: PrintWriter, args: ParsedArgs) {
+ dumpBuffersLocked(pw, args)
+ }
+
+ private fun dumpParameterizedLocked(fd: FileDescriptor, pw: PrintWriter, args: ParsedArgs) {
+ when (args.command) {
+ "bugreport-critical" -> dumpCriticalLocked(fd, pw, args)
+ "bugreport-normal" -> dumpNormalLocked(pw, args)
+ "dumpables" -> dumpDumpablesLocked(fd, pw, args)
+ "buffers" -> dumpBuffersLocked(pw, args)
+ else -> dumpTargetsLocked(args.nonFlagArgs, fd, pw, args)
+ }
+ }
+
+ private fun dumpTargetsLocked(
+ targets: List<String>,
+ fd: FileDescriptor,
+ pw: PrintWriter,
+ args: ParsedArgs
+ ) {
+ if (targets.isEmpty()) {
+ pw.println("Nothing to dump :(")
+ } else {
+ for (target in targets) {
+ dumpTarget(target, fd, pw, args)
+ }
+ }
+ }
+
+ private fun dumpTarget(
+ target: String,
+ fd: FileDescriptor,
+ pw: PrintWriter,
+ args: ParsedArgs
+ ) {
+ if (target == "config") {
+ dumpConfig(pw)
+ return
+ }
+
+ for (dumpable in dumpables.values) {
+ if (dumpable.name.endsWith(target)) {
+ dumpDumpable(dumpable, fd, pw, args)
+ return
+ }
+ }
+
+ for (buffer in buffers.values) {
+ if (buffer.name.endsWith(target)) {
+ dumpBuffer(buffer, pw, args)
+ return
+ }
+ }
+ }
+
+ private fun dumpDumpablesLocked(fd: FileDescriptor, pw: PrintWriter, args: ParsedArgs) {
+ for (module in dumpables.values) {
+ dumpDumpable(module, fd, pw, args)
+ }
+ }
+
+ private fun dumpBuffersLocked(pw: PrintWriter, args: ParsedArgs) {
+ for (buffer in buffers.values) {
+ dumpBuffer(buffer, pw, args)
+ }
+ }
+
+ private fun dumpDumpable(
+ dumpable: RegisteredDumpable<Dumpable>,
+ fd: FileDescriptor,
+ pw: PrintWriter,
+ args: ParsedArgs
+ ) {
+ pw.println()
+ pw.println("${dumpable.name}:")
+ pw.println("----------------------------------------------------------------------------")
+ dumpable.dumpable.dump(fd, pw, args.rawArgs)
+ }
+
+ private fun dumpBuffer(
+ buffer: RegisteredDumpable<LogBuffer>,
+ pw: PrintWriter,
+ args: ParsedArgs
+ ) {
+ pw.println()
+ pw.println()
+ pw.println("BUFFER ${buffer.name}:")
+ pw.println("============================================================================")
+ buffer.dumpable.dump(pw, args.tailLength)
+ }
+
+ private fun dumpConfig(pw: PrintWriter) {
+ pw.println("SystemUiServiceComponents configuration:")
+ pw.print("vendor component: ")
+ pw.println(context.resources.getString(R.string.config_systemUIVendorServiceComponent))
+ dumpServiceList(pw, "global", R.array.config_systemUIServiceComponents)
+ dumpServiceList(pw, "per-user", R.array.config_systemUIServiceComponentsPerUser)
+ }
+
+ private fun dumpServiceList(pw: PrintWriter, type: String, resId: Int) {
+ val services: Array<String>? = context.resources.getStringArray(resId)
+ pw.print(type)
+ pw.print(": ")
+ if (services == null) {
+ pw.println("N/A")
+ return
+ }
+ pw.print(services.size)
+ pw.println(" services")
+ for (i in services.indices) {
+ pw.print(" ")
+ pw.print(i)
+ pw.print(": ")
+ pw.println(services[i])
+ }
+ }
+
+ private fun parseArgs(args: Array<String>): ParsedArgs {
+ val mutArgs = args.toMutableList()
+ val pArgs = ParsedArgs(args, mutArgs)
+
+ val iterator = mutArgs.iterator()
+ while (iterator.hasNext()) {
+ val arg = iterator.next()
+ if (arg.startsWith("-")) {
+ iterator.remove()
+ when (arg) {
+ PRIORITY_ARG -> {
+ pArgs.dumpPriority = readArgument(iterator, PRIORITY_ARG) {
+ if (PRIORITY_OPTIONS.contains(it)) {
+ it
+ } else {
+ throw IllegalArgumentException()
+ }
+ }
+ }
+ "-t", "--tail" -> {
+ pArgs.tailLength = readArgument(iterator, "--tail") {
+ it.toInt()
+ }
+ }
+ else -> {
+ throw ArgParseException("Unknown flag: $arg")
+ }
+ }
+ }
+ }
+
+ if (mutArgs.isNotEmpty() && COMMANDS.contains(mutArgs[0])) {
+ pArgs.command = mutArgs.removeAt(0)
+ }
+
+ return pArgs
+ }
+
+ private fun <T> readArgument(
+ iterator: MutableIterator<String>,
+ flag: String,
+ parser: (arg: String) -> T
+ ): T {
+ if (!iterator.hasNext()) {
+ throw ArgParseException("Missing argument for $flag")
+ }
+ val value = iterator.next()
+
+ return try {
+ parser(value).also { iterator.remove() }
+ } catch (e: Exception) {
+ throw ArgParseException("Invalid argument '$value' for flag $flag")
+ }
+ }
+
+ private fun canAssignToNameLocked(name: String, newDumpable: Any): Boolean {
+ val existingDumpable = dumpables[name]?.dumpable ?: buffers[name]?.dumpable
+ return existingDumpable == null || newDumpable == existingDumpable
+ }
+
+ companion object {
+ const val PRIORITY_ARG = "--dump-priority"
+ const val PRIORITY_ARG_CRITICAL = "CRITICAL"
+ const val PRIORITY_ARG_HIGH = "HIGH"
+ const val PRIORITY_ARG_NORMAL = "NORMAL"
+ }
+}
+
+private val PRIORITY_OPTIONS =
+ arrayOf(PRIORITY_ARG_CRITICAL, PRIORITY_ARG_HIGH, PRIORITY_ARG_NORMAL)
+
+private val COMMANDS = arrayOf("bugreport-critical", "bugreport-normal", "buffers", "dumpables")
+
+private val RESERVED_NAMES = arrayOf("config", *COMMANDS)
+
+private data class RegisteredDumpable<T>(
+ val name: String,
+ val dumpable: T
+)
+
+private class ParsedArgs(
+ val rawArgs: Array<String>,
+ val nonFlagArgs: List<String>
+) {
+ var dumpPriority: String? = null
+ var tailLength: Int = 0
+ var command: String? = null
+}
+
+class ArgParseException(message: String) : Exception(message) \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index 18c7baec1f74..7defef90380f 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -17,9 +17,9 @@
package com.android.systemui.log
import android.util.Log
-import com.android.systemui.DumpController
-import com.android.systemui.Dumpable
+import com.android.systemui.dump.DumpManager
import com.android.systemui.log.dagger.LogModule
+import java.io.PrintWriter
import java.text.SimpleDateFormat
import java.util.ArrayDeque
import java.util.Locale
@@ -35,11 +35,10 @@ import java.util.Locale
* You can dump the entire buffer at any time by running:
*
* ```
- * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService \
- * dependency DumpController <bufferName>
+ * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService <bufferName>
* ```
*
- * where `bufferName` is the (case-sensitive) [name] passed to the constructor.
+ * ...where `bufferName` is the (case-sensitive) [name] passed to the constructor.
*
* By default, only messages of WARN level or higher are echoed to logcat, but this can be adjusted
* locally (usually for debugging purposes).
@@ -75,8 +74,8 @@ class LogBuffer(
) {
private val buffer: ArrayDeque<LogMessageImpl> = ArrayDeque()
- fun attach(dumpController: DumpController) {
- dumpController.registerDumpable(name, onDump)
+ fun attach(dumpManager: DumpManager) {
+ dumpManager.registerBuffer(name, this)
}
/**
@@ -174,22 +173,26 @@ class LogBuffer(
}
/** Converts the entire buffer to a newline-delimited string */
- fun dump(): String {
+ fun dump(pw: PrintWriter, tailLength: Int) {
synchronized(buffer) {
- val sb = StringBuilder()
- for (message in buffer) {
- dumpMessage(message, sb)
+ val start = if (tailLength <= 0) { 0 } else { buffer.size - tailLength }
+
+ for ((i, message) in buffer.withIndex()) {
+ if (i >= start) {
+ dumpMessage(message, pw)
+ }
}
- return sb.toString()
}
}
- private fun dumpMessage(message: LogMessage, sb: StringBuilder) {
- sb.append(DATE_FORMAT.format(message.timestamp))
- .append(" ").append(message.level)
- .append(" ").append(message.tag)
- .append(" ").append(message.printer(message))
- .append("\n")
+ private fun dumpMessage(message: LogMessage, pw: PrintWriter) {
+ pw.print(DATE_FORMAT.format(message.timestamp))
+ pw.print(" ")
+ pw.print(message.level)
+ pw.print(" ")
+ pw.print(message.tag)
+ pw.print(" ")
+ pw.println(message.printer(message))
}
private fun echoToLogcat(message: LogMessage) {
@@ -203,10 +206,6 @@ class LogBuffer(
LogLevel.WTF -> Log.wtf(message.tag, strMessage)
}
}
-
- private val onDump = Dumpable { _, pw, _ ->
- pw.println(dump())
- }
}
private const val TAG = "LogBuffer"
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 4a7469c1bd2d..b1d972e6d97f 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -20,8 +20,8 @@ import android.content.ContentResolver;
import android.os.Build;
import android.os.Looper;
-import com.android.systemui.DumpController;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogcatEchoTracker;
import com.android.systemui.log.LogcatEchoTrackerDebug;
@@ -43,9 +43,9 @@ public class LogModule {
@DozeLog
public static LogBuffer provideDozeLogBuffer(
LogcatEchoTracker bufferFilter,
- DumpController dumpController) {
+ DumpManager dumpManager) {
LogBuffer buffer = new LogBuffer("DozeLog", 100, 10, bufferFilter);
- buffer.attach(dumpController);
+ buffer.attach(dumpManager);
return buffer;
}
@@ -55,9 +55,9 @@ public class LogModule {
@NotificationLog
public static LogBuffer provideNotificationsLogBuffer(
LogcatEchoTracker bufferFilter,
- DumpController dumpController) {
+ DumpManager dumpManager) {
LogBuffer buffer = new LogBuffer("NotifLog2", 1000, 10, bufferFilter);
- buffer.attach(dumpController);
+ buffer.attach(dumpManager);
return buffer;
}
@@ -67,9 +67,9 @@ public class LogModule {
@QSLog
public static LogBuffer provideQuickSettingsLogBuffer(
LogcatEchoTracker bufferFilter,
- DumpController dumpController) {
+ DumpManager dumpManager) {
LogBuffer buffer = new LogBuffer("QSLog", 500, 10, bufferFilter);
- buffer.attach(dumpController);
+ buffer.attach(dumpManager);
return buffer;
}