diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java b/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java new file mode 100644 index 0000000000000..7f047f8821226 --- /dev/null +++ b/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.soundtrigger_middleware; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.Map; + +/** + * A collection of pretty-print utilities for data objects. + */ +class ObjectPrinter { + /** Default maximum elements to print in a collection. */ + static public final int kDefaultMaxCollectionLength = 16; + + /** + * Simple version of {@link #print(Object, boolean, int)} that prints an object, without + * recursing into sub-objects. + * + * @param obj The object to print. + * @return A string representing the object. + */ + static String print(@Nullable Object obj) { + return print(obj, false, kDefaultMaxCollectionLength); + } + + /** + * Pretty-prints an object. + * + * @param obj The object to print. + * @param deep Whether to pretty-print sub-objects (if false, just prints them + * with {@link Object#toString()}). + * @param maxCollectionLength Whenever encountering collections, maximum number of elements to + * print. + * @return A string representing the object. + */ + static String print(@Nullable Object obj, boolean deep, int maxCollectionLength) { + StringBuilder builder = new StringBuilder(); + print(builder, obj, deep, maxCollectionLength); + return builder.toString(); + } + + /** + * This version is suitable for use inside a toString() override of an object, e.g.: + *
+ * class MyObject {
+ * ...
+ * @Override
+ * String toString() {
+ * return ObjectPrinter.printPublicFields(this, ...);
+ * }
+ * }
+ *
+ *
+ * @param obj The object to print.
+ * @param deep Whether to pretty-print sub-objects (if false, just prints them
+ * with {@link Object#toString()}).
+ * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
+ * print.
+ */
+ static String printPublicFields(@Nullable Object obj, boolean deep, int maxCollectionLength) {
+ StringBuilder builder = new StringBuilder();
+ printPublicFields(builder, obj, deep, maxCollectionLength);
+ return builder.toString();
+ }
+
+ /**
+ * A version of {@link #print(Object, boolean, int)} that uses a {@link StringBuilder}.
+ *
+ * @param builder StringBuilder to print into.
+ * @param obj The object to print.
+ * @param deep Whether to pretty-print sub-objects (if false, just prints them
+ * with {@link Object#toString()}).
+ * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
+ * print.
+ */
+ static void print(@NonNull StringBuilder builder, @Nullable Object obj, boolean deep,
+ int maxCollectionLength) {
+ try {
+ if (obj == null) {
+ builder.append("null");
+ return;
+ }
+ if (obj instanceof Boolean) {
+ builder.append(obj.toString());
+ return;
+ }
+ if (obj instanceof Number) {
+ builder.append(obj.toString());
+ return;
+ }
+ if (obj instanceof Character) {
+ builder.append('\'');
+ builder.append(obj.toString());
+ builder.append('\'');
+ return;
+ }
+ if (obj instanceof String) {
+ builder.append('"');
+ builder.append(obj.toString());
+ builder.append('"');
+ return;
+ }
+
+ Class cls = obj.getClass();
+
+ if (Collection.class.isAssignableFrom(cls)) {
+ Collection collection = (Collection) obj;
+ builder.append("[ ");
+ int length = collection.size();
+ boolean isLong = false;
+ int i = 0;
+ for (Object child : collection) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ if (i >= maxCollectionLength) {
+ isLong = true;
+ break;
+ }
+ print(builder, child, deep, maxCollectionLength);
+ ++i;
+ }
+ if (isLong) {
+ builder.append("... (+");
+ builder.append(length - maxCollectionLength);
+ builder.append(" entries)");
+ }
+ builder.append(" ]");
+ return;
+ }
+
+ if (Map.class.isAssignableFrom(cls)) {
+ Map, ?> map = (Map, ?>) obj;
+ builder.append("< ");
+ int length = map.size();
+ boolean isLong = false;
+ int i = 0;
+ for (Map.Entry, ?> child : map.entrySet()) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ if (i >= maxCollectionLength) {
+ isLong = true;
+ break;
+ }
+ print(builder, child.getKey(), deep, maxCollectionLength);
+ builder.append(": ");
+ print(builder, child.getValue(), deep, maxCollectionLength);
+ ++i;
+ }
+ if (isLong) {
+ builder.append("... (+");
+ builder.append(length - maxCollectionLength);
+ builder.append(" entries)");
+ }
+ builder.append(" >");
+ return;
+ }
+
+ if (cls.isArray()) {
+ builder.append("[ ");
+ int length = Array.getLength(obj);
+ boolean isLong = false;
+ for (int i = 0; i < length; ++i) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ if (i >= maxCollectionLength) {
+ isLong = true;
+ break;
+ }
+ print(builder, Array.get(obj, i), deep, maxCollectionLength);
+ }
+ if (isLong) {
+ builder.append("... (+");
+ builder.append(length - maxCollectionLength);
+ builder.append(" entries)");
+ }
+ builder.append(" ]");
+ return;
+ }
+
+ if (!deep) {
+ builder.append(obj.toString());
+ return;
+ }
+ printPublicFields(builder, obj, deep, maxCollectionLength);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * A version of {@link #printPublicFields(Object, boolean, int)} that uses a {@link
+ * StringBuilder}.
+ *
+ * @param obj The object to print.
+ * @param deep Whether to pretty-print sub-objects (if false, just prints them
+ * with {@link Object#toString()}).
+ * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
+ * print.
+ */
+ static void printPublicFields(@NonNull StringBuilder builder, @Nullable Object obj,
+ boolean deep,
+ int maxCollectionLength) {
+ try {
+ Class cls = obj.getClass();
+ builder.append("{ ");
+
+ boolean first = true;
+ for (Field fld : cls.getDeclaredFields()) {
+ int mod = fld.getModifiers();
+ if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.STATIC) == 0) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(", ");
+ }
+ builder.append(fld.getName());
+ builder.append(": ");
+ print(builder, fld.get(obj), deep, maxCollectionLength);
+ }
+ }
+ builder.append(" }");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index 7cb7bf4db7ea5..fa78cb0931c27 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -17,7 +17,7 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
-import android.content.Context;
+import android.annotation.Nullable;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
@@ -30,6 +30,7 @@ import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
@@ -38,7 +39,6 @@ import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
-import java.util.Objects;
/**
* An ISoundTriggerMiddlewareService decorator, which adds logging of all API calls (and
@@ -365,43 +365,65 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareSer
}
}
- static private String formatArgs(Object[] args) {
- String result = Arrays.toString(args);
- // Strip the square brackets.
- return result.substring(1, result.length() - 1);
+ private static String printArgs(@NonNull Object[] args) {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < args.length; ++i) {
+ if (i > 0) {
+ result.append(", ");
+ }
+ printObject(result, args[i]);
+ }
+ return result.toString();
}
- private void logReturnWithObject(Object object, String methodName, Object retVal,
- Object[] args) {
+ private static void printObject(@NonNull StringBuilder builder, @Nullable Object obj) {
+ if (obj instanceof Parcelable) {
+ ObjectPrinter.print(builder, obj, true, 16);
+ } else {
+ builder.append(obj.toString());
+ }
+ }
+
+ private static String printObject(@Nullable Object obj) {
+ StringBuilder builder = new StringBuilder();
+ printObject(builder, obj);
+ return builder.toString();
+ }
+
+ private void logReturnWithObject(@NonNull Object object, String methodName,
+ @Nullable Object retVal,
+ @NonNull Object[] args) {
final String message = String.format("%s[this=%s, caller=%d/%d](%s) -> %s", methodName,
- Objects.toString(object),
+ object,
Binder.getCallingUid(), Binder.getCallingPid(),
- formatArgs(args),
- Objects.toString(retVal));
+ printArgs(args),
+ printObject(retVal));
Log.i(TAG, message);
appendMessage(message);
}
- private void logVoidReturnWithObject(Object object, String methodName, Object[] args) {
+ private void logVoidReturnWithObject(@NonNull Object object, @NonNull String methodName,
+ @NonNull Object[] args) {
final String message = String.format("%s[this=%s, caller=%d/%d](%s)", methodName,
- Objects.toString(object),
+ object,
Binder.getCallingUid(), Binder.getCallingPid(),
- formatArgs(args));
+ printArgs(args));
Log.i(TAG, message);
appendMessage(message);
}
- private void logExceptionWithObject(Object object, String methodName, Exception ex,
+ private void logExceptionWithObject(@NonNull Object object, @NonNull String methodName,
+ @NonNull Exception ex,
Object[] args) {
final String message = String.format("%s[this=%s, caller=%d/%d](%s) threw", methodName,
- Objects.toString(object),
+ object,
Binder.getCallingUid(), Binder.getCallingPid(),
- formatArgs(args));
+ printArgs(args));
Log.e(TAG, message, ex);
appendMessage(message + " " + ex.toString());
}
- private void appendMessage(String message) {
+ private void appendMessage(@NonNull String message) {
Event event = new Event(message);
synchronized (mLastEvents) {
if (mLastEvents.size() > NUM_EVENTS_TO_DUMP) {