Merge "Proto dump from SystemUI." into tm-qpr-dev

This commit is contained in:
Fabian Kozynski
2022-10-21 19:22:55 +00:00
committed by Android (Google) Code Review
19 changed files with 514 additions and 37 deletions

View File

@@ -322,4 +322,7 @@ oneway interface IStatusBar
/** Unregisters a nearby media devices provider. */ /** Unregisters a nearby media devices provider. */
void unregisterNearbyMediaDevicesProvider(in INearbyMediaDevicesProvider provider); void unregisterNearbyMediaDevicesProvider(in INearbyMediaDevicesProvider provider);
/** Dump protos from SystemUI. The proto definition is defined there */
void dumpProto(in String[] args, in ParcelFileDescriptor pfd);
} }

View File

@@ -30,7 +30,6 @@ public interface Dumpable {
/** /**
* Called when it's time to dump the internal state * Called when it's time to dump the internal state
* @param fd A file descriptor.
* @param pw Where to write your dump to. * @param pw Where to write your dump to.
* @param args Arguments. * @param args Arguments.
*/ */

View File

@@ -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>)
}

View File

@@ -121,6 +121,6 @@ public class SystemUIService extends Service {
DumpHandler.PRIORITY_ARG_CRITICAL}; DumpHandler.PRIORITY_ARG_CRITICAL};
} }
mDumpHandler.dump(pw, massagedArgs); mDumpHandler.dump(fd, pw, massagedArgs);
} }
} }

View File

@@ -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_CRITICAL
import com.android.systemui.dump.DumpHandler.Companion.PRIORITY_ARG_HIGH 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.DumpHandler.Companion.PRIORITY_ARG_NORMAL
import com.android.systemui.dump.nano.SystemUIProtoDump
import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager 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 java.io.PrintWriter
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider import javax.inject.Provider
@@ -100,7 +105,7 @@ class DumpHandler @Inject constructor(
/** /**
* Dump the diagnostics! Behavior can be controlled via [args]. * 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()") Trace.beginSection("DumpManager#dump()")
val start = SystemClock.uptimeMillis() val start = SystemClock.uptimeMillis()
@@ -111,10 +116,12 @@ class DumpHandler @Inject constructor(
return return
} }
when (parsedArgs.dumpPriority) { when {
PRIORITY_ARG_CRITICAL -> dumpCritical(pw, parsedArgs) parsedArgs.dumpPriority == PRIORITY_ARG_CRITICAL -> dumpCritical(pw, parsedArgs)
PRIORITY_ARG_NORMAL -> dumpNormal(pw, parsedArgs) parsedArgs.dumpPriority == PRIORITY_ARG_NORMAL && !parsedArgs.proto -> {
else -> dumpParameterized(pw, parsedArgs) dumpNormal(pw, parsedArgs)
}
else -> dumpParameterized(fd, pw, parsedArgs)
} }
pw.println() pw.println()
@@ -122,7 +129,7 @@ class DumpHandler @Inject constructor(
Trace.endSection() Trace.endSection()
} }
private fun dumpParameterized(pw: PrintWriter, args: ParsedArgs) { private fun dumpParameterized(fd: FileDescriptor, pw: PrintWriter, args: ParsedArgs) {
when (args.command) { when (args.command) {
"bugreport-critical" -> dumpCritical(pw, args) "bugreport-critical" -> dumpCritical(pw, args)
"bugreport-normal" -> dumpNormal(pw, args) "bugreport-normal" -> dumpNormal(pw, args)
@@ -130,7 +137,13 @@ class DumpHandler @Inject constructor(
"buffers" -> dumpBuffers(pw, args) "buffers" -> dumpBuffers(pw, args)
"config" -> dumpConfig(pw) "config" -> dumpConfig(pw)
"help" -> dumpHelp(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( private fun dumpTargets(
targets: List<String>, targets: List<String>,
pw: PrintWriter, pw: PrintWriter,
@@ -267,6 +300,7 @@ class DumpHandler @Inject constructor(
} }
} }
} }
PROTO -> pArgs.proto = true
"-t", "--tail" -> { "-t", "--tail" -> {
pArgs.tailLength = readArgument(iterator, arg) { pArgs.tailLength = readArgument(iterator, arg) {
it.toInt() it.toInt()
@@ -278,6 +312,9 @@ class DumpHandler @Inject constructor(
"-h", "--help" -> { "-h", "--help" -> {
pArgs.command = "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 -> { else -> {
throw ArgParseException("Unknown flag: $arg") throw ArgParseException("Unknown flag: $arg")
} }
@@ -314,7 +351,7 @@ class DumpHandler @Inject constructor(
const val PRIORITY_ARG_CRITICAL = "CRITICAL" const val PRIORITY_ARG_CRITICAL = "CRITICAL"
const val PRIORITY_ARG_HIGH = "HIGH" const val PRIORITY_ARG_HIGH = "HIGH"
const val PRIORITY_ARG_NORMAL = "NORMAL" 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 tailLength: Int = 0
var command: String? = null var command: String? = null
var listOnly = false var listOnly = false
var proto = false
} }
class ArgParseException(message: String) : Exception(message) class ArgParseException(message: String) : Exception(message)

View File

@@ -18,6 +18,8 @@ package com.android.systemui.dump
import android.util.ArrayMap import android.util.ArrayMap
import com.android.systemui.Dumpable 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 com.android.systemui.plugins.log.LogBuffer
import java.io.PrintWriter import java.io.PrintWriter
import javax.inject.Inject import javax.inject.Inject
@@ -90,7 +92,7 @@ open class DumpManager @Inject constructor() {
target: String, target: String,
pw: PrintWriter, pw: PrintWriter,
args: Array<String>, args: Array<String>,
tailLength: Int tailLength: Int,
) { ) {
for (dumpable in dumpables.values) { for (dumpable in dumpables.values) {
if (dumpable.name.endsWith(target)) { 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] * Dumps all registered dumpables to [pw]
*/ */
@@ -184,6 +216,14 @@ open class DumpManager @Inject constructor() {
buffer.dumpable.dump(pw, tailLength) 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 { private fun canAssignToNameLocked(name: String, newDumpable: Any): Boolean {
val existingDumpable = dumpables[name]?.dumpable ?: buffers[name]?.dumpable val existingDumpable = dumpables[name]?.dumpable ?: buffers[name]?.dumpable
return existingDumpable == null || newDumpable == existingDumpable return existingDumpable == null || newDumpable == existingDumpable

View File

@@ -51,6 +51,7 @@ public class SystemUIAuxiliaryDumpService extends Service {
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
// Simulate the NORMAL priority arg being passed to us // Simulate the NORMAL priority arg being passed to us
mDumpHandler.dump( mDumpHandler.dump(
fd,
pw, pw,
new String[] { DumpHandler.PRIORITY_ARG, DumpHandler.PRIORITY_ARG_NORMAL }); new String[] { DumpHandler.PRIORITY_ARG, DumpHandler.PRIORITY_ARG_NORMAL });
} }

View 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;
}

View File

@@ -34,10 +34,12 @@ import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence; import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable; import com.android.systemui.Dumpable;
import com.android.systemui.ProtoDumpable;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.nano.SystemUIProtoDump;
import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile; 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.TileServiceKey;
import com.android.systemui.qs.external.TileServiceRequestController; import com.android.systemui.qs.external.TileServiceRequestController;
import com.android.systemui.qs.logging.QSLogger; 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.UserFileManager;
import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager; 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.leak.GarbageMonitor;
import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.settings.SecureSettings;
import org.jetbrains.annotations.NotNull;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Provider; import javax.inject.Provider;
@@ -82,7 +89,7 @@ import javax.inject.Provider;
* This class also provides the interface for adding/removing/changing tiles. * This class also provides the interface for adding/removing/changing tiles.
*/ */
@SysUISingleton @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 String TAG = "QSTileHost";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int MAX_QS_INSTANCE_ID = 1 << 20; 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) mTiles.values().stream().filter(obj -> obj instanceof Dumpable)
.forEach(o -> ((Dumpable) o).dump(pw, args)); .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]);
}
} }

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -69,12 +69,15 @@ import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.GcUtils; import com.android.internal.util.GcUtils;
import com.android.internal.view.AppearanceRegion; import com.android.internal.view.AppearanceRegion;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.commandline.CommandRegistry; import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.policy.CallbackController; import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tracing.ProtoTracer;
import java.io.FileDescriptor;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
@@ -184,6 +187,7 @@ public class CommandQueue extends IStatusBar.Stub implements
private int mLastUpdatedImeDisplayId = INVALID_DISPLAY; private int mLastUpdatedImeDisplayId = INVALID_DISPLAY;
private ProtoTracer mProtoTracer; private ProtoTracer mProtoTracer;
private final @Nullable CommandRegistry mRegistry; private final @Nullable CommandRegistry mRegistry;
private final @Nullable DumpHandler mDumpHandler;
/** /**
* These methods are called back on the main thread. * These methods are called back on the main thread.
@@ -473,12 +477,18 @@ public class CommandQueue extends IStatusBar.Stub implements
} }
public CommandQueue(Context context) { 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; mProtoTracer = protoTracer;
mRegistry = registry; mRegistry = registry;
mDumpHandler = dumpHandler;
context.getSystemService(DisplayManager.class).registerDisplayListener(this, mHandler); context.getSystemService(DisplayManager.class).registerDisplayListener(this, mHandler);
// We always have default display. // We always have default display.
setDisabled(DEFAULT_DISPLAY, DISABLE_NONE, DISABLE2_NONE); setDisabled(DEFAULT_DISPLAY, DISABLE_NONE, DISABLE2_NONE);
@@ -1177,6 +1187,35 @@ public class CommandQueue extends IStatusBar.Stub implements
thr.start(); 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 @Override
public void runGcForTest() { public void runGcForTest() {
// Gc sysui // Gc sysui

View File

@@ -29,6 +29,7 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaDataManager;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
@@ -181,8 +182,10 @@ public interface CentralSurfacesDependenciesModule {
static CommandQueue provideCommandQueue( static CommandQueue provideCommandQueue(
Context context, Context context,
ProtoTracer protoTracer, ProtoTracer protoTracer,
CommandRegistry registry) { CommandRegistry registry,
return new CommandQueue(context, protoTracer, registry); DumpHandler dumpHandler
) {
return new CommandQueue(context, protoTracer, registry, dumpHandler);
} }
/** /**

View File

@@ -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;
}

View File

@@ -19,11 +19,17 @@ package com.android.systemui.dump
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.CoreStartable import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable import com.android.systemui.Dumpable
import com.android.systemui.ProtoDumpable
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager
import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.google.common.truth.Truth.assertThat 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.Before
import org.junit.Test import org.junit.Test
import org.mockito.Mock import org.mockito.Mock
@@ -31,9 +37,6 @@ import org.mockito.Mockito.anyInt
import org.mockito.Mockito.never import org.mockito.Mockito.never
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import java.io.PrintWriter
import java.io.StringWriter
import javax.inject.Provider
@SmallTest @SmallTest
class DumpHandlerTest : SysuiTestCase() { class DumpHandlerTest : SysuiTestCase() {
@@ -47,6 +50,8 @@ class DumpHandlerTest : SysuiTestCase() {
@Mock @Mock
private lateinit var pw: PrintWriter private lateinit var pw: PrintWriter
@Mock
private lateinit var fd: FileDescriptor
@Mock @Mock
private lateinit var dumpable1: Dumpable private lateinit var dumpable1: Dumpable
@@ -55,6 +60,11 @@ class DumpHandlerTest : SysuiTestCase() {
@Mock @Mock
private lateinit var dumpable3: Dumpable private lateinit var dumpable3: Dumpable
@Mock
private lateinit var protoDumpable1: ProtoDumpable
@Mock
private lateinit var protoDumpable2: ProtoDumpable
@Mock @Mock
private lateinit var buffer1: LogBuffer private lateinit var buffer1: LogBuffer
@Mock @Mock
@@ -88,7 +98,7 @@ class DumpHandlerTest : SysuiTestCase() {
// WHEN some of them are dumped explicitly // WHEN some of them are dumped explicitly
val args = arrayOf("dumpable1", "dumpable3", "buffer2") 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 // THEN only the requested ones have their dump() method called
verify(dumpable1).dump(pw, args) verify(dumpable1).dump(pw, args)
@@ -107,7 +117,7 @@ class DumpHandlerTest : SysuiTestCase() {
// WHEN that module is dumped // WHEN that module is dumped
val args = arrayOf("dumpable1") val args = arrayOf("dumpable1")
dumpHandler.dump(pw, args) dumpHandler.dump(fd, pw, args)
// THEN its dump() method is called // THEN its dump() method is called
verify(dumpable1).dump(pw, args) verify(dumpable1).dump(pw, args)
@@ -124,7 +134,7 @@ class DumpHandlerTest : SysuiTestCase() {
// WHEN a critical dump is requested // WHEN a critical dump is requested
val args = arrayOf("--dump-priority", "CRITICAL") val args = arrayOf("--dump-priority", "CRITICAL")
dumpHandler.dump(pw, args) dumpHandler.dump(fd, pw, args)
// THEN all modules are dumped (but no buffers) // THEN all modules are dumped (but no buffers)
verify(dumpable1).dump(pw, args) verify(dumpable1).dump(pw, args)
@@ -145,7 +155,7 @@ class DumpHandlerTest : SysuiTestCase() {
// WHEN a normal dump is requested // WHEN a normal dump is requested
val args = arrayOf("--dump-priority", "NORMAL") val args = arrayOf("--dump-priority", "NORMAL")
dumpHandler.dump(pw, args) dumpHandler.dump(fd, pw, args)
// THEN all buffers are dumped (but no modules) // THEN all buffers are dumped (but no modules)
verify(dumpable1, never()).dump( verify(dumpable1, never()).dump(
@@ -168,11 +178,35 @@ class DumpHandlerTest : SysuiTestCase() {
val spw = PrintWriter(stringWriter) val spw = PrintWriter(stringWriter)
// When a config dump is requested // 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) 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 { private class EmptyCoreStartable : CoreStartable {
override fun start() {} override fun start() {}
} }

View File

@@ -52,6 +52,7 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.nano.SystemUIProtoDump;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile;
@@ -114,8 +115,6 @@ public class QSTileHostTest extends SysuiTestCase {
@Mock @Mock
private DumpManager mDumpManager; private DumpManager mDumpManager;
@Mock @Mock
private QSTile.State mMockState;
@Mock
private CentralSurfaces mCentralSurfaces; private CentralSurfaces mCentralSurfaces;
@Mock @Mock
private QSLogger mQSLogger; private QSLogger mQSLogger;
@@ -195,7 +194,6 @@ public class QSTileHostTest extends SysuiTestCase {
} }
private void setUpTileFactory() { private void setUpTileFactory() {
when(mMockState.toString()).thenReturn(MOCK_STATE_STRING);
// Only create this kind of tiles // Only create this kind of tiles
when(mDefaultFactory.createTile(anyString())).thenAnswer( when(mDefaultFactory.createTile(anyString())).thenAnswer(
invocation -> { invocation -> {
@@ -209,7 +207,11 @@ public class QSTileHostTest extends SysuiTestCase {
} else if ("na".equals(spec)) { } else if ("na".equals(spec)) {
return new NotAvailableTile(mQSTileHost); return new NotAvailableTile(mQSTileHost);
} else if (CUSTOM_TILE_SPEC.equals(spec)) { } 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) } else if ("internet".equals(spec)
|| "wifi".equals(spec) || "wifi".equals(spec)
|| "cell".equals(spec)) { || "cell".equals(spec)) {
@@ -647,7 +649,7 @@ public class QSTileHostTest extends SysuiTestCase {
@Test @Test
public void testSetTileRemoved_removedBySystem() { public void testSetTileRemoved_removedBySystem() {
int user = mUserTracker.getUserId(); int user = mUserTracker.getUserId();
saveSetting("spec1" + CUSTOM_TILE_SPEC); saveSetting("spec1," + CUSTOM_TILE_SPEC);
// This will be done by TileServiceManager // This will be done by TileServiceManager
mQSTileHost.setTileAdded(CUSTOM_TILE, user, true); mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
@@ -658,6 +660,27 @@ public class QSTileHostTest extends SysuiTestCase {
.getBoolean(CUSTOM_TILE.flattenToString(), false)); .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) { private SharedPreferences getSharedPreferenecesForUser(int user) {
return mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user); return mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user);
} }
@@ -707,12 +730,9 @@ public class QSTileHostTest extends SysuiTestCase {
@Override @Override
public State newTileState() { public State newTileState() {
return mMockState; State s = mock(QSTile.State.class);
} when(s.toString()).thenReturn(MOCK_STATE_STRING);
return s;
@Override
public State getState() {
return mMockState;
} }
@Override @Override

View File

@@ -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()
}
}

View File

@@ -2253,6 +2253,25 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 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) { synchronized (mLock) {
for (int i = 0; i < mDisplayUiState.size(); i++) { for (int i = 0; i < mDisplayUiState.size(); i++) {

View File

@@ -1823,7 +1823,8 @@ public final class SystemServer implements Dumpable {
t.traceBegin("StartStatusBarManagerService"); t.traceBegin("StartStatusBarManagerService");
try { try {
statusBar = new StatusBarManagerService(context); 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) { } catch (Throwable e) {
reportWtf("starting StatusBarManagerService", e); reportWtf("starting StatusBarManagerService", e);
} }