Loading core/java/com/android/internal/midi/MidiDispatcher.java +37 −4 Original line number Diff line number Diff line Loading @@ -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>(); Loading @@ -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 Loading @@ -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); } } } } Loading @@ -79,7 +104,15 @@ public final class MidiDispatcher extends MidiReceiver { @Override public void onFlush() throws IOException { for (MidiReceiver receiver : mReceivers) { 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); } } } } } media/java/android/media/midi/IMidiDeviceServer.aidl +2 −1 Original line number Diff line number Diff line Loading @@ -28,7 +28,8 @@ interface IMidiDeviceServer void closeDevice(); // connects the input port pfd to the specified output port void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber); // Returns the PID of the called process. int connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber); MidiDeviceInfo getDeviceInfo(); void setDeviceInfo(in MidiDeviceInfo deviceInfo); Loading media/java/android/media/midi/MidiDevice.java +30 −9 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.media.midi; import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.util.Log; Loading @@ -41,6 +42,7 @@ public final class MidiDevice implements Closeable { private final IMidiManager mMidiManager; private final IBinder mClientToken; private final IBinder mDeviceToken; private boolean mIsDeviceClosed; private final CloseGuard mGuard = CloseGuard.get(); Loading Loading @@ -122,6 +124,9 @@ public final class MidiDevice implements Closeable { * or null in case of failure. */ public MidiInputPort openInputPort(int portNumber) { if (mIsDeviceClosed) { return null; } try { IBinder token = new Binder(); ParcelFileDescriptor pfd = mDeviceServer.openInputPort(token, portNumber); Loading @@ -145,6 +150,9 @@ public final class MidiDevice implements Closeable { * or null in case of failure. */ public MidiOutputPort openOutputPort(int portNumber) { if (mIsDeviceClosed) { return null; } try { IBinder token = new Binder(); ParcelFileDescriptor pfd = mDeviceServer.openOutputPort(token, portNumber); Loading Loading @@ -174,6 +182,9 @@ public final class MidiDevice implements Closeable { if (outputPortNumber < 0 || outputPortNumber >= mDeviceInfo.getOutputPortCount()) { throw new IllegalArgumentException("outputPortNumber out of range"); } if (mIsDeviceClosed) { return null; } ParcelFileDescriptor pfd = inputPort.claimFileDescriptor(); if (pfd == null) { Loading @@ -181,9 +192,16 @@ public final class MidiDevice implements Closeable { } try { IBinder token = new Binder(); mDeviceServer.connectPorts(token, pfd, outputPortNumber); int calleePid = mDeviceServer.connectPorts(token, pfd, outputPortNumber); // If the service is a different Process then it will duplicate the pfd // and we can safely close this one. // But if the service is in the same Process then closing the pfd will // kill the connection. So don't do that. if (calleePid != Process.myPid()) { // close our copy of the file descriptor IoUtils.closeQuietly(pfd); } return new MidiConnection(token, inputPort); } catch (RemoteException e) { Log.e(TAG, "RemoteException in connectPorts"); Loading @@ -194,7 +212,9 @@ public final class MidiDevice implements Closeable { @Override public void close() throws IOException { synchronized (mGuard) { if (!mIsDeviceClosed) { mGuard.close(); mIsDeviceClosed = true; try { mMidiManager.closeDevice(mClientToken, mDeviceToken); } catch (RemoteException e) { Loading @@ -202,6 +222,7 @@ public final class MidiDevice implements Closeable { } } } } @Override protected void finalize() throws Throwable { Loading media/java/android/media/midi/MidiDeviceServer.java +49 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -102,6 +106,10 @@ public final class MidiDeviceServer implements Closeable { abstract void close(); MidiInputPort getInputPort() { return null; } @Override public void binderDied() { close(); Loading Loading @@ -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() { Loading Loading @@ -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); Loading @@ -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"); Loading @@ -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 Loading @@ -254,7 +281,7 @@ public final class MidiDeviceServer implements Closeable { } @Override public void connectPorts(IBinder token, ParcelFileDescriptor pfd, public int connectPorts(IBinder token, ParcelFileDescriptor pfd, int outputPortNumber) { MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber); MidiDispatcher dispatcher = mOutputPortDispatchers[outputPortNumber]; Loading @@ -270,6 +297,10 @@ 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 } @Override Loading Loading @@ -302,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]; Loading @@ -311,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) { Loading media/java/android/media/midi/MidiOutputPort.java +1 −1 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading
core/java/com/android/internal/midi/MidiDispatcher.java +37 −4 Original line number Diff line number Diff line Loading @@ -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>(); Loading @@ -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 Loading @@ -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); } } } } Loading @@ -79,7 +104,15 @@ public final class MidiDispatcher extends MidiReceiver { @Override public void onFlush() throws IOException { for (MidiReceiver receiver : mReceivers) { 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); } } } } }
media/java/android/media/midi/IMidiDeviceServer.aidl +2 −1 Original line number Diff line number Diff line Loading @@ -28,7 +28,8 @@ interface IMidiDeviceServer void closeDevice(); // connects the input port pfd to the specified output port void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber); // Returns the PID of the called process. int connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber); MidiDeviceInfo getDeviceInfo(); void setDeviceInfo(in MidiDeviceInfo deviceInfo); Loading
media/java/android/media/midi/MidiDevice.java +30 −9 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.media.midi; import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.util.Log; Loading @@ -41,6 +42,7 @@ public final class MidiDevice implements Closeable { private final IMidiManager mMidiManager; private final IBinder mClientToken; private final IBinder mDeviceToken; private boolean mIsDeviceClosed; private final CloseGuard mGuard = CloseGuard.get(); Loading Loading @@ -122,6 +124,9 @@ public final class MidiDevice implements Closeable { * or null in case of failure. */ public MidiInputPort openInputPort(int portNumber) { if (mIsDeviceClosed) { return null; } try { IBinder token = new Binder(); ParcelFileDescriptor pfd = mDeviceServer.openInputPort(token, portNumber); Loading @@ -145,6 +150,9 @@ public final class MidiDevice implements Closeable { * or null in case of failure. */ public MidiOutputPort openOutputPort(int portNumber) { if (mIsDeviceClosed) { return null; } try { IBinder token = new Binder(); ParcelFileDescriptor pfd = mDeviceServer.openOutputPort(token, portNumber); Loading Loading @@ -174,6 +182,9 @@ public final class MidiDevice implements Closeable { if (outputPortNumber < 0 || outputPortNumber >= mDeviceInfo.getOutputPortCount()) { throw new IllegalArgumentException("outputPortNumber out of range"); } if (mIsDeviceClosed) { return null; } ParcelFileDescriptor pfd = inputPort.claimFileDescriptor(); if (pfd == null) { Loading @@ -181,9 +192,16 @@ public final class MidiDevice implements Closeable { } try { IBinder token = new Binder(); mDeviceServer.connectPorts(token, pfd, outputPortNumber); int calleePid = mDeviceServer.connectPorts(token, pfd, outputPortNumber); // If the service is a different Process then it will duplicate the pfd // and we can safely close this one. // But if the service is in the same Process then closing the pfd will // kill the connection. So don't do that. if (calleePid != Process.myPid()) { // close our copy of the file descriptor IoUtils.closeQuietly(pfd); } return new MidiConnection(token, inputPort); } catch (RemoteException e) { Log.e(TAG, "RemoteException in connectPorts"); Loading @@ -194,7 +212,9 @@ public final class MidiDevice implements Closeable { @Override public void close() throws IOException { synchronized (mGuard) { if (!mIsDeviceClosed) { mGuard.close(); mIsDeviceClosed = true; try { mMidiManager.closeDevice(mClientToken, mDeviceToken); } catch (RemoteException e) { Loading @@ -202,6 +222,7 @@ public final class MidiDevice implements Closeable { } } } } @Override protected void finalize() throws Throwable { Loading
media/java/android/media/midi/MidiDeviceServer.java +49 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -102,6 +106,10 @@ public final class MidiDeviceServer implements Closeable { abstract void close(); MidiInputPort getInputPort() { return null; } @Override public void binderDied() { close(); Loading Loading @@ -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() { Loading Loading @@ -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); Loading @@ -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"); Loading @@ -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 Loading @@ -254,7 +281,7 @@ public final class MidiDeviceServer implements Closeable { } @Override public void connectPorts(IBinder token, ParcelFileDescriptor pfd, public int connectPorts(IBinder token, ParcelFileDescriptor pfd, int outputPortNumber) { MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber); MidiDispatcher dispatcher = mOutputPortDispatchers[outputPortNumber]; Loading @@ -270,6 +297,10 @@ 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 } @Override Loading Loading @@ -302,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]; Loading @@ -311,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) { Loading
media/java/android/media/midi/MidiOutputPort.java +1 −1 Original line number Diff line number Diff line Loading @@ -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); } Loading