Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dd43b24a authored by Ytai Ben-Tsvi's avatar Ytai Ben-Tsvi
Browse files

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
parent 7504d838
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
/*
 * 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);
}
+70 −14
Original line number Diff line number Diff line
@@ -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);
        }
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -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;

+47 −8
Original line number Diff line number Diff line
@@ -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