resolve merge conflicts of c76a3ac to stage-aosp-master
am: 67ffd5e617
Change-Id: Ib2d89672efe8757edff320d5ed800b7b0293323b
This commit is contained in:
@@ -26,11 +26,23 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
* Utility class for dispatching MIDI data to a list of {@link android.media.midi.MidiReceiver}s.
|
||||
* This class subclasses {@link android.media.midi.MidiReceiver} and dispatches any data it receives
|
||||
* to its receiver list. Any receivers that throw an exception upon receiving data will
|
||||
* be automatically removed from the receiver list, but no IOException will be returned
|
||||
* from the dispatcher's {@link android.media.midi.MidiReceiver#onSend} in that case.
|
||||
* be automatically removed from the receiver list. If a MidiReceiverFailureHandler has been
|
||||
* provided to the MidiDispatcher, it will be notified about the failure, but the exception
|
||||
* itself will be swallowed.
|
||||
*/
|
||||
public final class MidiDispatcher extends MidiReceiver {
|
||||
|
||||
// MidiDispatcher's client and MidiReceiver's owner can be different
|
||||
// classes (e.g. MidiDeviceService is a client, but MidiDeviceServer is
|
||||
// the owner), and errors occuring during sending need to be reported
|
||||
// to the owner rather than to the sender.
|
||||
//
|
||||
// Note that the callbacks will be called on the sender's thread.
|
||||
public interface MidiReceiverFailureHandler {
|
||||
void onReceiverFailure(MidiReceiver receiver, IOException failure);
|
||||
}
|
||||
|
||||
private final MidiReceiverFailureHandler mFailureHandler;
|
||||
private final CopyOnWriteArrayList<MidiReceiver> mReceivers
|
||||
= new CopyOnWriteArrayList<MidiReceiver>();
|
||||
|
||||
@@ -46,6 +58,14 @@ public final class MidiDispatcher extends MidiReceiver {
|
||||
}
|
||||
};
|
||||
|
||||
public MidiDispatcher() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public MidiDispatcher(MidiReceiverFailureHandler failureHandler) {
|
||||
mFailureHandler = failureHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of {@link android.media.midi.MidiReceiver}s this dispatcher contains.
|
||||
* @return the number of receivers
|
||||
@@ -70,8 +90,13 @@ public final class MidiDispatcher extends MidiReceiver {
|
||||
try {
|
||||
receiver.send(msg, offset, count, timestamp);
|
||||
} catch (IOException e) {
|
||||
// if the receiver fails we remove the receiver but do not propagate the exception
|
||||
// If the receiver fails we remove the receiver but do not propagate the exception.
|
||||
// Note that this may also happen if the client code stalls, and thus underlying
|
||||
// MidiInputPort.onSend has raised IOException for EAGAIN / EWOULDBLOCK error.
|
||||
mReceivers.remove(receiver);
|
||||
if (mFailureHandler != null) {
|
||||
mFailureHandler.onReceiverFailure(receiver, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,7 +104,15 @@ public final class MidiDispatcher extends MidiReceiver {
|
||||
@Override
|
||||
public void onFlush() throws IOException {
|
||||
for (MidiReceiver receiver : mReceivers) {
|
||||
receiver.flush();
|
||||
try {
|
||||
receiver.flush();
|
||||
} catch (IOException e) {
|
||||
// This is just a special case of 'send' thus handle in the same way.
|
||||
mReceivers.remove(receiver);
|
||||
if (mFailureHandler != null) {
|
||||
mFailureHandler.onReceiverFailure(receiver, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,10 @@ public final class MidiDeviceServer implements Closeable {
|
||||
|
||||
private final Callback mCallback;
|
||||
|
||||
private final HashMap<IBinder, PortClient> mPortClients = new HashMap<IBinder, PortClient>();
|
||||
private final HashMap<MidiInputPort, PortClient> mInputPortClients =
|
||||
new HashMap<MidiInputPort, PortClient>();
|
||||
|
||||
public interface Callback {
|
||||
/**
|
||||
* Called to notify when an our device status has changed
|
||||
@@ -102,6 +106,10 @@ public final class MidiDeviceServer implements Closeable {
|
||||
|
||||
abstract void close();
|
||||
|
||||
MidiInputPort getInputPort() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
close();
|
||||
@@ -152,9 +160,12 @@ public final class MidiDeviceServer implements Closeable {
|
||||
mInputPorts.remove(mInputPort);
|
||||
IoUtils.closeQuietly(mInputPort);
|
||||
}
|
||||
}
|
||||
|
||||
private final HashMap<IBinder, PortClient> mPortClients = new HashMap<IBinder, PortClient>();
|
||||
@Override
|
||||
MidiInputPort getInputPort() {
|
||||
return mInputPort;
|
||||
}
|
||||
}
|
||||
|
||||
// Binder interface stub for receiving connection requests from clients
|
||||
private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() {
|
||||
@@ -215,6 +226,12 @@ public final class MidiDeviceServer implements Closeable {
|
||||
ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair(
|
||||
OsConstants.SOCK_SEQPACKET);
|
||||
MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber);
|
||||
// Undo the default blocking-mode of the server-side socket for
|
||||
// physical devices to avoid stalling the Java device handler if
|
||||
// client app code gets stuck inside 'onSend' handler.
|
||||
if (mDeviceInfo.getType() != MidiDeviceInfo.TYPE_VIRTUAL) {
|
||||
IoUtils.setBlocking(pair[0].getFileDescriptor(), false);
|
||||
}
|
||||
MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber];
|
||||
synchronized (dispatcher) {
|
||||
dispatcher.getSender().connect(inputPort);
|
||||
@@ -228,6 +245,9 @@ public final class MidiDeviceServer implements Closeable {
|
||||
synchronized (mPortClients) {
|
||||
mPortClients.put(token, client);
|
||||
}
|
||||
synchronized (mInputPortClients) {
|
||||
mInputPortClients.put(inputPort, client);
|
||||
}
|
||||
return pair[1];
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "unable to create ParcelFileDescriptors in openOutputPort");
|
||||
@@ -237,12 +257,19 @@ public final class MidiDeviceServer implements Closeable {
|
||||
|
||||
@Override
|
||||
public void closePort(IBinder token) {
|
||||
MidiInputPort inputPort = null;
|
||||
synchronized (mPortClients) {
|
||||
PortClient client = mPortClients.remove(token);
|
||||
if (client != null) {
|
||||
inputPort = client.getInputPort();
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
if (inputPort != null) {
|
||||
synchronized (mInputPortClients) {
|
||||
mInputPortClients.remove(inputPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -270,6 +297,9 @@ public final class MidiDeviceServer implements Closeable {
|
||||
synchronized (mPortClients) {
|
||||
mPortClients.put(token, client);
|
||||
}
|
||||
synchronized (mInputPortClients) {
|
||||
mInputPortClients.put(inputPort, client);
|
||||
}
|
||||
return Process.myPid(); // for caller to detect same process ID
|
||||
}
|
||||
|
||||
@@ -303,7 +333,7 @@ public final class MidiDeviceServer implements Closeable {
|
||||
|
||||
mOutputPortDispatchers = new MidiDispatcher[numOutputPorts];
|
||||
for (int i = 0; i < numOutputPorts; i++) {
|
||||
mOutputPortDispatchers[i] = new MidiDispatcher();
|
||||
mOutputPortDispatchers[i] = new MidiDispatcher(mInputPortFailureHandler);
|
||||
}
|
||||
|
||||
mInputPortOpen = new boolean[mInputPortCount];
|
||||
@@ -312,6 +342,20 @@ public final class MidiDeviceServer implements Closeable {
|
||||
mGuard.open("close");
|
||||
}
|
||||
|
||||
private final MidiDispatcher.MidiReceiverFailureHandler mInputPortFailureHandler =
|
||||
new MidiDispatcher.MidiReceiverFailureHandler() {
|
||||
public void onReceiverFailure(MidiReceiver receiver, IOException failure) {
|
||||
Log.e(TAG, "MidiInputPort failed to send data", failure);
|
||||
PortClient client = null;
|
||||
synchronized (mInputPortClients) {
|
||||
client = mInputPortClients.remove(receiver);
|
||||
}
|
||||
if (client != null) {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Constructor for MidiDeviceService.onCreate()
|
||||
/* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers,
|
||||
MidiDeviceInfo deviceInfo, Callback callback) {
|
||||
|
||||
@@ -83,7 +83,7 @@ public final class MidiOutputPort extends MidiSender implements Closeable {
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// FIXME report I/O failure?
|
||||
Log.e(TAG, "read failed");
|
||||
Log.e(TAG, "read failed", e);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(mInputStream);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public final class UsbMidiDevice implements Closeable {
|
||||
|
||||
private MidiDeviceServer mServer;
|
||||
|
||||
// event schedulers for each output port
|
||||
// event schedulers for each input port of the physical device
|
||||
private MidiEventScheduler[] mEventSchedulers;
|
||||
|
||||
private static final int BUFFER_SIZE = 512;
|
||||
@@ -160,9 +160,9 @@ public final class UsbMidiDevice implements Closeable {
|
||||
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++) {
|
||||
int inputPortCount = subdeviceCount;
|
||||
mInputPortReceivers = new InputReceiverProxy[inputPortCount];
|
||||
for (int port = 0; port < inputPortCount; port++) {
|
||||
mInputPortReceivers[port] = new InputReceiverProxy();
|
||||
}
|
||||
}
|
||||
@@ -176,14 +176,14 @@ public final class UsbMidiDevice implements Closeable {
|
||||
}
|
||||
|
||||
mFileDescriptors = fileDescriptors;
|
||||
int inputCount = fileDescriptors.length;
|
||||
int inputStreamCount = fileDescriptors.length;
|
||||
// last file descriptor returned from nativeOpen() is only used for unblocking Os.poll()
|
||||
// in our input thread
|
||||
int outputCount = fileDescriptors.length - 1;
|
||||
int outputStreamCount = fileDescriptors.length - 1;
|
||||
|
||||
mPollFDs = new StructPollfd[inputCount];
|
||||
mInputStreams = new FileInputStream[inputCount];
|
||||
for (int i = 0; i < inputCount; i++) {
|
||||
mPollFDs = new StructPollfd[inputStreamCount];
|
||||
mInputStreams = new FileInputStream[inputStreamCount];
|
||||
for (int i = 0; i < inputStreamCount; i++) {
|
||||
FileDescriptor fd = fileDescriptors[i];
|
||||
StructPollfd pollfd = new StructPollfd();
|
||||
pollfd.fd = fd;
|
||||
@@ -192,9 +192,9 @@ public final class UsbMidiDevice implements Closeable {
|
||||
mInputStreams[i] = new FileInputStream(fd);
|
||||
}
|
||||
|
||||
mOutputStreams = new FileOutputStream[outputCount];
|
||||
mEventSchedulers = new MidiEventScheduler[outputCount];
|
||||
for (int i = 0; i < outputCount; i++) {
|
||||
mOutputStreams = new FileOutputStream[outputStreamCount];
|
||||
mEventSchedulers = new MidiEventScheduler[outputStreamCount];
|
||||
for (int i = 0; i < outputStreamCount; i++) {
|
||||
mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]);
|
||||
|
||||
MidiEventScheduler scheduler = new MidiEventScheduler();
|
||||
@@ -204,7 +204,7 @@ public final class UsbMidiDevice implements Closeable {
|
||||
|
||||
final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
|
||||
|
||||
// Create input thread which will read from all input ports
|
||||
// Create input thread which will read from all output ports of the physical device
|
||||
new Thread("UsbMidiDevice input thread") {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -249,8 +249,8 @@ public final class UsbMidiDevice implements Closeable {
|
||||
}
|
||||
}.start();
|
||||
|
||||
// Create output thread for each output port
|
||||
for (int port = 0; port < outputCount; port++) {
|
||||
// Create output thread for each input port of the physical device
|
||||
for (int port = 0; port < outputStreamCount; port++) {
|
||||
final MidiEventScheduler eventSchedulerF = mEventSchedulers[port];
|
||||
final FileOutputStream outputStreamF = mOutputStreams[port];
|
||||
final int portF = port;
|
||||
|
||||
Reference in New Issue
Block a user