Proto dump from SystemUI.
Needs to be called by doing `adb shell dumpsys statusbar --proto` because using `dumpsys activity service` adds text to the dump. Will also be dumped as part of the BR Test: atest SystemUITests Test: Used in CTS Test: manual dump and BR Fixes 237786667 Change-Id: I74abf65a76c059ea9bb5a14728192b6b683b7fc7
This commit is contained in:
@@ -322,4 +322,7 @@ oneway interface IStatusBar
|
||||
|
||||
/** Unregisters a nearby media devices provider. */
|
||||
void unregisterNearbyMediaDevicesProvider(in INearbyMediaDevicesProvider provider);
|
||||
|
||||
/** Dump protos from SystemUI. The proto definition is defined there */
|
||||
void dumpProto(in String[] args, in ParcelFileDescriptor pfd);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ public interface Dumpable {
|
||||
|
||||
/**
|
||||
* Called when it's time to dump the internal state
|
||||
* @param fd A file descriptor.
|
||||
* @param pw Where to write your dump to.
|
||||
* @param args Arguments.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.android.systemui
|
||||
|
||||
import com.android.systemui.dump.nano.SystemUIProtoDump
|
||||
|
||||
interface ProtoDumpable : Dumpable {
|
||||
fun dumpProto(systemUIProtoDump: SystemUIProtoDump, args: Array<String>)
|
||||
}
|
||||
@@ -121,6 +121,6 @@ public class SystemUIService extends Service {
|
||||
DumpHandler.PRIORITY_ARG_CRITICAL};
|
||||
}
|
||||
|
||||
mDumpHandler.dump(pw, massagedArgs);
|
||||
mDumpHandler.dump(fd, pw, massagedArgs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,13 @@ import com.android.systemui.R
|
||||
import com.android.systemui.dump.DumpHandler.Companion.PRIORITY_ARG_CRITICAL
|
||||
import com.android.systemui.dump.DumpHandler.Companion.PRIORITY_ARG_HIGH
|
||||
import com.android.systemui.dump.DumpHandler.Companion.PRIORITY_ARG_NORMAL
|
||||
import com.android.systemui.dump.nano.SystemUIProtoDump
|
||||
import com.android.systemui.plugins.log.LogBuffer
|
||||
import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager
|
||||
import com.google.protobuf.nano.MessageNano
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.FileDescriptor
|
||||
import java.io.FileOutputStream
|
||||
import java.io.PrintWriter
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
@@ -100,7 +105,7 @@ class DumpHandler @Inject constructor(
|
||||
/**
|
||||
* Dump the diagnostics! Behavior can be controlled via [args].
|
||||
*/
|
||||
fun dump(pw: PrintWriter, args: Array<String>) {
|
||||
fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
|
||||
Trace.beginSection("DumpManager#dump()")
|
||||
val start = SystemClock.uptimeMillis()
|
||||
|
||||
@@ -111,10 +116,12 @@ class DumpHandler @Inject constructor(
|
||||
return
|
||||
}
|
||||
|
||||
when (parsedArgs.dumpPriority) {
|
||||
PRIORITY_ARG_CRITICAL -> dumpCritical(pw, parsedArgs)
|
||||
PRIORITY_ARG_NORMAL -> dumpNormal(pw, parsedArgs)
|
||||
else -> dumpParameterized(pw, parsedArgs)
|
||||
when {
|
||||
parsedArgs.dumpPriority == PRIORITY_ARG_CRITICAL -> dumpCritical(pw, parsedArgs)
|
||||
parsedArgs.dumpPriority == PRIORITY_ARG_NORMAL && !parsedArgs.proto -> {
|
||||
dumpNormal(pw, parsedArgs)
|
||||
}
|
||||
else -> dumpParameterized(fd, pw, parsedArgs)
|
||||
}
|
||||
|
||||
pw.println()
|
||||
@@ -122,7 +129,7 @@ class DumpHandler @Inject constructor(
|
||||
Trace.endSection()
|
||||
}
|
||||
|
||||
private fun dumpParameterized(pw: PrintWriter, args: ParsedArgs) {
|
||||
private fun dumpParameterized(fd: FileDescriptor, pw: PrintWriter, args: ParsedArgs) {
|
||||
when (args.command) {
|
||||
"bugreport-critical" -> dumpCritical(pw, args)
|
||||
"bugreport-normal" -> dumpNormal(pw, args)
|
||||
@@ -130,7 +137,13 @@ class DumpHandler @Inject constructor(
|
||||
"buffers" -> dumpBuffers(pw, args)
|
||||
"config" -> dumpConfig(pw)
|
||||
"help" -> dumpHelp(pw)
|
||||
else -> dumpTargets(args.nonFlagArgs, pw, args)
|
||||
else -> {
|
||||
if (args.proto) {
|
||||
dumpProtoTargets(args.nonFlagArgs, fd, args)
|
||||
} else {
|
||||
dumpTargets(args.nonFlagArgs, pw, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +173,26 @@ class DumpHandler @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun dumpProtoTargets(
|
||||
targets: List<String>,
|
||||
fd: FileDescriptor,
|
||||
args: ParsedArgs
|
||||
) {
|
||||
val systemUIProto = SystemUIProtoDump()
|
||||
if (targets.isNotEmpty()) {
|
||||
for (target in targets) {
|
||||
dumpManager.dumpProtoTarget(target, systemUIProto, args.rawArgs)
|
||||
}
|
||||
} else {
|
||||
dumpManager.dumpProtoDumpables(systemUIProto, args.rawArgs)
|
||||
}
|
||||
val buffer = BufferedOutputStream(FileOutputStream(fd))
|
||||
buffer.use {
|
||||
it.write(MessageNano.toByteArray(systemUIProto))
|
||||
it.flush()
|
||||
}
|
||||
}
|
||||
|
||||
private fun dumpTargets(
|
||||
targets: List<String>,
|
||||
pw: PrintWriter,
|
||||
@@ -267,6 +300,7 @@ class DumpHandler @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
PROTO -> pArgs.proto = true
|
||||
"-t", "--tail" -> {
|
||||
pArgs.tailLength = readArgument(iterator, arg) {
|
||||
it.toInt()
|
||||
@@ -278,6 +312,9 @@ class DumpHandler @Inject constructor(
|
||||
"-h", "--help" -> {
|
||||
pArgs.command = "help"
|
||||
}
|
||||
// This flag is passed as part of the proto dump in Bug reports, we can ignore
|
||||
// it because this is our default behavior.
|
||||
"-a" -> {}
|
||||
else -> {
|
||||
throw ArgParseException("Unknown flag: $arg")
|
||||
}
|
||||
@@ -314,7 +351,7 @@ class DumpHandler @Inject constructor(
|
||||
const val PRIORITY_ARG_CRITICAL = "CRITICAL"
|
||||
const val PRIORITY_ARG_HIGH = "HIGH"
|
||||
const val PRIORITY_ARG_NORMAL = "NORMAL"
|
||||
const val PROTO = "--sysui_proto"
|
||||
const val PROTO = "--proto"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,6 +375,7 @@ private class ParsedArgs(
|
||||
var tailLength: Int = 0
|
||||
var command: String? = null
|
||||
var listOnly = false
|
||||
var proto = false
|
||||
}
|
||||
|
||||
class ArgParseException(message: String) : Exception(message)
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.systemui.dump
|
||||
|
||||
import android.util.ArrayMap
|
||||
import com.android.systemui.Dumpable
|
||||
import com.android.systemui.ProtoDumpable
|
||||
import com.android.systemui.dump.nano.SystemUIProtoDump
|
||||
import com.android.systemui.plugins.log.LogBuffer
|
||||
import java.io.PrintWriter
|
||||
import javax.inject.Inject
|
||||
@@ -90,7 +92,7 @@ open class DumpManager @Inject constructor() {
|
||||
target: String,
|
||||
pw: PrintWriter,
|
||||
args: Array<String>,
|
||||
tailLength: Int
|
||||
tailLength: Int,
|
||||
) {
|
||||
for (dumpable in dumpables.values) {
|
||||
if (dumpable.name.endsWith(target)) {
|
||||
@@ -107,6 +109,36 @@ open class DumpManager @Inject constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun dumpProtoTarget(
|
||||
target: String,
|
||||
protoDump: SystemUIProtoDump,
|
||||
args: Array<String>
|
||||
) {
|
||||
for (dumpable in dumpables.values) {
|
||||
if (dumpable.dumpable is ProtoDumpable && dumpable.name.endsWith(target)) {
|
||||
dumpProtoDumpable(dumpable.dumpable, protoDump, args)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun dumpProtoDumpables(
|
||||
systemUIProtoDump: SystemUIProtoDump,
|
||||
args: Array<String>
|
||||
) {
|
||||
for (dumpable in dumpables.values) {
|
||||
if (dumpable.dumpable is ProtoDumpable) {
|
||||
dumpProtoDumpable(
|
||||
dumpable.dumpable,
|
||||
systemUIProtoDump,
|
||||
args
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps all registered dumpables to [pw]
|
||||
*/
|
||||
@@ -184,6 +216,14 @@ open class DumpManager @Inject constructor() {
|
||||
buffer.dumpable.dump(pw, tailLength)
|
||||
}
|
||||
|
||||
private fun dumpProtoDumpable(
|
||||
protoDumpable: ProtoDumpable,
|
||||
systemUIProtoDump: SystemUIProtoDump,
|
||||
args: Array<String>
|
||||
) {
|
||||
protoDumpable.dumpProto(systemUIProtoDump, args)
|
||||
}
|
||||
|
||||
private fun canAssignToNameLocked(name: String, newDumpable: Any): Boolean {
|
||||
val existingDumpable = dumpables[name]?.dumpable ?: buffers[name]?.dumpable
|
||||
return existingDumpable == null || newDumpable == existingDumpable
|
||||
@@ -195,4 +235,4 @@ private data class RegisteredDumpable<T>(
|
||||
val dumpable: T
|
||||
)
|
||||
|
||||
private const val TAG = "DumpManager"
|
||||
private const val TAG = "DumpManager"
|
||||
|
||||
@@ -51,6 +51,7 @@ public class SystemUIAuxiliaryDumpService extends Service {
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
// Simulate the NORMAL priority arg being passed to us
|
||||
mDumpHandler.dump(
|
||||
fd,
|
||||
pw,
|
||||
new String[] { DumpHandler.PRIORITY_ARG, DumpHandler.PRIORITY_ARG_NORMAL });
|
||||
}
|
||||
|
||||
27
packages/SystemUI/src/com/android/systemui/dump/sysui.proto
Normal file
27
packages/SystemUI/src/com/android/systemui/dump/sysui.proto
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.
|
||||
*/
|
||||
syntax = "proto3";
|
||||
|
||||
package com.android.systemui.dump;
|
||||
|
||||
import "frameworks/base/packages/SystemUI/src/com/android/systemui/qs/proto/tiles.proto";
|
||||
|
||||
option java_multiple_files = true;
|
||||
|
||||
message SystemUIProtoDump {
|
||||
repeated com.android.systemui.qs.QsTileState tiles = 1;
|
||||
}
|
||||
|
||||
@@ -34,10 +34,12 @@ import com.android.internal.logging.InstanceId;
|
||||
import com.android.internal.logging.InstanceIdSequence;
|
||||
import com.android.internal.logging.UiEventLogger;
|
||||
import com.android.systemui.Dumpable;
|
||||
import com.android.systemui.ProtoDumpable;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.dump.nano.SystemUIProtoDump;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.plugins.qs.QSFactory;
|
||||
import com.android.systemui.plugins.qs.QSTile;
|
||||
@@ -48,6 +50,7 @@ import com.android.systemui.qs.external.TileLifecycleManager;
|
||||
import com.android.systemui.qs.external.TileServiceKey;
|
||||
import com.android.systemui.qs.external.TileServiceRequestController;
|
||||
import com.android.systemui.qs.logging.QSLogger;
|
||||
import com.android.systemui.qs.nano.QsTileState;
|
||||
import com.android.systemui.settings.UserFileManager;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.shared.plugins.PluginManager;
|
||||
@@ -59,16 +62,20 @@ import com.android.systemui.tuner.TunerService.Tunable;
|
||||
import com.android.systemui.util.leak.GarbageMonitor;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
@@ -82,7 +89,7 @@ import javax.inject.Provider;
|
||||
* This class also provides the interface for adding/removing/changing tiles.
|
||||
*/
|
||||
@SysUISingleton
|
||||
public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, Dumpable {
|
||||
public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, ProtoDumpable {
|
||||
private static final String TAG = "QSTileHost";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
private static final int MAX_QS_INSTANCE_ID = 1 << 20;
|
||||
@@ -671,4 +678,15 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
||||
mTiles.values().stream().filter(obj -> obj instanceof Dumpable)
|
||||
.forEach(o -> ((Dumpable) o).dump(pw, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dumpProto(@NotNull SystemUIProtoDump systemUIProtoDump, @NotNull String[] args) {
|
||||
List<QsTileState> data = mTiles.values().stream()
|
||||
.map(QSTile::getState)
|
||||
.map(TileStateToProtoKt::toProto)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
systemUIProtoDump.tiles = data.toArray(new QsTileState[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.qs
|
||||
|
||||
import android.service.quicksettings.Tile
|
||||
import android.text.TextUtils
|
||||
import com.android.systemui.plugins.qs.QSTile
|
||||
import com.android.systemui.qs.external.CustomTile
|
||||
import com.android.systemui.qs.nano.QsTileState
|
||||
import com.android.systemui.util.nano.ComponentNameProto
|
||||
|
||||
fun QSTile.State.toProto(): QsTileState? {
|
||||
if (TextUtils.isEmpty(spec)) return null
|
||||
val state = QsTileState()
|
||||
if (spec.startsWith(CustomTile.PREFIX)) {
|
||||
val protoComponentName = ComponentNameProto()
|
||||
val tileComponentName = CustomTile.getComponentFromSpec(spec)
|
||||
protoComponentName.packageName = tileComponentName.packageName
|
||||
protoComponentName.className = tileComponentName.className
|
||||
state.componentName = protoComponentName
|
||||
} else {
|
||||
state.spec = spec
|
||||
}
|
||||
state.state =
|
||||
when (this.state) {
|
||||
Tile.STATE_UNAVAILABLE -> QsTileState.UNAVAILABLE
|
||||
Tile.STATE_INACTIVE -> QsTileState.INACTIVE
|
||||
Tile.STATE_ACTIVE -> QsTileState.ACTIVE
|
||||
else -> QsTileState.UNAVAILABLE
|
||||
}
|
||||
label?.let { state.label = it.toString() }
|
||||
secondaryLabel?.let { state.secondaryLabel = it.toString() }
|
||||
if (this is QSTile.BooleanState) {
|
||||
state.booleanState = value
|
||||
}
|
||||
return state
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package com.android.systemui.qs;
|
||||
|
||||
import "frameworks/base/packages/SystemUI/src/com/android/systemui/util/proto/component_name.proto";
|
||||
|
||||
option java_multiple_files = true;
|
||||
|
||||
message QsTileState {
|
||||
oneof identifier {
|
||||
string spec = 1;
|
||||
com.android.systemui.util.ComponentNameProto component_name = 2;
|
||||
}
|
||||
|
||||
enum State {
|
||||
UNAVAILABLE = 0;
|
||||
INACTIVE = 1;
|
||||
ACTIVE = 2;
|
||||
}
|
||||
|
||||
State state = 3;
|
||||
oneof optional_boolean_state {
|
||||
bool boolean_state = 4;
|
||||
}
|
||||
oneof optional_label {
|
||||
string label = 5;
|
||||
}
|
||||
oneof optional_secondary_label {
|
||||
string secondary_label = 6;
|
||||
}
|
||||
}
|
||||
@@ -69,12 +69,15 @@ import com.android.internal.statusbar.LetterboxDetails;
|
||||
import com.android.internal.statusbar.StatusBarIcon;
|
||||
import com.android.internal.util.GcUtils;
|
||||
import com.android.internal.view.AppearanceRegion;
|
||||
import com.android.systemui.dump.DumpHandler;
|
||||
import com.android.systemui.statusbar.CommandQueue.Callbacks;
|
||||
import com.android.systemui.statusbar.commandline.CommandRegistry;
|
||||
import com.android.systemui.statusbar.policy.CallbackController;
|
||||
import com.android.systemui.tracing.ProtoTracer;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -184,6 +187,7 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
private int mLastUpdatedImeDisplayId = INVALID_DISPLAY;
|
||||
private ProtoTracer mProtoTracer;
|
||||
private final @Nullable CommandRegistry mRegistry;
|
||||
private final @Nullable DumpHandler mDumpHandler;
|
||||
|
||||
/**
|
||||
* These methods are called back on the main thread.
|
||||
@@ -473,12 +477,18 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
}
|
||||
|
||||
public CommandQueue(Context context) {
|
||||
this(context, null, null);
|
||||
this(context, null, null, null);
|
||||
}
|
||||
|
||||
public CommandQueue(Context context, ProtoTracer protoTracer, CommandRegistry registry) {
|
||||
public CommandQueue(
|
||||
Context context,
|
||||
ProtoTracer protoTracer,
|
||||
CommandRegistry registry,
|
||||
DumpHandler dumpHandler
|
||||
) {
|
||||
mProtoTracer = protoTracer;
|
||||
mRegistry = registry;
|
||||
mDumpHandler = dumpHandler;
|
||||
context.getSystemService(DisplayManager.class).registerDisplayListener(this, mHandler);
|
||||
// We always have default display.
|
||||
setDisabled(DEFAULT_DISPLAY, DISABLE_NONE, DISABLE2_NONE);
|
||||
@@ -1177,6 +1187,35 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
thr.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dumpProto(String[] args, ParcelFileDescriptor pfd) {
|
||||
final FileDescriptor fd = pfd.getFileDescriptor();
|
||||
// This is mimicking Binder#dumpAsync, but on this side of the binder. Might be possible
|
||||
// to just throw this work onto the handler just like the other messages
|
||||
Thread thr = new Thread("Sysui.dumpProto") {
|
||||
public void run() {
|
||||
try {
|
||||
if (mDumpHandler == null) {
|
||||
return;
|
||||
}
|
||||
// We won't be using the PrintWriter.
|
||||
OutputStream o = new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) {}
|
||||
};
|
||||
mDumpHandler.dump(fd, new PrintWriter(o), args);
|
||||
} finally {
|
||||
try {
|
||||
// Close the file descriptor so the TransferPipe finishes its thread
|
||||
pfd.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
thr.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runGcForTest() {
|
||||
// Gc sysui
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.android.systemui.animation.DialogLaunchAnimator;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dump.DumpHandler;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.media.MediaDataManager;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
@@ -181,8 +182,10 @@ public interface CentralSurfacesDependenciesModule {
|
||||
static CommandQueue provideCommandQueue(
|
||||
Context context,
|
||||
ProtoTracer protoTracer,
|
||||
CommandRegistry registry) {
|
||||
return new CommandQueue(context, protoTracer, registry);
|
||||
CommandRegistry registry,
|
||||
DumpHandler dumpHandler
|
||||
) {
|
||||
return new CommandQueue(context, protoTracer, registry, dumpHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package com.android.systemui.util;
|
||||
|
||||
option java_multiple_files = true;
|
||||
|
||||
message ComponentNameProto {
|
||||
string package_name = 1;
|
||||
string class_name = 2;
|
||||
}
|
||||
@@ -19,11 +19,17 @@ package com.android.systemui.dump
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.CoreStartable
|
||||
import com.android.systemui.Dumpable
|
||||
import com.android.systemui.ProtoDumpable
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.plugins.log.LogBuffer
|
||||
import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import java.io.FileDescriptor
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import javax.inject.Provider
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
@@ -31,9 +37,6 @@ import org.mockito.Mockito.anyInt
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import javax.inject.Provider
|
||||
|
||||
@SmallTest
|
||||
class DumpHandlerTest : SysuiTestCase() {
|
||||
@@ -47,6 +50,8 @@ class DumpHandlerTest : SysuiTestCase() {
|
||||
|
||||
@Mock
|
||||
private lateinit var pw: PrintWriter
|
||||
@Mock
|
||||
private lateinit var fd: FileDescriptor
|
||||
|
||||
@Mock
|
||||
private lateinit var dumpable1: Dumpable
|
||||
@@ -55,6 +60,11 @@ class DumpHandlerTest : SysuiTestCase() {
|
||||
@Mock
|
||||
private lateinit var dumpable3: Dumpable
|
||||
|
||||
@Mock
|
||||
private lateinit var protoDumpable1: ProtoDumpable
|
||||
@Mock
|
||||
private lateinit var protoDumpable2: ProtoDumpable
|
||||
|
||||
@Mock
|
||||
private lateinit var buffer1: LogBuffer
|
||||
@Mock
|
||||
@@ -88,7 +98,7 @@ class DumpHandlerTest : SysuiTestCase() {
|
||||
|
||||
// WHEN some of them are dumped explicitly
|
||||
val args = arrayOf("dumpable1", "dumpable3", "buffer2")
|
||||
dumpHandler.dump(pw, args)
|
||||
dumpHandler.dump(fd, pw, args)
|
||||
|
||||
// THEN only the requested ones have their dump() method called
|
||||
verify(dumpable1).dump(pw, args)
|
||||
@@ -107,7 +117,7 @@ class DumpHandlerTest : SysuiTestCase() {
|
||||
|
||||
// WHEN that module is dumped
|
||||
val args = arrayOf("dumpable1")
|
||||
dumpHandler.dump(pw, args)
|
||||
dumpHandler.dump(fd, pw, args)
|
||||
|
||||
// THEN its dump() method is called
|
||||
verify(dumpable1).dump(pw, args)
|
||||
@@ -124,7 +134,7 @@ class DumpHandlerTest : SysuiTestCase() {
|
||||
|
||||
// WHEN a critical dump is requested
|
||||
val args = arrayOf("--dump-priority", "CRITICAL")
|
||||
dumpHandler.dump(pw, args)
|
||||
dumpHandler.dump(fd, pw, args)
|
||||
|
||||
// THEN all modules are dumped (but no buffers)
|
||||
verify(dumpable1).dump(pw, args)
|
||||
@@ -145,7 +155,7 @@ class DumpHandlerTest : SysuiTestCase() {
|
||||
|
||||
// WHEN a normal dump is requested
|
||||
val args = arrayOf("--dump-priority", "NORMAL")
|
||||
dumpHandler.dump(pw, args)
|
||||
dumpHandler.dump(fd, pw, args)
|
||||
|
||||
// THEN all buffers are dumped (but no modules)
|
||||
verify(dumpable1, never()).dump(
|
||||
@@ -168,11 +178,35 @@ class DumpHandlerTest : SysuiTestCase() {
|
||||
val spw = PrintWriter(stringWriter)
|
||||
|
||||
// When a config dump is requested
|
||||
dumpHandler.dump(spw, arrayOf("config"))
|
||||
dumpHandler.dump(fd, spw, arrayOf("config"))
|
||||
|
||||
assertThat(stringWriter.toString()).contains(EmptyCoreStartable::class.java.simpleName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDumpAllProtoDumpables() {
|
||||
dumpManager.registerDumpable("protoDumpable1", protoDumpable1)
|
||||
dumpManager.registerDumpable("protoDumpable2", protoDumpable2)
|
||||
|
||||
val args = arrayOf(DumpHandler.PROTO)
|
||||
dumpHandler.dump(fd, pw, args)
|
||||
|
||||
verify(protoDumpable1).dumpProto(any(), eq(args))
|
||||
verify(protoDumpable2).dumpProto(any(), eq(args))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDumpSingleProtoDumpable() {
|
||||
dumpManager.registerDumpable("protoDumpable1", protoDumpable1)
|
||||
dumpManager.registerDumpable("protoDumpable2", protoDumpable2)
|
||||
|
||||
val args = arrayOf(DumpHandler.PROTO, "protoDumpable1")
|
||||
dumpHandler.dump(fd, pw, args)
|
||||
|
||||
verify(protoDumpable1).dumpProto(any(), eq(args))
|
||||
verify(protoDumpable2, never()).dumpProto(any(), any())
|
||||
}
|
||||
|
||||
private class EmptyCoreStartable : CoreStartable {
|
||||
override fun start() {}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.classifier.FalsingManagerFake;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.dump.nano.SystemUIProtoDump;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.plugins.qs.QSFactory;
|
||||
import com.android.systemui.plugins.qs.QSTile;
|
||||
@@ -114,8 +115,6 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private DumpManager mDumpManager;
|
||||
@Mock
|
||||
private QSTile.State mMockState;
|
||||
@Mock
|
||||
private CentralSurfaces mCentralSurfaces;
|
||||
@Mock
|
||||
private QSLogger mQSLogger;
|
||||
@@ -195,7 +194,6 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
private void setUpTileFactory() {
|
||||
when(mMockState.toString()).thenReturn(MOCK_STATE_STRING);
|
||||
// Only create this kind of tiles
|
||||
when(mDefaultFactory.createTile(anyString())).thenAnswer(
|
||||
invocation -> {
|
||||
@@ -209,7 +207,11 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
} else if ("na".equals(spec)) {
|
||||
return new NotAvailableTile(mQSTileHost);
|
||||
} else if (CUSTOM_TILE_SPEC.equals(spec)) {
|
||||
return mCustomTile;
|
||||
QSTile tile = mCustomTile;
|
||||
QSTile.State s = mock(QSTile.State.class);
|
||||
s.spec = spec;
|
||||
when(mCustomTile.getState()).thenReturn(s);
|
||||
return tile;
|
||||
} else if ("internet".equals(spec)
|
||||
|| "wifi".equals(spec)
|
||||
|| "cell".equals(spec)) {
|
||||
@@ -647,7 +649,7 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void testSetTileRemoved_removedBySystem() {
|
||||
int user = mUserTracker.getUserId();
|
||||
saveSetting("spec1" + CUSTOM_TILE_SPEC);
|
||||
saveSetting("spec1," + CUSTOM_TILE_SPEC);
|
||||
|
||||
// This will be done by TileServiceManager
|
||||
mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
|
||||
@@ -658,6 +660,27 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
.getBoolean(CUSTOM_TILE.flattenToString(), false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProtoDump_noTiles() {
|
||||
SystemUIProtoDump proto = new SystemUIProtoDump();
|
||||
mQSTileHost.dumpProto(proto, new String[0]);
|
||||
|
||||
assertEquals(0, proto.tiles.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTilesInOrder() {
|
||||
saveSetting("spec1," + CUSTOM_TILE_SPEC);
|
||||
|
||||
SystemUIProtoDump proto = new SystemUIProtoDump();
|
||||
mQSTileHost.dumpProto(proto, new String[0]);
|
||||
|
||||
assertEquals(2, proto.tiles.length);
|
||||
assertEquals("spec1", proto.tiles[0].getSpec());
|
||||
assertEquals(CUSTOM_TILE.getPackageName(), proto.tiles[1].getComponentName().packageName);
|
||||
assertEquals(CUSTOM_TILE.getClassName(), proto.tiles[1].getComponentName().className);
|
||||
}
|
||||
|
||||
private SharedPreferences getSharedPreferenecesForUser(int user) {
|
||||
return mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user);
|
||||
}
|
||||
@@ -707,12 +730,9 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
|
||||
@Override
|
||||
public State newTileState() {
|
||||
return mMockState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return mMockState;
|
||||
State s = mock(QSTile.State.class);
|
||||
when(s.toString()).thenReturn(MOCK_STATE_STRING);
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.android.systemui.qs
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.service.quicksettings.Tile
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.plugins.qs.QSTile
|
||||
import com.android.systemui.qs.external.CustomTile
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class TileStateToProtoTest : SysuiTestCase() {
|
||||
|
||||
companion object {
|
||||
private const val TEST_LABEL = "label"
|
||||
private const val TEST_SUBTITLE = "subtitle"
|
||||
private const val TEST_SPEC = "spec"
|
||||
private val TEST_COMPONENT = ComponentName("test_pkg", "test_cls")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun platformTile_INACTIVE() {
|
||||
val state =
|
||||
QSTile.State().apply {
|
||||
spec = TEST_SPEC
|
||||
label = TEST_LABEL
|
||||
secondaryLabel = TEST_SUBTITLE
|
||||
state = Tile.STATE_INACTIVE
|
||||
}
|
||||
val proto = state.toProto()
|
||||
|
||||
assertThat(proto).isNotNull()
|
||||
assertThat(proto?.hasSpec()).isTrue()
|
||||
assertThat(proto?.spec).isEqualTo(TEST_SPEC)
|
||||
assertThat(proto?.hasComponentName()).isFalse()
|
||||
assertThat(proto?.label).isEqualTo(TEST_LABEL)
|
||||
assertThat(proto?.secondaryLabel).isEqualTo(TEST_SUBTITLE)
|
||||
assertThat(proto?.state).isEqualTo(Tile.STATE_INACTIVE)
|
||||
assertThat(proto?.hasBooleanState()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun componentTile_UNAVAILABLE() {
|
||||
val state =
|
||||
QSTile.State().apply {
|
||||
spec = CustomTile.toSpec(TEST_COMPONENT)
|
||||
label = TEST_LABEL
|
||||
secondaryLabel = TEST_SUBTITLE
|
||||
state = Tile.STATE_UNAVAILABLE
|
||||
}
|
||||
val proto = state.toProto()
|
||||
|
||||
assertThat(proto).isNotNull()
|
||||
assertThat(proto?.hasSpec()).isFalse()
|
||||
assertThat(proto?.hasComponentName()).isTrue()
|
||||
val componentName = proto?.componentName
|
||||
assertThat(componentName?.packageName).isEqualTo(TEST_COMPONENT.packageName)
|
||||
assertThat(componentName?.className).isEqualTo(TEST_COMPONENT.className)
|
||||
assertThat(proto?.label).isEqualTo(TEST_LABEL)
|
||||
assertThat(proto?.secondaryLabel).isEqualTo(TEST_SUBTITLE)
|
||||
assertThat(proto?.state).isEqualTo(Tile.STATE_UNAVAILABLE)
|
||||
assertThat(proto?.hasBooleanState()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun booleanState_ACTIVE() {
|
||||
val state =
|
||||
QSTile.BooleanState().apply {
|
||||
spec = TEST_SPEC
|
||||
label = TEST_LABEL
|
||||
secondaryLabel = TEST_SUBTITLE
|
||||
state = Tile.STATE_ACTIVE
|
||||
value = true
|
||||
}
|
||||
val proto = state.toProto()
|
||||
|
||||
assertThat(proto).isNotNull()
|
||||
assertThat(proto?.hasSpec()).isTrue()
|
||||
assertThat(proto?.spec).isEqualTo(TEST_SPEC)
|
||||
assertThat(proto?.hasComponentName()).isFalse()
|
||||
assertThat(proto?.label).isEqualTo(TEST_LABEL)
|
||||
assertThat(proto?.secondaryLabel).isEqualTo(TEST_SUBTITLE)
|
||||
assertThat(proto?.state).isEqualTo(Tile.STATE_ACTIVE)
|
||||
assertThat(proto?.hasBooleanState()).isTrue()
|
||||
assertThat(proto?.booleanState).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun noSpec_returnsNull() {
|
||||
val state =
|
||||
QSTile.State().apply {
|
||||
label = TEST_LABEL
|
||||
secondaryLabel = TEST_SUBTITLE
|
||||
state = Tile.STATE_ACTIVE
|
||||
}
|
||||
val proto = state.toProto()
|
||||
|
||||
assertThat(proto).isNull()
|
||||
}
|
||||
}
|
||||
@@ -2253,6 +2253,25 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
|
||||
boolean proto = false;
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if ("--proto".equals(args[i])) {
|
||||
proto = true;
|
||||
}
|
||||
}
|
||||
if (proto) {
|
||||
if (mBar == null) return;
|
||||
try (TransferPipe tp = new TransferPipe()) {
|
||||
// Sending the command to the remote, which needs to execute async to avoid blocking
|
||||
// See Binder#dumpAsync() for inspiration
|
||||
mBar.dumpProto(args, tp.getWriteFd());
|
||||
// Times out after 5s
|
||||
tp.go(fd);
|
||||
} catch (Throwable t) {
|
||||
Slog.e(TAG, "Error sending command to IStatusBar", t);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
for (int i = 0; i < mDisplayUiState.size(); i++) {
|
||||
|
||||
@@ -1823,7 +1823,8 @@ public final class SystemServer implements Dumpable {
|
||||
t.traceBegin("StartStatusBarManagerService");
|
||||
try {
|
||||
statusBar = new StatusBarManagerService(context);
|
||||
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
|
||||
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar, false,
|
||||
DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
|
||||
} catch (Throwable e) {
|
||||
reportWtf("starting StatusBarManagerService", e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user