From dd43b24ae86424e01c86851cc92039984344cee2 Mon Sep 17 00:00:00 2001 From: Ytai Ben-Tsvi Date: Fri, 21 Feb 2020 17:37:07 -0800 Subject: [PATCH] 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 --- .../soundtrigger_middleware/Dumpable.java | 32 +++++++ .../SoundTriggerMiddlewareLogging.java | 84 +++++++++++++++---- .../SoundTriggerMiddlewareService.java | 8 ++ .../SoundTriggerMiddlewareValidation.java | 55 ++++++++++-- 4 files changed, 157 insertions(+), 22 deletions(-) create mode 100644 services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java diff --git a/services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java b/services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java new file mode 100644 index 0000000000000..f9aa009c6e2ad --- /dev/null +++ b/services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java @@ -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); +} 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 d6546fe2126a0..7cb7bf4db7ea5 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java @@ -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 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); + } } } diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java index ca3acbfa0f78c..0d8fc76e1bd28 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java @@ -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; diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java index 82fdee81516a3..c45f37dfdbd89 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java @@ -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 mModuleHandles; + private Map> 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(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 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 entry : mLoadedModels.entrySet()) { + pw.print(entry.getKey()); + pw.print('\t'); + pw.print(entry.getValue().activityState.name()); + pw.println(); + } + } + //////////////////////////////////////////////////////////////////////////////////////////// // Callbacks