diff --git a/media/java/android/media/midi/IMidiDeviceServer.aidl b/media/java/android/media/midi/IMidiDeviceServer.aidl index 3331aae649bf9..642078aa669ac 100644 --- a/media/java/android/media/midi/IMidiDeviceServer.aidl +++ b/media/java/android/media/midi/IMidiDeviceServer.aidl @@ -24,4 +24,7 @@ interface IMidiDeviceServer ParcelFileDescriptor openInputPort(IBinder token, int portNumber); ParcelFileDescriptor openOutputPort(IBinder token, int portNumber); void closePort(IBinder token); + + // connects the input port pfd to the specified output port + void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber); } diff --git a/media/java/android/media/midi/MidiDevice.java b/media/java/android/media/midi/MidiDevice.java index af0737da516f5..569f7c6457686 100644 --- a/media/java/android/media/midi/MidiDevice.java +++ b/media/java/android/media/midi/MidiDevice.java @@ -26,6 +26,8 @@ import android.util.Log; import dalvik.system.CloseGuard; +import libcore.io.IoUtils; + import java.io.Closeable; import java.io.IOException; @@ -44,8 +46,29 @@ public final class MidiDevice implements Closeable { private Context mContext; private ServiceConnection mServiceConnection; + private final CloseGuard mGuard = CloseGuard.get(); + public class MidiConnection implements Closeable { + private final IBinder mToken; + private final MidiInputPort mInputPort; + + MidiConnection(IBinder token, MidiInputPort inputPort) { + mToken = token; + mInputPort = inputPort; + } + + @Override + public void close() throws IOException { + try { + mDeviceServer.closePort(mToken); + IoUtils.closeQuietly(mInputPort); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in MidiConnection.close"); + } + } + } + /* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server) { this(deviceInfo, server, null, null); } @@ -108,6 +131,36 @@ public final class MidiDevice implements Closeable { } } + /** + * Connects the supplied {@link MidiInputPort} to the output port of this device + * with the specified port number. Once the connection is made, the MidiInput port instance + * can no longer receive data via its {@link MidiReciever.receive} method. + * This method returns a {@link #MidiConnection} object, which can be used to close the connection + * @param inputPort the inputPort to connect + * @param outputPortNumber the port number of the output port to connect inputPort to. + * @return {@link #MidiConnection} object if the connection is successful, or null in case of failure + */ + public MidiConnection connectPorts(MidiInputPort inputPort, int outputPortNumber) { + if (outputPortNumber < 0 || outputPortNumber >= mDeviceInfo.getOutputPortCount()) { + throw new IllegalArgumentException("outputPortNumber out of range"); + } + + ParcelFileDescriptor pfd = inputPort.claimFileDescriptor(); + if (pfd == null) { + return null; + } + try { + IBinder token = new Binder(); + mDeviceServer.connectPorts(token, pfd, outputPortNumber); + // close our copy of the file descriptor + IoUtils.closeQuietly(pfd); + return new MidiConnection(token, inputPort); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in connectPorts"); + return null; + } + } + @Override public void close() throws IOException { synchronized (mGuard) { diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java index 3b4b6f0f02838..6531052a56100 100644 --- a/media/java/android/media/midi/MidiDeviceServer.java +++ b/media/java/android/media/midi/MidiDeviceServer.java @@ -200,6 +200,18 @@ public final class MidiDeviceServer implements Closeable { } } } + + @Override + public void connectPorts(IBinder token, ParcelFileDescriptor pfd, + int outputPortNumber) { + MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber); + mOutputPortDispatchers[outputPortNumber].getSender().connect(inputPort); + mInputPorts.add(inputPort); + OutputPortClient client = new OutputPortClient(token, inputPort); + synchronized (mPortClients) { + mPortClients.put(token, client); + } + } }; /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers, diff --git a/media/java/android/media/midi/MidiInputPort.java b/media/java/android/media/midi/MidiInputPort.java index 74e1fa41cd8a3..752075e1301c5 100644 --- a/media/java/android/media/midi/MidiInputPort.java +++ b/media/java/android/media/midi/MidiInputPort.java @@ -41,7 +41,8 @@ public final class MidiInputPort extends MidiReceiver implements Closeable { private IMidiDeviceServer mDeviceServer; private final IBinder mToken; private final int mPortNumber; - private final FileOutputStream mOutputStream; + private ParcelFileDescriptor mParcelFileDescriptor; + private FileOutputStream mOutputStream; private final CloseGuard mGuard = CloseGuard.get(); private boolean mIsClosed; @@ -53,8 +54,9 @@ public final class MidiInputPort extends MidiReceiver implements Closeable { ParcelFileDescriptor pfd, int portNumber) { mDeviceServer = server; mToken = token; + mParcelFileDescriptor = pfd; mPortNumber = portNumber; - mOutputStream = new ParcelFileDescriptor.AutoCloseOutputStream(pfd); + mOutputStream = new FileOutputStream(pfd.getFileDescriptor()); mGuard.open("close"); } @@ -89,11 +91,27 @@ public final class MidiInputPort extends MidiReceiver implements Closeable { } synchronized (mBuffer) { + if (mOutputStream == null) { + throw new IOException("MidiInputPort is closed"); + } int length = MidiPortImpl.packMessage(msg, offset, count, timestamp, mBuffer); mOutputStream.write(mBuffer, 0, length); } } + // used by MidiDevice.connectInputPort() to connect our socket directly to another device + /* package */ ParcelFileDescriptor claimFileDescriptor() { + synchronized (mBuffer) { + ParcelFileDescriptor pfd = mParcelFileDescriptor; + if (pfd != null) { + IoUtils.closeQuietly(mOutputStream); + mParcelFileDescriptor = null; + mOutputStream = null; + } + return pfd; + } + } + @Override public int getMaxMessageSize() { return MidiPortImpl.MAX_PACKET_DATA_SIZE; @@ -104,7 +122,16 @@ public final class MidiInputPort extends MidiReceiver implements Closeable { synchronized (mGuard) { if (mIsClosed) return; mGuard.close(); - mOutputStream.close(); + synchronized (mBuffer) { + if (mParcelFileDescriptor != null) { + mParcelFileDescriptor.close(); + mParcelFileDescriptor = null; + } + if (mOutputStream != null) { + mOutputStream.close(); + mOutputStream = null; + } + } if (mDeviceServer != null) { try { mDeviceServer.closePort(mToken);