Add sysdump to SoundTriggerMiddlewareService

This adds last events and current state to a dump of the service.
There is still some work to be done to make the output prettier.

Change-Id: I0b734cc7d2ca607a86ae1e6bcb6ae48718f5c51f
Merged-In: I0b734cc7d2ca607a86ae1e6bcb6ae48718f5c51f
Bug: 150325756
This commit is contained in:
Ytai Ben-Tsvi
2020-02-21 17:37:07 -08:00
parent 7504d838e8
commit dd43b24ae8
4 changed files with 157 additions and 22 deletions

View File

@@ -0,0 +1,32 @@
/*
* 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 java.io.PrintWriter;
/**
* An interface of an object that can generate a dump.
*/
interface Dumpable {
/**
* Generate a human-readable dump into the given writer.
* @param pw The writer.
*/
void dump(@NonNull PrintWriter pw);
}

View File

@@ -17,6 +17,7 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.content.Context;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
@@ -27,11 +28,16 @@ import android.media.soundtrigger_middleware.RecognitionConfig;
import android.media.soundtrigger_middleware.RecognitionEvent;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.Objects;
/**
@@ -56,13 +62,11 @@ import java.util.Objects;
* String, Object, Object[])}, {@link #logVoidReturnWithObject(Object, String, Object[])} and {@link
* #logExceptionWithObject(Object, String, Exception, Object[])}.
*/
public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareService {
public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareService, Dumpable {
private static final String TAG = "SoundTriggerMiddlewareLogging";
private final @NonNull ISoundTriggerMiddlewareService mDelegate;
public SoundTriggerMiddlewareLogging(
@NonNull ISoundTriggerMiddlewareService delegate) {
public SoundTriggerMiddlewareLogging(@NonNull ISoundTriggerMiddlewareService delegate) {
mDelegate = delegate;
}
@@ -346,6 +350,21 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareSer
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Actual logging logic below.
private static final int NUM_EVENTS_TO_DUMP = 64;
private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
private final @NonNull LinkedList<Event> mLastEvents = new LinkedList<>();
static private class Event {
public final long timestamp = System.currentTimeMillis();
public final String message;
private Event(String message) {
this.message = message;
}
}
static private String formatArgs(Object[] args) {
String result = Arrays.toString(args);
// Strip the square brackets.
@@ -354,23 +373,60 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareSer
private void logReturnWithObject(Object object, String methodName, Object retVal,
Object[] args) {
// TODO(ytai): Save last N entries and emit on dump().
Log.i(TAG, String.format("%s[this=%s](%s) -> %s", methodName, Objects.toString(object),
final String message = String.format("%s[this=%s, caller=%d/%d](%s) -> %s", methodName,
Objects.toString(object),
Binder.getCallingUid(), Binder.getCallingPid(),
formatArgs(args),
Objects.toString(retVal)));
Objects.toString(retVal));
Log.i(TAG, message);
appendMessage(message);
}
private void logVoidReturnWithObject(Object object, String methodName, Object[] args) {
// TODO(ytai): Save last N entries and emit on dump().
Log.i(TAG, String.format("%s[this=%s](%s)", methodName, Objects.toString(object),
formatArgs(args)));
final String message = String.format("%s[this=%s, caller=%d/%d](%s)", methodName,
Objects.toString(object),
Binder.getCallingUid(), Binder.getCallingPid(),
formatArgs(args));
Log.i(TAG, message);
appendMessage(message);
}
private void logExceptionWithObject(Object object, String methodName, Exception ex,
Object[] args) {
// TODO(ytai): Save last N entries and emit on dump().
Log.e(TAG, String.format("%s[this=%s](%s) threw", methodName, Objects.toString(object),
formatArgs(args)),
ex);
final String message = String.format("%s[this=%s, caller=%d/%d](%s) threw", methodName,
Objects.toString(object),
Binder.getCallingUid(), Binder.getCallingPid(),
formatArgs(args));
Log.e(TAG, message, ex);
appendMessage(message + " " + ex.toString());
}
private void appendMessage(String message) {
Event event = new Event(message);
synchronized (mLastEvents) {
if (mLastEvents.size() > NUM_EVENTS_TO_DUMP) {
mLastEvents.remove();
}
mLastEvents.add(event);
}
}
@Override public void dump(PrintWriter pw) {
pw.println();
pw.println("=========================================");
pw.println("Last events");
pw.println("=========================================");
synchronized (mLastEvents) {
for (Event event : mLastEvents) {
pw.print(DATE_FORMAT.format(new Date(event.timestamp)));
pw.print('\t');
pw.println(event.message);
}
}
pw.println();
if (mDelegate instanceof Dumpable) {
((Dumpable) mDelegate).dump(pw);
}
}
}

View File

@@ -32,6 +32,8 @@ import android.util.Log;
import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
/**
@@ -89,6 +91,12 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
mDelegate.setExternalCaptureState(active);
}
@Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
if (mDelegate instanceof Dumpable) {
((Dumpable) mDelegate).dump(fout);
}
}
private final static class ModuleService extends ISoundTriggerModule.Stub {
private final ISoundTriggerModule mDelegate;

View File

@@ -40,6 +40,7 @@ import android.util.Log;
import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -103,12 +104,12 @@ import java.util.Set;
*
* {@hide}
*/
public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddlewareService {
public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddlewareService, Dumpable {
private static final String TAG = "SoundTriggerMiddlewareValidation";
private final @NonNull ISoundTriggerMiddlewareService mDelegate;
private final @NonNull Context mContext;
private Set<Integer> mModuleHandles;
private Map<Integer, Set<ModuleService>> mModules;
public SoundTriggerMiddlewareValidation(
@NonNull ISoundTriggerMiddlewareService delegate, @NonNull Context context) {
@@ -154,9 +155,9 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// From here on, every exception isn't client's fault.
try {
SoundTriggerModuleDescriptor[] result = mDelegate.listModules();
mModuleHandles = new HashSet<Integer>(result.length);
mModules = new HashMap<>(result.length);
for (SoundTriggerModuleDescriptor desc : result) {
mModuleHandles.add(desc.handle);
mModules.put(desc.handle, new HashSet<>());
}
return result;
} catch (Exception e) {
@@ -176,18 +177,18 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
synchronized (this) {
// State validation.
if (mModuleHandles == null) {
if (mModules == null) {
throw new IllegalStateException(
"Client must call listModules() prior to attaching.");
}
if (!mModuleHandles.contains(handle)) {
if (!mModules.containsKey(handle)) {
throw new IllegalArgumentException("Invalid handle: " + handle);
}
// From here on, every exception isn't client's fault.
try {
ModuleService moduleService =
new ModuleService(callback);
new ModuleService(handle, callback);
moduleService.attach(mDelegate.attach(handle, moduleService));
return moduleService;
} catch (Exception e) {
@@ -271,6 +272,28 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
"This implementation is not inteded to be used directly with Binder.");
}
@Override public void dump(PrintWriter pw) {
synchronized (this) {
if (mModules != null) {
for (int handle : mModules.keySet()) {
pw.println("=========================================");
pw.printf("Active sessions for module %d", handle);
pw.println();
pw.println("=========================================");
for (ModuleService session : mModules.get(handle)) {
session.dump(pw);
}
}
}
}
pw.println();
if (mDelegate instanceof Dumpable) {
((Dumpable) mDelegate).dump(pw);
}
}
/** State of a sound model. */
static class ModelState {
/** Activity state of a sound model. */
@@ -345,9 +368,11 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
private final ISoundTriggerCallback mCallback;
private ISoundTriggerModule mDelegate;
private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
private final int mHandle;
ModuleService(@NonNull ISoundTriggerCallback callback) {
ModuleService(int handle, @NonNull ISoundTriggerCallback callback) {
mCallback = callback;
mHandle = handle;
try {
mCallback.asBinder().linkToDeath(null, 0);
} catch (RemoteException e) {
@@ -357,6 +382,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
void attach(@NonNull ISoundTriggerModule delegate) {
mDelegate = delegate;
mModules.get(mHandle).add(this);
}
@Override
@@ -652,11 +678,24 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
mDelegate.detach();
mDelegate = null;
mCallback.asBinder().unlinkToDeath(null, 0);
mModules.get(mHandle).remove(this);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
void dump(PrintWriter pw) {
pw.printf("Loaded models for session %s (handle, active)", toString());
pw.println();
pw.println("-------------------------------");
for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
pw.print(entry.getKey());
pw.print('\t');
pw.print(entry.getValue().activityState.name());
pw.println();
}
}
////////////////////////////////////////////////////////////////////////////////////////////
// Callbacks