Loading Android.mk +1 −1 Original line number Diff line number Diff line Loading @@ -333,8 +333,8 @@ LOCAL_SRC_FILES += \ media/java/android/media/IRingtonePlayer.aidl \ media/java/android/media/IVolumeController.aidl \ media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \ media/java/android/media/midi/IMidiDeviceListener.aidl \ media/java/android/media/midi/IMidiDeviceServer.aidl \ media/java/android/media/midi/IMidiListener.aidl \ media/java/android/media/midi/IMidiManager.aidl \ media/java/android/media/projection/IMediaProjection.aidl \ media/java/android/media/projection/IMediaProjectionCallback.aidl \ Loading media/java/android/media/midi/IMidiListener.aidl→media/java/android/media/midi/IMidiDeviceListener.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package android.media.midi; import android.media.midi.MidiDeviceInfo; /** @hide */ oneway interface IMidiListener oneway interface IMidiDeviceListener { void onDeviceAdded(in MidiDeviceInfo device); void onDeviceRemoved(in MidiDeviceInfo device); Loading media/java/android/media/midi/IMidiManager.aidl +12 −6 Original line number Diff line number Diff line Loading @@ -16,8 +16,8 @@ package android.media.midi; import android.media.midi.IMidiDeviceListener; import android.media.midi.IMidiDeviceServer; import android.media.midi.IMidiListener; import android.media.midi.MidiDeviceInfo; import android.os.Bundle; import android.os.IBinder; Loading @@ -28,14 +28,20 @@ interface IMidiManager MidiDeviceInfo[] getDeviceList(); // for device creation & removal notifications void registerListener(IBinder token, in IMidiListener listener); void unregisterListener(IBinder token, in IMidiListener listener); void registerListener(IBinder token, in IMidiDeviceListener listener); void unregisterListener(IBinder token, in IMidiDeviceListener listener); // for communicating with MIDI devices // for opening built-in MIDI devices IMidiDeviceServer openDevice(IBinder token, in MidiDeviceInfo device); // for implementing virtual MIDI devices // for registering built-in MIDI devices MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts, int numOutputPorts, in Bundle properties, boolean isPrivate, int type); int numOutputPorts, in Bundle properties, int type); // for unregistering built-in MIDI devices void unregisterDeviceServer(in IMidiDeviceServer server); // used by MidiDeviceService to access the MidiDeviceInfo that was created based on its // manifest's meta-data MidiDeviceInfo getServiceDeviceInfo(String packageName, String className); } media/java/android/media/midi/MidiDeviceInfo.java +31 −3 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ public class MidiDeviceInfo implements Parcelable { private final int mInputPortCount; private final int mOutputPortCount; private final Bundle mProperties; private final boolean mIsPrivate; /** * Bundle key for the device's manufacturer name property. Loading Loading @@ -83,6 +84,8 @@ public class MidiDeviceInfo implements Parcelable { * Bundle key for the device's ALSA card number. * Only set for USB MIDI devices. * Used with the {@link android.os.Bundle} returned by {@link #getProperties} * * @hide */ public static final String PROPERTY_ALSA_CARD = "alsa_card"; Loading @@ -90,20 +93,32 @@ public class MidiDeviceInfo implements Parcelable { * Bundle key for the device's ALSA device number. * Only set for USB MIDI devices. * Used with the {@link android.os.Bundle} returned by {@link #getProperties} * * @hide */ public static final String PROPERTY_ALSA_DEVICE = "alsa_device"; /** * {@link android.content.pm.ServiceInfo} for the service hosting the device implementation. * Only set for Virtual MIDI devices. * Used with the {@link android.os.Bundle} returned by {@link #getProperties} * * @hide */ public static final String PROPERTY_SERVICE_INFO = "service_info"; /** * MidiDeviceInfo should only be instantiated by MidiService implementation * @hide */ public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts, Bundle properties) { Bundle properties, boolean isPrivate) { mType = type; mId = id; mInputPortCount = numInputPorts; mOutputPortCount = numOutputPorts; mProperties = properties; mIsPrivate = isPrivate; } /** Loading Loading @@ -152,6 +167,16 @@ public class MidiDeviceInfo implements Parcelable { return mProperties; } /** * Returns true if the device is private. Private devices are only visible and accessible * to clients with the same UID as the application that is hosting the device. * * @return true if the device is private */ public boolean isPrivate() { return mIsPrivate; } @Override public boolean equals(Object o) { if (o instanceof MidiDeviceInfo) { Loading @@ -171,7 +196,8 @@ public class MidiDeviceInfo implements Parcelable { return ("MidiDeviceInfo[mType=" + mType + ",mInputPortCount=" + mInputPortCount + ",mOutputPortCount=" + mOutputPortCount + ",mProperties=" + mProperties); ",mProperties=" + mProperties + ",mIsPrivate=" + mIsPrivate); } public static final Parcelable.Creator<MidiDeviceInfo> CREATOR = Loading @@ -182,7 +208,8 @@ public class MidiDeviceInfo implements Parcelable { int inputPorts = in.readInt(); int outputPorts = in.readInt(); Bundle properties = in.readBundle(); return new MidiDeviceInfo(type, id, inputPorts, outputPorts, properties); boolean isPrivate = (in.readInt() == 1); return new MidiDeviceInfo(type, id, inputPorts, outputPorts, properties, isPrivate); } public MidiDeviceInfo[] newArray(int size) { Loading @@ -200,5 +227,6 @@ public class MidiDeviceInfo implements Parcelable { parcel.writeInt(mInputPortCount); parcel.writeInt(mOutputPortCount); parcel.writeBundle(mProperties); parcel.writeInt(mIsPrivate ? 1 : 0); } } media/java/android/media/midi/MidiDeviceServer.java +90 −164 Original line number Diff line number Diff line Loading @@ -16,21 +16,22 @@ package android.media.midi; import android.os.IBinder; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.system.OsConstants; import android.util.Log; import libcore.io.IoUtils; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; /** * This class is used to provide the implemention of MIDI device. * Applications may call {@link MidiManager#createDeviceServer} * to create an instance of this class to implement a virtual MIDI device. * Internal class used for providing an implementation for a MIDI device. * * CANDIDATE FOR PUBLIC API * @hide */ public final class MidiDeviceServer implements Closeable { Loading @@ -40,64 +41,36 @@ public final class MidiDeviceServer implements Closeable { // MidiDeviceInfo for the device implemented by this server private MidiDeviceInfo mDeviceInfo; private int mInputPortCount; private int mOutputPortCount; // output ports for receiving messages from our clients // we can have only one per port number private MidiOutputPort[] mInputPortSenders; // receivers attached to our input ports private ArrayList<MidiReceiver>[] mInputPortReceivers; // input ports for sending messages to our clients // we can have multiple outputs per port number private ArrayList<MidiInputPort>[] mOutputPortReceivers; // subclass of MidiInputPort for passing to clients // that notifies us when the connection has failed private class ServerInputPort extends MidiInputPort { ServerInputPort(ParcelFileDescriptor pfd, int portNumber) { super(pfd, portNumber); } private final int mInputPortCount; private final int mOutputPortCount; @Override public void onIOException() { synchronized (mOutputPortReceivers) { mOutputPortReceivers[getPortNumber()].clear(); } } } // MidiReceivers for receiving data on our input ports private final MidiReceiver[] mInputPortReceivers; // subclass of MidiOutputPort for passing to clients // that notifies us when the connection has failed private class ServerOutputPort extends MidiOutputPort { ServerOutputPort(ParcelFileDescriptor pfd, int portNumber) { super(pfd, portNumber); } // MidiDispatchers for sending data on our output ports private MidiDispatcher[] mOutputPortDispatchers; @Override public void onIOException() { synchronized (mInputPortSenders) { mInputPortSenders[getPortNumber()] = null; } } } // MidiOutputPorts for clients connected to our input ports private final MidiOutputPort[] mInputPortOutputPorts; // Binder interface stub for receiving connection requests from clients private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() { @Override public ParcelFileDescriptor openInputPort(int portNumber) { if (mDeviceInfo.isPrivate()) { if (Binder.getCallingUid() != Process.myUid()) { throw new SecurityException("Can't access private device from different UID"); } } if (portNumber < 0 || portNumber >= mInputPortCount) { Log.e(TAG, "portNumber out of range in openInputPort: " + portNumber); return null; } ParcelFileDescriptor result = null; synchronized (mInputPortSenders) { if (mInputPortSenders[portNumber] != null) { synchronized (mInputPortOutputPorts) { if (mInputPortOutputPorts[portNumber] != null) { Log.d(TAG, "port " + portNumber + " already open"); return null; } Loading @@ -105,47 +78,86 @@ public final class MidiDeviceServer implements Closeable { try { ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair( OsConstants.SOCK_SEQPACKET); MidiOutputPort newOutputPort = new ServerOutputPort(pair[0], portNumber); mInputPortSenders[portNumber] = newOutputPort; result = pair[1]; final MidiOutputPort outputPort = new MidiOutputPort(pair[0], portNumber); mInputPortOutputPorts[portNumber] = outputPort; final int portNumberF = portNumber; final MidiReceiver inputPortReceviver = mInputPortReceivers[portNumber]; ArrayList<MidiReceiver> receivers = mInputPortReceivers[portNumber]; synchronized (receivers) { for (int i = 0; i < receivers.size(); i++) { newOutputPort.connect(receivers.get(i)); outputPort.connect(new MidiReceiver() { @Override public void post(byte[] msg, int offset, int count, long timestamp) throws IOException { try { inputPortReceviver.post(msg, offset, count, timestamp); } catch (IOException e) { IoUtils.closeQuietly(mInputPortOutputPorts[portNumberF]); mInputPortOutputPorts[portNumberF] = null; // FIXME also flush the receiver } } }); return pair[1]; } catch (IOException e) { Log.e(TAG, "unable to create ParcelFileDescriptors in openInputPort"); return null; } } return result; } @Override public ParcelFileDescriptor openOutputPort(int portNumber) { if (mDeviceInfo.isPrivate()) { if (Binder.getCallingUid() != Process.myUid()) { throw new SecurityException("Can't access private device from different UID"); } } if (portNumber < 0 || portNumber >= mOutputPortCount) { Log.e(TAG, "portNumber out of range in openOutputPort: " + portNumber); return null; } synchronized (mOutputPortReceivers) { try { ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair( OsConstants.SOCK_SEQPACKET); mOutputPortReceivers[portNumber].add(new ServerInputPort(pair[0], portNumber)); final MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber); final MidiSender sender = mOutputPortDispatchers[portNumber].getSender(); sender.connect(new MidiReceiver() { @Override public void post(byte[] msg, int offset, int count, long timestamp) throws IOException { try { inputPort.post(msg, offset, count, timestamp); } catch (IOException e) { IoUtils.closeQuietly(inputPort); sender.disconnect(this); // FIXME also flush the receiver? } } }); return pair[1]; } catch (IOException e) { Log.e(TAG, "unable to create ParcelFileDescriptors in openOutputPort"); return null; } } } }; /* package */ MidiDeviceServer(IMidiManager midiManager) { /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers, int numOutputPorts) { mMidiManager = midiManager; mInputPortReceivers = inputPortReceivers; mInputPortCount = inputPortReceivers.length; mOutputPortCount = numOutputPorts; mInputPortOutputPorts = new MidiOutputPort[mInputPortCount]; mOutputPortDispatchers = new MidiDispatcher[numOutputPorts]; for (int i = 0; i < numOutputPorts; i++) { mOutputPortDispatchers[i] = new MidiDispatcher(); } } /* package */ IMidiDeviceServer getBinderInterface() { Loading @@ -157,19 +169,6 @@ public final class MidiDeviceServer implements Closeable { throw new IllegalStateException("setDeviceInfo should only be called once"); } mDeviceInfo = deviceInfo; mInputPortCount = deviceInfo.getInputPortCount(); mOutputPortCount = deviceInfo.getOutputPortCount(); mInputPortSenders = new MidiOutputPort[mInputPortCount]; mInputPortReceivers = new ArrayList[mInputPortCount]; for (int i = 0; i < mInputPortCount; i++) { mInputPortReceivers[i] = new ArrayList<MidiReceiver>(); } mOutputPortReceivers = new ArrayList[mOutputPortCount]; for (int i = 0; i < mOutputPortCount; i++) { mOutputPortReceivers[i] = new ArrayList<MidiInputPort>(); } } @Override Loading @@ -183,86 +182,13 @@ public final class MidiDeviceServer implements Closeable { } /** * Returns a {@link MidiDeviceInfo} object, which describes this device. * * @return the {@link MidiDeviceInfo} object */ public MidiDeviceInfo getInfo() { return mDeviceInfo; } /** * Called to open a {@link MidiSender} to allow receiving MIDI messages * on the device's input port for the specified port number. * * @param portNumber the number of the input port * @return the {@link MidiSender} * Returns an array of {@link MidiReceiver} for the device's output ports. * Clients can use these receivers to send data out the device's output ports. * @return array of MidiReceivers */ public MidiSender openInputPortSender(int portNumber) { if (portNumber < 0 || portNumber >= mDeviceInfo.getInputPortCount()) { throw new IllegalArgumentException("portNumber " + portNumber + " out of range"); } final int portNumberF = portNumber; return new MidiSender() { @Override public void connect(MidiReceiver receiver) { // We always synchronize on mInputPortSenders before receivers if we need to // synchronize on both. synchronized (mInputPortSenders) { ArrayList<MidiReceiver> receivers = mInputPortReceivers[portNumberF]; synchronized (receivers) { receivers.add(receiver); MidiOutputPort outputPort = mInputPortSenders[portNumberF]; if (outputPort != null) { outputPort.connect(receiver); } } } } @Override public void disconnect(MidiReceiver receiver) { // We always synchronize on mInputPortSenders before receivers if we need to // synchronize on both. synchronized (mInputPortSenders) { ArrayList<MidiReceiver> receivers = mInputPortReceivers[portNumberF]; synchronized (receivers) { receivers.remove(receiver); MidiOutputPort outputPort = mInputPortSenders[portNumberF]; if (outputPort != null) { outputPort.disconnect(receiver); } } } } }; } /** * Called to open a {@link MidiReceiver} to allow sending MIDI messages * on the virtual device's output port for the specified port number. * * @param portNumber the number of the output port * @return the {@link MidiReceiver} */ public MidiReceiver openOutputPortReceiver(int portNumber) { if (portNumber < 0 || portNumber >= mDeviceInfo.getOutputPortCount()) { throw new IllegalArgumentException("portNumber " + portNumber + " out of range"); } final int portNumberF = portNumber; return new MidiReceiver() { @Override public void post(byte[] msg, int offset, int count, long timestamp) throws IOException { ArrayList<MidiInputPort> receivers = mOutputPortReceivers[portNumberF]; synchronized (receivers) { for (int i = 0; i < receivers.size(); i++) { // FIXME catch errors and remove dead ones receivers.get(i).post(msg, offset, count, timestamp); } } } }; public MidiReceiver[] getOutputPortReceivers() { MidiReceiver[] receivers = new MidiReceiver[mOutputPortCount]; System.arraycopy(mOutputPortDispatchers, 0, receivers, 0, mOutputPortCount); return receivers; } } Loading
Android.mk +1 −1 Original line number Diff line number Diff line Loading @@ -333,8 +333,8 @@ LOCAL_SRC_FILES += \ media/java/android/media/IRingtonePlayer.aidl \ media/java/android/media/IVolumeController.aidl \ media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \ media/java/android/media/midi/IMidiDeviceListener.aidl \ media/java/android/media/midi/IMidiDeviceServer.aidl \ media/java/android/media/midi/IMidiListener.aidl \ media/java/android/media/midi/IMidiManager.aidl \ media/java/android/media/projection/IMediaProjection.aidl \ media/java/android/media/projection/IMediaProjectionCallback.aidl \ Loading
media/java/android/media/midi/IMidiListener.aidl→media/java/android/media/midi/IMidiDeviceListener.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package android.media.midi; import android.media.midi.MidiDeviceInfo; /** @hide */ oneway interface IMidiListener oneway interface IMidiDeviceListener { void onDeviceAdded(in MidiDeviceInfo device); void onDeviceRemoved(in MidiDeviceInfo device); Loading
media/java/android/media/midi/IMidiManager.aidl +12 −6 Original line number Diff line number Diff line Loading @@ -16,8 +16,8 @@ package android.media.midi; import android.media.midi.IMidiDeviceListener; import android.media.midi.IMidiDeviceServer; import android.media.midi.IMidiListener; import android.media.midi.MidiDeviceInfo; import android.os.Bundle; import android.os.IBinder; Loading @@ -28,14 +28,20 @@ interface IMidiManager MidiDeviceInfo[] getDeviceList(); // for device creation & removal notifications void registerListener(IBinder token, in IMidiListener listener); void unregisterListener(IBinder token, in IMidiListener listener); void registerListener(IBinder token, in IMidiDeviceListener listener); void unregisterListener(IBinder token, in IMidiDeviceListener listener); // for communicating with MIDI devices // for opening built-in MIDI devices IMidiDeviceServer openDevice(IBinder token, in MidiDeviceInfo device); // for implementing virtual MIDI devices // for registering built-in MIDI devices MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts, int numOutputPorts, in Bundle properties, boolean isPrivate, int type); int numOutputPorts, in Bundle properties, int type); // for unregistering built-in MIDI devices void unregisterDeviceServer(in IMidiDeviceServer server); // used by MidiDeviceService to access the MidiDeviceInfo that was created based on its // manifest's meta-data MidiDeviceInfo getServiceDeviceInfo(String packageName, String className); }
media/java/android/media/midi/MidiDeviceInfo.java +31 −3 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ public class MidiDeviceInfo implements Parcelable { private final int mInputPortCount; private final int mOutputPortCount; private final Bundle mProperties; private final boolean mIsPrivate; /** * Bundle key for the device's manufacturer name property. Loading Loading @@ -83,6 +84,8 @@ public class MidiDeviceInfo implements Parcelable { * Bundle key for the device's ALSA card number. * Only set for USB MIDI devices. * Used with the {@link android.os.Bundle} returned by {@link #getProperties} * * @hide */ public static final String PROPERTY_ALSA_CARD = "alsa_card"; Loading @@ -90,20 +93,32 @@ public class MidiDeviceInfo implements Parcelable { * Bundle key for the device's ALSA device number. * Only set for USB MIDI devices. * Used with the {@link android.os.Bundle} returned by {@link #getProperties} * * @hide */ public static final String PROPERTY_ALSA_DEVICE = "alsa_device"; /** * {@link android.content.pm.ServiceInfo} for the service hosting the device implementation. * Only set for Virtual MIDI devices. * Used with the {@link android.os.Bundle} returned by {@link #getProperties} * * @hide */ public static final String PROPERTY_SERVICE_INFO = "service_info"; /** * MidiDeviceInfo should only be instantiated by MidiService implementation * @hide */ public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts, Bundle properties) { Bundle properties, boolean isPrivate) { mType = type; mId = id; mInputPortCount = numInputPorts; mOutputPortCount = numOutputPorts; mProperties = properties; mIsPrivate = isPrivate; } /** Loading Loading @@ -152,6 +167,16 @@ public class MidiDeviceInfo implements Parcelable { return mProperties; } /** * Returns true if the device is private. Private devices are only visible and accessible * to clients with the same UID as the application that is hosting the device. * * @return true if the device is private */ public boolean isPrivate() { return mIsPrivate; } @Override public boolean equals(Object o) { if (o instanceof MidiDeviceInfo) { Loading @@ -171,7 +196,8 @@ public class MidiDeviceInfo implements Parcelable { return ("MidiDeviceInfo[mType=" + mType + ",mInputPortCount=" + mInputPortCount + ",mOutputPortCount=" + mOutputPortCount + ",mProperties=" + mProperties); ",mProperties=" + mProperties + ",mIsPrivate=" + mIsPrivate); } public static final Parcelable.Creator<MidiDeviceInfo> CREATOR = Loading @@ -182,7 +208,8 @@ public class MidiDeviceInfo implements Parcelable { int inputPorts = in.readInt(); int outputPorts = in.readInt(); Bundle properties = in.readBundle(); return new MidiDeviceInfo(type, id, inputPorts, outputPorts, properties); boolean isPrivate = (in.readInt() == 1); return new MidiDeviceInfo(type, id, inputPorts, outputPorts, properties, isPrivate); } public MidiDeviceInfo[] newArray(int size) { Loading @@ -200,5 +227,6 @@ public class MidiDeviceInfo implements Parcelable { parcel.writeInt(mInputPortCount); parcel.writeInt(mOutputPortCount); parcel.writeBundle(mProperties); parcel.writeInt(mIsPrivate ? 1 : 0); } }
media/java/android/media/midi/MidiDeviceServer.java +90 −164 Original line number Diff line number Diff line Loading @@ -16,21 +16,22 @@ package android.media.midi; import android.os.IBinder; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.system.OsConstants; import android.util.Log; import libcore.io.IoUtils; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; /** * This class is used to provide the implemention of MIDI device. * Applications may call {@link MidiManager#createDeviceServer} * to create an instance of this class to implement a virtual MIDI device. * Internal class used for providing an implementation for a MIDI device. * * CANDIDATE FOR PUBLIC API * @hide */ public final class MidiDeviceServer implements Closeable { Loading @@ -40,64 +41,36 @@ public final class MidiDeviceServer implements Closeable { // MidiDeviceInfo for the device implemented by this server private MidiDeviceInfo mDeviceInfo; private int mInputPortCount; private int mOutputPortCount; // output ports for receiving messages from our clients // we can have only one per port number private MidiOutputPort[] mInputPortSenders; // receivers attached to our input ports private ArrayList<MidiReceiver>[] mInputPortReceivers; // input ports for sending messages to our clients // we can have multiple outputs per port number private ArrayList<MidiInputPort>[] mOutputPortReceivers; // subclass of MidiInputPort for passing to clients // that notifies us when the connection has failed private class ServerInputPort extends MidiInputPort { ServerInputPort(ParcelFileDescriptor pfd, int portNumber) { super(pfd, portNumber); } private final int mInputPortCount; private final int mOutputPortCount; @Override public void onIOException() { synchronized (mOutputPortReceivers) { mOutputPortReceivers[getPortNumber()].clear(); } } } // MidiReceivers for receiving data on our input ports private final MidiReceiver[] mInputPortReceivers; // subclass of MidiOutputPort for passing to clients // that notifies us when the connection has failed private class ServerOutputPort extends MidiOutputPort { ServerOutputPort(ParcelFileDescriptor pfd, int portNumber) { super(pfd, portNumber); } // MidiDispatchers for sending data on our output ports private MidiDispatcher[] mOutputPortDispatchers; @Override public void onIOException() { synchronized (mInputPortSenders) { mInputPortSenders[getPortNumber()] = null; } } } // MidiOutputPorts for clients connected to our input ports private final MidiOutputPort[] mInputPortOutputPorts; // Binder interface stub for receiving connection requests from clients private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() { @Override public ParcelFileDescriptor openInputPort(int portNumber) { if (mDeviceInfo.isPrivate()) { if (Binder.getCallingUid() != Process.myUid()) { throw new SecurityException("Can't access private device from different UID"); } } if (portNumber < 0 || portNumber >= mInputPortCount) { Log.e(TAG, "portNumber out of range in openInputPort: " + portNumber); return null; } ParcelFileDescriptor result = null; synchronized (mInputPortSenders) { if (mInputPortSenders[portNumber] != null) { synchronized (mInputPortOutputPorts) { if (mInputPortOutputPorts[portNumber] != null) { Log.d(TAG, "port " + portNumber + " already open"); return null; } Loading @@ -105,47 +78,86 @@ public final class MidiDeviceServer implements Closeable { try { ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair( OsConstants.SOCK_SEQPACKET); MidiOutputPort newOutputPort = new ServerOutputPort(pair[0], portNumber); mInputPortSenders[portNumber] = newOutputPort; result = pair[1]; final MidiOutputPort outputPort = new MidiOutputPort(pair[0], portNumber); mInputPortOutputPorts[portNumber] = outputPort; final int portNumberF = portNumber; final MidiReceiver inputPortReceviver = mInputPortReceivers[portNumber]; ArrayList<MidiReceiver> receivers = mInputPortReceivers[portNumber]; synchronized (receivers) { for (int i = 0; i < receivers.size(); i++) { newOutputPort.connect(receivers.get(i)); outputPort.connect(new MidiReceiver() { @Override public void post(byte[] msg, int offset, int count, long timestamp) throws IOException { try { inputPortReceviver.post(msg, offset, count, timestamp); } catch (IOException e) { IoUtils.closeQuietly(mInputPortOutputPorts[portNumberF]); mInputPortOutputPorts[portNumberF] = null; // FIXME also flush the receiver } } }); return pair[1]; } catch (IOException e) { Log.e(TAG, "unable to create ParcelFileDescriptors in openInputPort"); return null; } } return result; } @Override public ParcelFileDescriptor openOutputPort(int portNumber) { if (mDeviceInfo.isPrivate()) { if (Binder.getCallingUid() != Process.myUid()) { throw new SecurityException("Can't access private device from different UID"); } } if (portNumber < 0 || portNumber >= mOutputPortCount) { Log.e(TAG, "portNumber out of range in openOutputPort: " + portNumber); return null; } synchronized (mOutputPortReceivers) { try { ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair( OsConstants.SOCK_SEQPACKET); mOutputPortReceivers[portNumber].add(new ServerInputPort(pair[0], portNumber)); final MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber); final MidiSender sender = mOutputPortDispatchers[portNumber].getSender(); sender.connect(new MidiReceiver() { @Override public void post(byte[] msg, int offset, int count, long timestamp) throws IOException { try { inputPort.post(msg, offset, count, timestamp); } catch (IOException e) { IoUtils.closeQuietly(inputPort); sender.disconnect(this); // FIXME also flush the receiver? } } }); return pair[1]; } catch (IOException e) { Log.e(TAG, "unable to create ParcelFileDescriptors in openOutputPort"); return null; } } } }; /* package */ MidiDeviceServer(IMidiManager midiManager) { /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers, int numOutputPorts) { mMidiManager = midiManager; mInputPortReceivers = inputPortReceivers; mInputPortCount = inputPortReceivers.length; mOutputPortCount = numOutputPorts; mInputPortOutputPorts = new MidiOutputPort[mInputPortCount]; mOutputPortDispatchers = new MidiDispatcher[numOutputPorts]; for (int i = 0; i < numOutputPorts; i++) { mOutputPortDispatchers[i] = new MidiDispatcher(); } } /* package */ IMidiDeviceServer getBinderInterface() { Loading @@ -157,19 +169,6 @@ public final class MidiDeviceServer implements Closeable { throw new IllegalStateException("setDeviceInfo should only be called once"); } mDeviceInfo = deviceInfo; mInputPortCount = deviceInfo.getInputPortCount(); mOutputPortCount = deviceInfo.getOutputPortCount(); mInputPortSenders = new MidiOutputPort[mInputPortCount]; mInputPortReceivers = new ArrayList[mInputPortCount]; for (int i = 0; i < mInputPortCount; i++) { mInputPortReceivers[i] = new ArrayList<MidiReceiver>(); } mOutputPortReceivers = new ArrayList[mOutputPortCount]; for (int i = 0; i < mOutputPortCount; i++) { mOutputPortReceivers[i] = new ArrayList<MidiInputPort>(); } } @Override Loading @@ -183,86 +182,13 @@ public final class MidiDeviceServer implements Closeable { } /** * Returns a {@link MidiDeviceInfo} object, which describes this device. * * @return the {@link MidiDeviceInfo} object */ public MidiDeviceInfo getInfo() { return mDeviceInfo; } /** * Called to open a {@link MidiSender} to allow receiving MIDI messages * on the device's input port for the specified port number. * * @param portNumber the number of the input port * @return the {@link MidiSender} * Returns an array of {@link MidiReceiver} for the device's output ports. * Clients can use these receivers to send data out the device's output ports. * @return array of MidiReceivers */ public MidiSender openInputPortSender(int portNumber) { if (portNumber < 0 || portNumber >= mDeviceInfo.getInputPortCount()) { throw new IllegalArgumentException("portNumber " + portNumber + " out of range"); } final int portNumberF = portNumber; return new MidiSender() { @Override public void connect(MidiReceiver receiver) { // We always synchronize on mInputPortSenders before receivers if we need to // synchronize on both. synchronized (mInputPortSenders) { ArrayList<MidiReceiver> receivers = mInputPortReceivers[portNumberF]; synchronized (receivers) { receivers.add(receiver); MidiOutputPort outputPort = mInputPortSenders[portNumberF]; if (outputPort != null) { outputPort.connect(receiver); } } } } @Override public void disconnect(MidiReceiver receiver) { // We always synchronize on mInputPortSenders before receivers if we need to // synchronize on both. synchronized (mInputPortSenders) { ArrayList<MidiReceiver> receivers = mInputPortReceivers[portNumberF]; synchronized (receivers) { receivers.remove(receiver); MidiOutputPort outputPort = mInputPortSenders[portNumberF]; if (outputPort != null) { outputPort.disconnect(receiver); } } } } }; } /** * Called to open a {@link MidiReceiver} to allow sending MIDI messages * on the virtual device's output port for the specified port number. * * @param portNumber the number of the output port * @return the {@link MidiReceiver} */ public MidiReceiver openOutputPortReceiver(int portNumber) { if (portNumber < 0 || portNumber >= mDeviceInfo.getOutputPortCount()) { throw new IllegalArgumentException("portNumber " + portNumber + " out of range"); } final int portNumberF = portNumber; return new MidiReceiver() { @Override public void post(byte[] msg, int offset, int count, long timestamp) throws IOException { ArrayList<MidiInputPort> receivers = mOutputPortReceivers[portNumberF]; synchronized (receivers) { for (int i = 0; i < receivers.size(); i++) { // FIXME catch errors and remove dead ones receivers.get(i).post(msg, offset, count, timestamp); } } } }; public MidiReceiver[] getOutputPortReceivers() { MidiReceiver[] receivers = new MidiReceiver[mOutputPortCount]; System.arraycopy(mOutputPortDispatchers, 0, receivers, 0, mOutputPortCount); return receivers; } }