Merge "Creates a generic mechanism to dump app-side information."

This commit is contained in:
Felipe Leme
2022-01-14 17:43:28 +00:00
committed by Android (Google) Code Review
8 changed files with 322 additions and 4 deletions

View File

@@ -47167,6 +47167,15 @@ package android.util {
field public float ydpi;
}
public interface Dumpable {
method public void dump(@NonNull java.io.PrintWriter, @Nullable String[]);
method @NonNull public default String getDumpableName();
}
public interface DumpableContainer {
method public boolean addDumpable(@NonNull android.util.Dumpable);
}
public class EventLog {
method public static int getTagCode(String);
method public static String getTagName(int);

View File

@@ -9,6 +9,10 @@ package android {
package android.app {
@UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
method public final boolean addDumpable(@NonNull android.util.Dumpable);
}
public class ActivityManager {
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);

View File

@@ -90,6 +90,7 @@ import android.transition.Scene;
import android.transition.TransitionManager;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Dumpable;
import android.util.EventLog;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -145,6 +146,7 @@ import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.util.dump.DumpableContainerImpl;
import dalvik.system.VMRuntime;
@@ -954,6 +956,9 @@ public class Activity extends ContextThemeWrapper
private SplashScreen mSplashScreen;
@Nullable
private DumpableContainerImpl mDumpableContainer;
private final WindowControllerCallback mWindowControllerCallback =
new WindowControllerCallback() {
/**
@@ -7081,8 +7086,23 @@ public class Activity extends ContextThemeWrapper
dumpInner(prefix, fd, writer, args);
}
/**
* See {@link android.util.DumpableContainer#addDumpable(Dumpable)}.
*
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final boolean addDumpable(@NonNull Dumpable dumpable) {
if (mDumpableContainer == null) {
mDumpableContainer = new DumpableContainerImpl();
}
return mDumpableContainer.addDumpable(dumpable);
}
void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd,
@NonNull PrintWriter writer, @Nullable String[] args) {
String innerPrefix = prefix + " ";
if (args != null && args.length > 0) {
// Handle special cases
switch (args[0]) {
@@ -7095,12 +7115,33 @@ public class Activity extends ContextThemeWrapper
case "--translation":
dumpUiTranslation(prefix, writer);
return;
case "--list-dumpables":
if (mDumpableContainer == null) {
writer.print(prefix); writer.println("No dumpables");
return;
}
mDumpableContainer.listDumpables(prefix, writer);
return;
case "--dump-dumpable":
if (args.length == 1) {
writer.println("--dump-dumpable requires the dumpable name");
return;
}
if (mDumpableContainer == null) {
writer.println("no dumpables");
return;
}
// Strips --dump-dumpable NAME
String[] prunedArgs = new String[args.length - 2];
System.arraycopy(args, 2, prunedArgs, 0, prunedArgs.length);
mDumpableContainer.dumpOneDumpable(prefix, writer, args[1], prunedArgs);
return;
}
}
writer.print(prefix); writer.print("Local Activity ");
writer.print(Integer.toHexString(System.identityHashCode(this)));
writer.println(" State:");
String innerPrefix = prefix + " ";
writer.print(innerPrefix); writer.print("mResumed=");
writer.print(mResumed); writer.print(" mStopped=");
writer.print(mStopped); writer.print(" mFinished=");
@@ -7138,6 +7179,10 @@ public class Activity extends ContextThemeWrapper
dumpUiTranslation(prefix, writer);
ResourcesManager.getInstance().dump(prefix, writer);
if (mDumpableContainer != null) {
mDumpableContainer.dumpAllDumpables(prefix, writer, args);
}
}
void dumpContentCaptureManager(String prefix, PrintWriter writer) {

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2021 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 android.util;
import android.annotation.NonNull;
import android.annotation.Nullable;
import java.io.PrintWriter;
/**
* Represents an object whose state can be dumped into a {@link PrintWriter}.
*/
public interface Dumpable {
/**
* Gets the name of the {@link Dumpable}.
*
* @return class name, by default.
*/
@NonNull
default String getDumpableName() {
return getClass().getName();
}
//TODO(b/149254050): decide whether it should take a ParcelFileDescription as well.
/**
* Dumps the internal state into the given {@code writer}.
*
* @param writer writer to be written to
* @param args optional list of arguments
*/
void dump(@NonNull PrintWriter writer, @Nullable String[] args);
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2021 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 android.util;
import android.annotation.NonNull;
/**
* Objects that contains a list of {@link Dumpable}, which will be dumped when the object itself
* is dumped.
*/
public interface DumpableContainer {
/**
* Adds the given {@link Dumpable dumpable} to the container.
*
* <p>If a dumpable with the same {@link Dumpable#getDumpableName() name} was added before, this
* call is ignored.
*
* @param dumpable dumpable to be added.
*
* @throws IllegalArgumentException if the {@link Dumpable#getDumpableName() dumpable name} is
* {@code null}.
*
* @return {@code true} if the dumpable was added, {@code false} if the call was ignored.
*/
boolean addDumpable(@NonNull Dumpable dumpable);
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2021 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.internal.util.dump;
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.Dumpable;
import android.util.DumpableContainer;
import android.util.IndentingPrintWriter;
import android.util.Log;
import java.io.PrintWriter;
import java.util.Objects;
// TODO(b/149254050): add unit tests
/**
* Helper class for {@link DumpableContainer} implementations - they can "implement it by
* association", i.e., by delegating the interface methods to a {@code DumpableContainerImpl}.
*
* @hide
*/
public final class DumpableContainerImpl implements DumpableContainer {
private static final String TAG = DumpableContainerImpl.class.getSimpleName();
private static final boolean DEBUG = false;
@Nullable
private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>();
@Override
public boolean addDumpable(Dumpable dumpable) {
Objects.requireNonNull(dumpable, "dumpable");
String name = dumpable.getDumpableName();
Objects.requireNonNull(name, () -> "name of" + dumpable);
if (mDumpables.containsKey(name)) {
Log.e(TAG, "addDumpable(): ignoring " + dumpable + " as there is already a dumpable"
+ " with that name (" + name + "): " + mDumpables.get(name));
return false;
}
if (DEBUG) {
Log.d(TAG, "Adding " + name + " -> " + dumpable);
}
mDumpables.put(name, dumpable);
return true;
}
/**
* Dumps the number of dumpable, without a newline.
*/
private int dumpNumberDumpables(IndentingPrintWriter writer) {
int size = mDumpables == null ? 0 : mDumpables.size();
if (size == 0) {
writer.print("No dumpables");
} else {
writer.print(size); writer.print(" dumpables");
}
return size;
}
/**
* Lists the name of all dumpables to the given {@code writer}.
*/
public void listDumpables(String prefix, PrintWriter writer) {
IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
int size = dumpNumberDumpables(ipw);
if (size == 0) {
ipw.println();
return;
}
ipw.print(": ");
for (int i = 0; i < size; i++) {
ipw.print(mDumpables.keyAt(i));
if (i < size - 1) ipw.print(' ');
}
ipw.println();
}
/**
* Dumps the content of all dumpables to the given {@code writer}.
*/
public void dumpAllDumpables(String prefix, PrintWriter writer, String[] args) {
IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
int size = dumpNumberDumpables(ipw);
if (size == 0) {
ipw.println();
return;
}
ipw.println(": ");
for (int i = 0; i < size; i++) {
String dumpableName = mDumpables.keyAt(i);
ipw.print('#'); ipw.print(i); ipw.print(": "); ipw.println(dumpableName);
Dumpable dumpable = mDumpables.valueAt(i);
indentAndDump(ipw, dumpable, args);
}
}
private void indentAndDump(IndentingPrintWriter writer, Dumpable dumpable, String[] args) {
writer.increaseIndent();
try {
dumpable.dump(writer, args);
} finally {
writer.decreaseIndent();
}
}
/**
* Dumps the content of a specific dumpable to the given {@code writer}.
*/
@SuppressWarnings("resource") // cannot close ipw as it would close writer
public void dumpOneDumpable(String prefix, PrintWriter writer, String dumpableName,
String[] args) {
IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
Dumpable dumpable = mDumpables.get(dumpableName);
if (dumpable == null) {
ipw.print("No "); ipw.println(dumpableName);
return;
}
ipw.print(dumpableName); ipw.println(':');
indentAndDump(ipw, dumpable, args);
}
}

View File

@@ -32,6 +32,9 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Dumpable;
import android.util.DumpableContainer;
import android.util.Log;
import android.util.TimingsTraceLog;
import android.view.SurfaceControl;
@@ -53,13 +56,19 @@ import java.util.Collections;
* Application class for SystemUI.
*/
public class SystemUIApplication extends Application implements
SystemUIAppComponentFactory.ContextInitializer {
SystemUIAppComponentFactory.ContextInitializer, DumpableContainer {
public static final String TAG = "SystemUIService";
private static final boolean DEBUG = false;
private ContextComponentHelper mComponentHelper;
private BootCompleteCacheImpl mBootCompleteCache;
private DumpManager mDumpManager;
/**
* Map of dumpables added externally.
*/
private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>();
/**
* Hold a reference on the stuff we start.
@@ -214,7 +223,7 @@ public class SystemUIApplication extends Application implements
}
}
final DumpManager dumpManager = mSysUIComponent.createDumpManager();
mDumpManager = mSysUIComponent.createDumpManager();
Log.v(TAG, "Starting SystemUI services for user " +
Process.myUserHandle().getIdentifier() + ".");
@@ -255,7 +264,7 @@ public class SystemUIApplication extends Application implements
mServices[i].onBootCompleted();
}
dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
mDumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
}
mSysUIComponent.getInitController().executePostInitTasks();
log.traceEnd();
@@ -263,6 +272,29 @@ public class SystemUIApplication extends Application implements
mServicesStarted = true;
}
// TODO(b/149254050): add unit tests? There doesn't seem to be a SystemUiApplicationTest...
@Override
public boolean addDumpable(Dumpable dumpable) {
String name = dumpable.getDumpableName();
if (mDumpables.containsKey(name)) {
// This is normal because SystemUIApplication is an application context that is shared
// among multiple components
if (DEBUG) {
Log.d(TAG, "addDumpable(): ignoring " + dumpable + " as there is already a dumpable"
+ " with that name (" + name + "): " + mDumpables.get(name));
}
return false;
}
if (DEBUG) Log.d(TAG, "addDumpable(): adding '" + name + "' = " + dumpable);
mDumpables.put(name, dumpable);
// TODO(b/149254050): replace com.android.systemui.dump.Dumpable by
// com.android.util.Dumpable and get rid of the intermediate lambda
mDumpManager.registerDumpable(dumpable.getDumpableName(),
(fd, pw, args) -> dumpable.dump(pw, args));
return true;
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (mServicesStarted) {

View File

@@ -24,6 +24,8 @@ import android.util.IndentingPrintWriter;
*
* <p>See {@link SystemServer.SystemServerDumper} for usage example.
*/
// TODO(b/149254050): replace / merge with package android.util.Dumpable (it would require
// exporting IndentingPrintWriter as @SystemApi) and/or changing the method to use a prefix
public interface Dumpable {
/**