Add event scheduling support to USB MIDI Manager code
Change-Id: I9fdaaac35c296acc67ee0cf346b0c7b56bf58393
diff --git a/core/java/com/android/internal/midi/EventScheduler.java b/core/java/com/android/internal/midi/EventScheduler.java
index 7526609..7b9a48c 100644
--- a/core/java/com/android/internal/midi/EventScheduler.java
+++ b/core/java/com/android/internal/midi/EventScheduler.java
@@ -27,10 +27,11 @@
public class EventScheduler {
private static final long NANOS_PER_MILLI = 1000000;
- private final Object lock = new Object();
+ private final Object mLock = new Object();
private SortedMap<Long, FastEventQueue> mEventBuffer;
private FastEventQueue mEventPool = null;
private int mMaxPoolSize = 200;
+ private boolean mClosed;
public EventScheduler() {
mEventBuffer = new TreeMap<Long, FastEventQueue>();
@@ -146,7 +147,7 @@
* @param event
*/
public void add(SchedulableEvent event) {
- synchronized (lock) {
+ synchronized (mLock) {
FastEventQueue list = mEventBuffer.get(event.getTimestamp());
if (list == null) {
long lowestTime = mEventBuffer.isEmpty() ? Long.MAX_VALUE
@@ -156,7 +157,7 @@
// If the event we added is earlier than the previous earliest
// event then notify any threads waiting for the next event.
if (event.getTimestamp() < lowestTime) {
- lock.notify();
+ mLock.notify();
}
} else {
list.add(event);
@@ -183,7 +184,7 @@
*/
public SchedulableEvent getNextEvent(long time) {
SchedulableEvent event = null;
- synchronized (lock) {
+ synchronized (mLock) {
if (!mEventBuffer.isEmpty()) {
long lowestTime = mEventBuffer.firstKey();
// Is it time for this list to be processed?
@@ -206,9 +207,9 @@
*/
public SchedulableEvent waitNextEvent() throws InterruptedException {
SchedulableEvent event = null;
- while (true) {
- long millisToWait = Integer.MAX_VALUE;
- synchronized (lock) {
+ synchronized (mLock) {
+ while (!mClosed) {
+ long millisToWait = Integer.MAX_VALUE;
if (!mEventBuffer.isEmpty()) {
long now = System.nanoTime();
long lowestTime = mEventBuffer.firstKey();
@@ -228,9 +229,16 @@
}
}
}
- lock.wait((int) millisToWait);
+ mLock.wait((int) millisToWait);
}
}
return event;
}
+
+ public void close() {
+ synchronized (mLock) {
+ mClosed = true;
+ mLock.notify();
+ }
+ }
}
diff --git a/core/java/com/android/internal/midi/MidiEventScheduler.java b/core/java/com/android/internal/midi/MidiEventScheduler.java
index 3a1d3fc..42d70f6 100644
--- a/core/java/com/android/internal/midi/MidiEventScheduler.java
+++ b/core/java/com/android/internal/midi/MidiEventScheduler.java
@@ -28,10 +28,16 @@
// Maintain a pool of scheduled events to reduce memory allocation.
// This pool increases performance by about 14%.
private final static int POOL_EVENT_SIZE = 16;
- private MidiReceiver mReceiver = new SchedulingReceiver();
- private class SchedulingReceiver extends MidiReceiver
- {
+ private final MidiReceiver[] mReceivers;
+
+ private class SchedulingReceiver extends MidiReceiver {
+ private final int mPortNumber;
+
+ public SchedulingReceiver(int portNumber) {
+ mPortNumber = portNumber;
+ }
+
/**
* Store these bytes in the EventScheduler to be delivered at the specified
* time.
@@ -41,12 +47,14 @@
throws IOException {
MidiEvent event = createScheduledEvent(msg, offset, count, timestamp);
if (event != null) {
+ event.portNumber = mPortNumber;
add(event);
}
}
}
public static class MidiEvent extends SchedulableEvent {
+ public int portNumber;
public int count = 0;
public byte[] data;
@@ -72,6 +80,17 @@
}
}
+ public MidiEventScheduler() {
+ this(0);
+ }
+
+ public MidiEventScheduler(int portCount) {
+ mReceivers = new MidiReceiver[portCount];
+ for (int i = 0; i < portCount; i++) {
+ mReceivers[i] = new SchedulingReceiver(i);
+ }
+ }
+
/**
* Create an event that contains the message.
*/
@@ -113,7 +132,15 @@
* @return the MidiReceiver
*/
public MidiReceiver getReceiver() {
- return mReceiver;
+ return mReceivers[0];
+ }
+
+ /**
+ * This MidiReceiver will write date to the scheduling buffer.
+ * @return the MidiReceiver
+ */
+ public MidiReceiver getReceiver(int portNumber) {
+ return mReceivers[portNumber];
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index 7c101a40f..6ece888 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -29,6 +29,9 @@
import android.system.StructPollfd;
import android.util.Log;
+import com.android.internal.midi.MidiEventScheduler;
+import com.android.internal.midi.MidiEventScheduler.MidiEvent;
+
import libcore.io.IoUtils;
import java.io.Closeable;
@@ -42,7 +45,7 @@
private MidiDeviceServer mServer;
- private final MidiReceiver[] mInputPortReceivers;
+ private final MidiEventScheduler mEventScheduler;
private static final int BUFFER_SIZE = 512;
@@ -99,19 +102,7 @@
for (int i = 0; i < outputCount; i++) {
mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]);
}
-
- mInputPortReceivers = new MidiReceiver[inputCount];
- for (int port = 0; port < inputCount; port++) {
- final int portF = port;
- mInputPortReceivers[port] = new MidiReceiver() {
- @Override
- public void onReceive(byte[] data, int offset, int count, long timestamp)
- throws IOException {
- // FIXME - timestamps are ignored, future posting not supported yet.
- mOutputStreams[portF].write(data, offset, count);
- }
- };
- }
+ mEventScheduler = new MidiEventScheduler(inputCount);
}
private boolean register(Context context, Bundle properties) {
@@ -121,16 +112,22 @@
return false;
}
+ int inputCount = mInputStreams.length;
int outputCount = mOutputStreams.length;
- mServer = midiManager.createDeviceServer(mInputPortReceivers, outputCount,
+ MidiReceiver[] inputPortReceivers = new MidiReceiver[inputCount];
+ for (int port = 0; port < inputCount; port++) {
+ inputPortReceivers[port] = mEventScheduler.getReceiver(port);
+ }
+
+ mServer = midiManager.createDeviceServer(inputPortReceivers, outputCount,
null, null, properties, MidiDeviceInfo.TYPE_USB, null);
if (mServer == null) {
return false;
}
final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
- // FIXME can we only run this when we have a dispatcher that has listeners?
- new Thread() {
+ // Create input thread
+ new Thread("UsbMidiDevice input thread") {
@Override
public void run() {
byte[] buffer = new byte[BUFFER_SIZE];
@@ -160,6 +157,33 @@
} catch (ErrnoException e) {
Log.d(TAG, "reader thread exiting");
}
+ Log.d(TAG, "input thread exit");
+ }
+ }.start();
+
+ // Create output thread
+ new Thread("UsbMidiDevice output thread") {
+ @Override
+ public void run() {
+ while (true) {
+ MidiEvent event;
+ try {
+ event = (MidiEvent)mEventScheduler.waitNextEvent();
+ } catch (InterruptedException e) {
+ // try again
+ continue;
+ }
+ if (event == null) {
+ break;
+ }
+ try {
+ mOutputStreams[event.portNumber].write(event.data, 0, event.count);
+ } catch (IOException e) {
+ Log.e(TAG, "write failed for port " + event.portNumber);
+ }
+ mEventScheduler.addEventToPool(event);
+ }
+ Log.d(TAG, "output thread exit");
}
}.start();
@@ -168,6 +192,8 @@
@Override
public void close() throws IOException {
+ mEventScheduler.close();
+
if (mServer != null) {
mServer.close();
}