Merge "MIDI Metrics: Add MIDI Metrics in Java"
This commit is contained in:
committed by
Android (Google) Code Review
commit
de8fac0022
@@ -49,10 +49,11 @@ public class MediaMetrics {
|
||||
public static final String AUDIO_FOCUS = AUDIO + SEPARATOR + "focus";
|
||||
public static final String AUDIO_FORCE_USE = AUDIO + SEPARATOR + "forceUse";
|
||||
public static final String AUDIO_MIC = AUDIO + SEPARATOR + "mic";
|
||||
public static final String AUDIO_MIDI = AUDIO + SEPARATOR + "midi";
|
||||
public static final String AUDIO_MODE = AUDIO + SEPARATOR + "mode";
|
||||
public static final String AUDIO_SERVICE = AUDIO + SEPARATOR + "service";
|
||||
public static final String AUDIO_VOLUME = AUDIO + SEPARATOR + "volume";
|
||||
public static final String AUDIO_VOLUME_EVENT = AUDIO_VOLUME + SEPARATOR + "event";
|
||||
public static final String AUDIO_MODE = AUDIO + SEPARATOR + "mode";
|
||||
public static final String METRICS_MANAGER = "metrics" + SEPARATOR + "manager";
|
||||
}
|
||||
|
||||
@@ -90,15 +91,27 @@ public class MediaMetrics {
|
||||
// The client name
|
||||
public static final Key<String> CLIENT_NAME = createKey("clientName", String.class);
|
||||
|
||||
public static final Key<Integer> CLOSED_COUNT =
|
||||
createKey("closedCount", Integer.class); // MIDI
|
||||
|
||||
// The device type
|
||||
public static final Key<Integer> DELAY_MS = createKey("delayMs", Integer.class);
|
||||
|
||||
// The device type
|
||||
public static final Key<String> DEVICE = createKey("device", String.class);
|
||||
|
||||
// Whether the device is disconnected. This is either "true" or "false"
|
||||
public static final Key<String> DEVICE_DISCONNECTED =
|
||||
createKey("deviceDisconnected", String.class); // MIDI
|
||||
|
||||
// The ID of the device
|
||||
public static final Key<Integer> DEVICE_ID =
|
||||
createKey("deviceId", Integer.class); // MIDI
|
||||
|
||||
// For volume changes, up or down
|
||||
public static final Key<String> DIRECTION = createKey("direction", String.class);
|
||||
|
||||
public static final Key<Long> DURATION_NS =
|
||||
createKey("durationNs", Long.class); // MIDI
|
||||
// A reason for early return or error
|
||||
public static final Key<String> EARLY_RETURN =
|
||||
createKey("earlyReturn", String.class);
|
||||
@@ -128,11 +141,17 @@ public class MediaMetrics {
|
||||
// Generally string "true" or "false"
|
||||
public static final Key<String> HAS_HEAD_TRACKER =
|
||||
createKey("hasHeadTracker", String.class); // spatializer
|
||||
public static final Key<Integer> HARDWARE_TYPE =
|
||||
createKey("hardwareType", Integer.class); // MIDI
|
||||
// Generally string "true" or "false"
|
||||
public static final Key<String> HEAD_TRACKER_ENABLED =
|
||||
createKey("headTrackerEnabled", String.class); // spatializer
|
||||
|
||||
public static final Key<Integer> INDEX = createKey("index", Integer.class); // volume
|
||||
public static final Key<Integer> INPUT_PORT_COUNT =
|
||||
createKey("inputPortCount", Integer.class); // MIDI
|
||||
// Either "true" or "false"
|
||||
public static final Key<String> IS_SHARED = createKey("isShared", String.class); // MIDI
|
||||
public static final Key<String> LOG_SESSION_ID = createKey("logSessionId", String.class);
|
||||
public static final Key<Integer> MAX_INDEX = createKey("maxIndex", Integer.class); // vol
|
||||
public static final Key<Integer> MIN_INDEX = createKey("minIndex", Integer.class); // vol
|
||||
@@ -149,6 +168,11 @@ public class MediaMetrics {
|
||||
public static final Key<Integer> OBSERVERS =
|
||||
createKey("observers", Integer.class);
|
||||
|
||||
public static final Key<Integer> OPENED_COUNT =
|
||||
createKey("openedCount", Integer.class); // MIDI
|
||||
public static final Key<Integer> OUTPUT_PORT_COUNT =
|
||||
createKey("outputPortCount", Integer.class); // MIDI
|
||||
|
||||
public static final Key<String> REQUEST =
|
||||
createKey("request", String.class);
|
||||
|
||||
@@ -163,6 +187,18 @@ public class MediaMetrics {
|
||||
public static final Key<String> STATE = createKey("state", String.class);
|
||||
public static final Key<Integer> STATUS = createKey("status", Integer.class);
|
||||
public static final Key<String> STREAM_TYPE = createKey("streamType", String.class);
|
||||
|
||||
// The following MIDI string is generally either "true" or "false"
|
||||
public static final Key<String> SUPPORTS_MIDI_UMP =
|
||||
createKey("supportsMidiUmp", String.class); // Universal MIDI Packets
|
||||
|
||||
public static final Key<Integer> TOTAL_INPUT_BYTES =
|
||||
createKey("totalInputBytes", Integer.class); // MIDI
|
||||
public static final Key<Integer> TOTAL_OUTPUT_BYTES =
|
||||
createKey("totalOutputBytes", Integer.class); // MIDI
|
||||
|
||||
// The following MIDI string is generally either "true" or "false"
|
||||
public static final Key<String> USING_ALSA = createKey("usingAlsa", String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,4 +60,7 @@ interface IMidiManager
|
||||
// used by MIDI devices to report their status
|
||||
// the token is used by MidiService for death notification
|
||||
void setDeviceStatus(in IMidiDeviceServer server, in MidiDeviceStatus status);
|
||||
|
||||
// Updates the number of bytes sent and received
|
||||
void updateTotalBytes(in IMidiDeviceServer server, int inputBytes, int outputBytes);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Internal class used for providing an implementation for a MIDI device.
|
||||
@@ -79,6 +80,9 @@ public final class MidiDeviceServer implements Closeable {
|
||||
private final HashMap<MidiInputPort, PortClient> mInputPortClients =
|
||||
new HashMap<MidiInputPort, PortClient>();
|
||||
|
||||
private AtomicInteger mTotalInputBytes = new AtomicInteger();
|
||||
private AtomicInteger mTotalOutputBytes = new AtomicInteger();
|
||||
|
||||
public interface Callback {
|
||||
/**
|
||||
* Called to notify when an our device status has changed
|
||||
@@ -133,6 +137,8 @@ public final class MidiDeviceServer implements Closeable {
|
||||
int portNumber = mOutputPort.getPortNumber();
|
||||
mInputPortOutputPorts[portNumber] = null;
|
||||
mInputPortOpen[portNumber] = false;
|
||||
mTotalOutputBytes.addAndGet(mOutputPort.pullTotalBytesCount());
|
||||
updateTotalBytes();
|
||||
updateDeviceStatus();
|
||||
}
|
||||
IoUtils.closeQuietly(mOutputPort);
|
||||
@@ -156,6 +162,8 @@ public final class MidiDeviceServer implements Closeable {
|
||||
dispatcher.getSender().disconnect(mInputPort);
|
||||
int openCount = dispatcher.getReceiverCount();
|
||||
mOutputPortOpenCount[portNumber] = openCount;
|
||||
mTotalInputBytes.addAndGet(mInputPort.pullTotalBytesCount());
|
||||
updateTotalBytes();
|
||||
updateDeviceStatus();
|
||||
}
|
||||
|
||||
@@ -405,18 +413,20 @@ public final class MidiDeviceServer implements Closeable {
|
||||
synchronized (mGuard) {
|
||||
if (mIsClosed) return;
|
||||
mGuard.close();
|
||||
|
||||
for (int i = 0; i < mInputPortCount; i++) {
|
||||
MidiOutputPort outputPort = mInputPortOutputPorts[i];
|
||||
if (outputPort != null) {
|
||||
mTotalOutputBytes.addAndGet(outputPort.pullTotalBytesCount());
|
||||
IoUtils.closeQuietly(outputPort);
|
||||
mInputPortOutputPorts[i] = null;
|
||||
}
|
||||
}
|
||||
for (MidiInputPort inputPort : mInputPorts) {
|
||||
mTotalInputBytes.addAndGet(inputPort.pullTotalBytesCount());
|
||||
IoUtils.closeQuietly(inputPort);
|
||||
}
|
||||
mInputPorts.clear();
|
||||
updateTotalBytes();
|
||||
try {
|
||||
mMidiManager.unregisterDeviceServer(mServer);
|
||||
} catch (RemoteException e) {
|
||||
@@ -449,4 +459,12 @@ public final class MidiDeviceServer implements Closeable {
|
||||
System.arraycopy(mOutputPortDispatchers, 0, receivers, 0, mOutputPortCount);
|
||||
return receivers;
|
||||
}
|
||||
|
||||
private void updateTotalBytes() {
|
||||
try {
|
||||
mMidiManager.updateTotalBytes(mServer, mTotalInputBytes.get(), mTotalOutputBytes.get());
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "RemoteException in updateTotalBytes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.io.Closeable;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* This class is used for sending data to a port on a MIDI device
|
||||
@@ -43,6 +44,7 @@ public final class MidiInputPort extends MidiReceiver implements Closeable {
|
||||
|
||||
private final CloseGuard mGuard = CloseGuard.get();
|
||||
private boolean mIsClosed;
|
||||
private AtomicInteger mTotalBytes = new AtomicInteger();
|
||||
|
||||
// buffer to use for sending data out our output stream
|
||||
private final byte[] mBuffer = new byte[MidiPortImpl.MAX_PACKET_SIZE];
|
||||
@@ -87,6 +89,7 @@ public final class MidiInputPort extends MidiReceiver implements Closeable {
|
||||
}
|
||||
int length = MidiPortImpl.packData(msg, offset, count, timestamp, mBuffer);
|
||||
mOutputStream.write(mBuffer, 0, length);
|
||||
mTotalBytes.addAndGet(length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,4 +173,12 @@ public final class MidiInputPort extends MidiReceiver implements Closeable {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls total number of bytes and sets to zero. This allows multiple callers.
|
||||
* @hide
|
||||
*/
|
||||
public int pullTotalBytesCount() {
|
||||
return mTotalBytes.getAndSet(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.io.Closeable;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* This class is used for receiving data from a port on a MIDI device
|
||||
@@ -46,6 +47,7 @@ public final class MidiOutputPort extends MidiSender implements Closeable {
|
||||
|
||||
private final CloseGuard mGuard = CloseGuard.get();
|
||||
private boolean mIsClosed;
|
||||
private AtomicInteger mTotalBytes = new AtomicInteger();
|
||||
|
||||
// This thread reads MIDI events from a socket and distributes them to the list of
|
||||
// MidiReceivers attached to this device.
|
||||
@@ -83,6 +85,7 @@ public final class MidiOutputPort extends MidiSender implements Closeable {
|
||||
Log.e(TAG, "Unknown packet type " + packetType);
|
||||
break;
|
||||
}
|
||||
mTotalBytes.addAndGet(count);
|
||||
} // while (true)
|
||||
} catch (IOException e) {
|
||||
// FIXME report I/O failure?
|
||||
@@ -163,4 +166,12 @@ public final class MidiOutputPort extends MidiSender implements Closeable {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls total number of bytes and sets to zero. This allows multiple callers.
|
||||
* @hide
|
||||
*/
|
||||
public int pullTotalBytesCount() {
|
||||
return mTotalBytes.getAndSet(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
// import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
@@ -31,6 +30,7 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.media.MediaMetrics;
|
||||
import android.media.midi.IBluetoothMidiService;
|
||||
import android.media.midi.IMidiDeviceListener;
|
||||
import android.media.midi.IMidiDeviceOpenCallback;
|
||||
@@ -63,12 +63,16 @@ import org.xmlpull.v1.XmlPullParser;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
// NOTE about locking order:
|
||||
// if there is a path that syncs on BOTH mDevicesByInfo AND mDeviceConnections,
|
||||
@@ -359,6 +363,17 @@ public class MidiService extends IMidiManager.Stub {
|
||||
private final ArrayList<DeviceConnection> mDeviceConnections
|
||||
= new ArrayList<DeviceConnection>();
|
||||
|
||||
// Keep track of number of added and removed collections for logging
|
||||
private AtomicInteger mDeviceConnectionsAdded = new AtomicInteger();
|
||||
private AtomicInteger mDeviceConnectionsRemoved = new AtomicInteger();
|
||||
|
||||
// Keep track of total time with at least one active connection
|
||||
private AtomicLong mTotalTimeConnectedNs = new AtomicLong();
|
||||
private Instant mPreviousCounterInstant = null;
|
||||
|
||||
private AtomicInteger mTotalInputBytes = new AtomicInteger();
|
||||
private AtomicInteger mTotalOutputBytes = new AtomicInteger();
|
||||
|
||||
public Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo,
|
||||
ServiceInfo serviceInfo, int uid) {
|
||||
mDeviceInfo = deviceInfo;
|
||||
@@ -460,6 +475,11 @@ public class MidiService extends IMidiManager.Stub {
|
||||
public void addDeviceConnection(DeviceConnection connection) {
|
||||
Log.d(TAG, "addDeviceConnection() [A] connection:" + connection);
|
||||
synchronized (mDeviceConnections) {
|
||||
mDeviceConnectionsAdded.incrementAndGet();
|
||||
if (mPreviousCounterInstant == null) {
|
||||
mPreviousCounterInstant = Instant.now();
|
||||
}
|
||||
|
||||
Log.d(TAG, " mServer:" + mServer);
|
||||
if (mServer != null) {
|
||||
Log.i(TAG, "++++ A");
|
||||
@@ -533,6 +553,20 @@ public class MidiService extends IMidiManager.Stub {
|
||||
public void removeDeviceConnection(DeviceConnection connection) {
|
||||
synchronized (mDevicesByInfo) {
|
||||
synchronized (mDeviceConnections) {
|
||||
int numRemovedConnections = mDeviceConnectionsRemoved.incrementAndGet();
|
||||
if (mPreviousCounterInstant != null) {
|
||||
mTotalTimeConnectedNs.addAndGet(Duration.between(
|
||||
mPreviousCounterInstant, Instant.now()).toNanos());
|
||||
}
|
||||
// Stop the clock if all devices have been removed.
|
||||
// Otherwise, start the clock from the current instant.
|
||||
if (numRemovedConnections >= mDeviceConnectionsAdded.get()) {
|
||||
mPreviousCounterInstant = null;
|
||||
} else {
|
||||
mPreviousCounterInstant = Instant.now();
|
||||
}
|
||||
logMetrics(false /* isDeviceDisconnected */);
|
||||
|
||||
mDeviceConnections.remove(connection);
|
||||
|
||||
if (connection.getDevice().getDeviceInfo().getType()
|
||||
@@ -569,6 +603,16 @@ public class MidiService extends IMidiManager.Stub {
|
||||
connection.getClient().removeDeviceConnection(connection);
|
||||
}
|
||||
mDeviceConnections.clear();
|
||||
|
||||
// If the timer is still going, some clients have not closed the connection yet.
|
||||
if (mPreviousCounterInstant != null) {
|
||||
Instant currentInstant = Instant.now();
|
||||
mTotalTimeConnectedNs.addAndGet(Duration.between(
|
||||
mPreviousCounterInstant, currentInstant).toNanos());
|
||||
mPreviousCounterInstant = currentInstant;
|
||||
}
|
||||
|
||||
logMetrics(true /* isDeviceDisconnected */);
|
||||
}
|
||||
setDeviceServer(null);
|
||||
|
||||
@@ -585,6 +629,35 @@ public class MidiService extends IMidiManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void logMetrics(boolean isDeviceDisconnected) {
|
||||
// Only log metrics if the device was used in a connection
|
||||
int numDeviceConnectionAdded = mDeviceConnectionsAdded.get();
|
||||
if (mDeviceInfo != null && numDeviceConnectionAdded > 0) {
|
||||
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIDI)
|
||||
.setUid(mUid)
|
||||
.set(MediaMetrics.Property.DEVICE_ID, mDeviceInfo.getId())
|
||||
.set(MediaMetrics.Property.INPUT_PORT_COUNT, mDeviceInfo.getInputPortCount())
|
||||
.set(MediaMetrics.Property.OUTPUT_PORT_COUNT,
|
||||
mDeviceInfo.getOutputPortCount())
|
||||
.set(MediaMetrics.Property.HARDWARE_TYPE, mDeviceInfo.getType())
|
||||
.set(MediaMetrics.Property.DURATION_NS, mTotalTimeConnectedNs.get())
|
||||
.set(MediaMetrics.Property.OPENED_COUNT, numDeviceConnectionAdded)
|
||||
.set(MediaMetrics.Property.CLOSED_COUNT, mDeviceConnectionsRemoved.get())
|
||||
.set(MediaMetrics.Property.DEVICE_DISCONNECTED,
|
||||
isDeviceDisconnected ? "true" : "false")
|
||||
.set(MediaMetrics.Property.IS_SHARED,
|
||||
!mDeviceInfo.isPrivate() ? "true" : "false")
|
||||
.set(MediaMetrics.Property.SUPPORTS_MIDI_UMP, mDeviceInfo.getDefaultProtocol()
|
||||
!= MidiDeviceInfo.PROTOCOL_UNKNOWN ? "true" : "false")
|
||||
.set(MediaMetrics.Property.USING_ALSA, mDeviceInfo.getProperties().get(
|
||||
MidiDeviceInfo.PROPERTY_ALSA_CARD) != null ? "true" : "false")
|
||||
.set(MediaMetrics.Property.EVENT, "deviceClosed")
|
||||
.set(MediaMetrics.Property.TOTAL_INPUT_BYTES, mTotalInputBytes.get())
|
||||
.set(MediaMetrics.Property.TOTAL_OUTPUT_BYTES, mTotalOutputBytes.get())
|
||||
.record();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
Log.d(TAG, "Device died: " + this);
|
||||
@@ -593,6 +666,11 @@ public class MidiService extends IMidiManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTotalBytes(int totalInputBytes, int totalOutputBytes) {
|
||||
mTotalInputBytes.set(totalInputBytes);
|
||||
mTotalOutputBytes.set(totalOutputBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("Device Info: ");
|
||||
@@ -1372,6 +1450,17 @@ public class MidiService extends IMidiManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTotalBytes(IMidiDeviceServer server, int totalInputBytes,
|
||||
int totalOutputBytes) {
|
||||
synchronized (mDevicesByInfo) {
|
||||
Device device = mDevicesByServer.get(server.asBinder());
|
||||
if (device != null) {
|
||||
device.updateTotalBytes(totalInputBytes, totalOutputBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
|
||||
if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
|
||||
|
||||
Reference in New Issue
Block a user