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. */
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
* @param fd A file descriptor.
* @param pw Where to write your dump to.
* @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};
}
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_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)

View File

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

View File

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

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.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]);
}
}

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.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

View File

@@ -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);
}
/**

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

View File

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

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) {
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++) {

View File

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