summaryrefslogtreecommitdiffstats
path: root/services/usb
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@google.com>2015-06-08 13:52:02 -0700
committerMike Lockwood <lockwood@google.com>2015-06-08 16:48:19 -0700
commit6d5a0f916499a69f28b860fd66d09b5025c30450 (patch)
tree7ea1e6899c8a12e19d180b3d4cf49d30532409bc /services/usb
parent5e4c382d15968c757fb9c5783cbd420156ea8ad2 (diff)
downloadframeworks_base-6d5a0f916499a69f28b860fd66d09b5025c30450.zip
frameworks_base-6d5a0f916499a69f28b860fd66d09b5025c30450.tar.gz
frameworks_base-6d5a0f916499a69f28b860fd66d09b5025c30450.tar.bz2
UsbMidiDevice: Only keep ALSA devices open when device is in use
This should fix contention problems for apps using USB APIs to implement MIDI support Bug: 20949468 Bug: 21630625 Change-Id: I32b44330ca0310a4693fd56a4b01ad399f82c1c9
Diffstat (limited to 'services/usb')
-rw-r--r--services/usb/java/com/android/server/usb/UsbMidiDevice.java229
1 files changed, 170 insertions, 59 deletions
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index 671cf01..97bf505 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -19,6 +19,7 @@ package com.android.server.usb;
import android.content.Context;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceServer;
+import android.media.midi.MidiDeviceStatus;
import android.media.midi.MidiManager;
import android.media.midi.MidiReceiver;
import android.media.midi.MidiSender;
@@ -43,38 +44,100 @@ import java.io.IOException;
public final class UsbMidiDevice implements Closeable {
private static final String TAG = "UsbMidiDevice";
+ private final int mAlsaCard;
+ private final int mAlsaDevice;
+ private final int mSubdeviceCount;
+ private final InputReceiverProxy[] mInputPortReceivers;
+
private MidiDeviceServer mServer;
// event schedulers for each output port
- private final MidiEventScheduler[] mEventSchedulers;
+ private MidiEventScheduler[] mEventSchedulers;
private static final int BUFFER_SIZE = 512;
- private final FileDescriptor[] mFileDescriptors;
+ private FileDescriptor[] mFileDescriptors;
// for polling multiple FileDescriptors for MIDI events
- private final StructPollfd[] mPollFDs;
+ private StructPollfd[] mPollFDs;
// streams for reading from ALSA driver
- private final FileInputStream[] mInputStreams;
+ private FileInputStream[] mInputStreams;
// streams for writing to ALSA driver
- private final FileOutputStream[] mOutputStreams;
+ private FileOutputStream[] mOutputStreams;
- public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) {
- // FIXME - support devices with different number of input and output ports
- int subDevices = nativeGetSubdeviceCount(card, device);
- if (subDevices <= 0) {
- Log.e(TAG, "nativeGetSubdeviceCount failed");
- return null;
+ private final Object mLock = new Object();
+ private boolean mIsOpen;
+
+ // pipe file descriptor for signalling input thread to exit
+ // only accessed from JNI code
+ private int mPipeFD = -1;
+
+ private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
+
+ @Override
+ public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
+ MidiDeviceInfo deviceInfo = status.getDeviceInfo();
+ int inputPorts = deviceInfo.getInputPortCount();
+ int outputPorts = deviceInfo.getOutputPortCount();
+ boolean hasOpenPorts = false;
+
+ for (int i = 0; i < inputPorts; i++) {
+ if (status.isInputPortOpen(i)) {
+ hasOpenPorts = true;
+ break;
+ }
+ }
+
+ if (!hasOpenPorts) {
+ for (int i = 0; i < outputPorts; i++) {
+ if (status.getOutputPortOpenCount(i) > 0) {
+ hasOpenPorts = true;
+ break;
+ }
+ }
+ }
+
+ synchronized (mLock) {
+ if (hasOpenPorts && !mIsOpen) {
+ openLocked();
+ } else if (!hasOpenPorts && mIsOpen) {
+ closeLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onClose() {
+ }
+ };
+
+ // This class acts as a proxy for our MidiEventScheduler receivers, which do not exist
+ // until the device has active clients
+ private final class InputReceiverProxy extends MidiReceiver {
+ private MidiReceiver mReceiver;
+
+ @Override
+ public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
+ MidiReceiver receiver = mReceiver;
+ if (receiver != null) {
+ receiver.send(msg, offset, count, timestamp);
+ }
}
+ public void setReceiver(MidiReceiver receiver) {
+ mReceiver = receiver;
+ }
+ }
+
+ public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) {
// FIXME - support devices with different number of input and output ports
- FileDescriptor[] fileDescriptors = nativeOpen(card, device, subDevices);
- if (fileDescriptors == null) {
- Log.e(TAG, "nativeOpen failed");
+ int subDeviceCount = nativeGetSubdeviceCount(card, device);
+ if (subDeviceCount <= 0) {
+ Log.e(TAG, "nativeGetSubdeviceCount failed");
return null;
}
- UsbMidiDevice midiDevice = new UsbMidiDevice(fileDescriptors);
+ UsbMidiDevice midiDevice = new UsbMidiDevice(card, device, subDeviceCount);
if (!midiDevice.register(context, properties)) {
IoUtils.closeQuietly(midiDevice);
Log.e(TAG, "createDeviceServer failed");
@@ -83,10 +146,32 @@ public final class UsbMidiDevice implements Closeable {
return midiDevice;
}
- private UsbMidiDevice(FileDescriptor[] fileDescriptors) {
+ private UsbMidiDevice(int card, int device, int subdeviceCount) {
+ mAlsaCard = card;
+ mAlsaDevice = device;
+ mSubdeviceCount = subdeviceCount;
+
+ // FIXME - support devices with different number of input and output ports
+ int inputCount = subdeviceCount;
+ mInputPortReceivers = new InputReceiverProxy[inputCount];
+ for (int port = 0; port < inputCount; port++) {
+ mInputPortReceivers[port] = new InputReceiverProxy();
+ }
+ }
+
+ private boolean openLocked() {
+ // FIXME - support devices with different number of input and output ports
+ FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice, mSubdeviceCount);
+ if (fileDescriptors == null) {
+ Log.e(TAG, "nativeOpen failed");
+ return false;
+ }
+
mFileDescriptors = fileDescriptors;
int inputCount = fileDescriptors.length;
- int outputCount = fileDescriptors.length;
+ // last file descriptor returned from nativeOpen() is only used for unblocking Os.poll()
+ // in our input thread
+ int outputCount = fileDescriptors.length - 1;
mPollFDs = new StructPollfd[inputCount];
mInputStreams = new FileInputStream[inputCount];
@@ -103,29 +188,12 @@ public final class UsbMidiDevice implements Closeable {
mEventSchedulers = new MidiEventScheduler[outputCount];
for (int i = 0; i < outputCount; i++) {
mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]);
- mEventSchedulers[i] = new MidiEventScheduler();
- }
- }
- private boolean register(Context context, Bundle properties) {
- MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
- if (midiManager == null) {
- Log.e(TAG, "No MidiManager in UsbMidiDevice.create()");
- return false;
+ MidiEventScheduler scheduler = new MidiEventScheduler();
+ mEventSchedulers[i] = scheduler;
+ mInputPortReceivers[i].setReceiver(scheduler.getReceiver());
}
- int inputCount = mInputStreams.length;
- int outputCount = mOutputStreams.length;
- MidiReceiver[] inputPortReceivers = new MidiReceiver[inputCount];
- for (int port = 0; port < inputCount; port++) {
- inputPortReceivers[port] = mEventSchedulers[port].getReceiver();
- }
-
- mServer = midiManager.createDeviceServer(inputPortReceivers, outputCount,
- null, null, properties, MidiDeviceInfo.TYPE_USB, null);
- if (mServer == null) {
- return false;
- }
final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
// Create input thread which will read from all input ports
@@ -134,24 +202,32 @@ public final class UsbMidiDevice implements Closeable {
public void run() {
byte[] buffer = new byte[BUFFER_SIZE];
try {
- boolean done = false;
- while (!done) {
- // look for a readable FileDescriptor
- for (int index = 0; index < mPollFDs.length; index++) {
- StructPollfd pfd = mPollFDs[index];
- if ((pfd.revents & OsConstants.POLLIN) != 0) {
- // clear readable flag
- pfd.revents = 0;
-
- int count = mInputStreams[index].read(buffer);
- outputReceivers[index].send(buffer, 0, count);
- } else if ((pfd.revents & (OsConstants.POLLERR
- | OsConstants.POLLHUP)) != 0) {
- done = true;
+ while (true) {
+ synchronized (mLock) {
+ if (!mIsOpen) break;
+
+ // look for a readable FileDescriptor
+ for (int index = 0; index < mPollFDs.length; index++) {
+ StructPollfd pfd = mPollFDs[index];
+ if ((pfd.revents & (OsConstants.POLLERR
+ | OsConstants.POLLHUP)) != 0) {
+ break;
+ } else if ((pfd.revents & OsConstants.POLLIN) != 0) {
+ // clear readable flag
+ pfd.revents = 0;
+
+ if (index == mInputStreams.length - 1) {
+ // last file descriptor is used only for unblocking Os.poll()
+ break;
+ }
+
+ int count = mInputStreams[index].read(buffer);
+ outputReceivers[index].send(buffer, 0, count);
+ }
}
}
- // wait until we have a readable port
+ // wait until we have a readable port or we are signalled to close
Os.poll(mPollFDs, -1 /* infinite timeout */);
}
} catch (IOException e) {
@@ -195,29 +271,64 @@ public final class UsbMidiDevice implements Closeable {
}.start();
}
+ mIsOpen = true;
+ return true;
+ }
+
+ private boolean register(Context context, Bundle properties) {
+ MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
+ if (midiManager == null) {
+ Log.e(TAG, "No MidiManager in UsbMidiDevice.create()");
+ return false;
+ }
+
+ mServer = midiManager.createDeviceServer(mInputPortReceivers, mSubdeviceCount,
+ null, null, properties, MidiDeviceInfo.TYPE_USB, mCallback);
+ if (mServer == null) {
+ return false;
+ }
+
return true;
}
@Override
public void close() throws IOException {
- for (int i = 0; i < mEventSchedulers.length; i++) {
- mEventSchedulers[i].close();
+ synchronized (mLock) {
+ if (mIsOpen) {
+ closeLocked();
+ }
}
if (mServer != null) {
- mServer.close();
+ IoUtils.closeQuietly(mServer);
+ }
+ }
+
+ private void closeLocked() {
+ for (int i = 0; i < mEventSchedulers.length; i++) {
+ mInputPortReceivers[i].setReceiver(null);
+ mEventSchedulers[i].close();
}
+ mEventSchedulers = null;
for (int i = 0; i < mInputStreams.length; i++) {
- mInputStreams[i].close();
+ IoUtils.closeQuietly(mInputStreams[i]);
}
+ mInputStreams = null;
+
for (int i = 0; i < mOutputStreams.length; i++) {
- mOutputStreams[i].close();
+ IoUtils.closeQuietly(mOutputStreams[i]);
}
+ mOutputStreams = null;
+
+ // nativeClose will close the file descriptors and signal the input thread to exit
nativeClose(mFileDescriptors);
+ mFileDescriptors = null;
+
+ mIsOpen = false;
}
private static native int nativeGetSubdeviceCount(int card, int device);
- private static native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount);
- private static native void nativeClose(FileDescriptor[] fileDescriptors);
+ private native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount);
+ private native void nativeClose(FileDescriptor[] fileDescriptors);
}