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:
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user