OSDN Git Service

MidiDevice: Add support for making direct connections between ports
authorMike Lockwood <lockwood@google.com>
Fri, 6 Mar 2015 18:48:51 +0000 (10:48 -0800)
committerMike Lockwood <lockwood@google.com>
Wed, 11 Mar 2015 17:19:37 +0000 (10:19 -0700)
The output port of one device can be connected to the input port of another
device using the new MidiDevice.connectPorts() method.
This allows an application to direct the output of one device directly
to the input port of another without having to copy data from one to another.

Change-Id: I4d361c4e0950b9b9516b0c2f0c158677b1aca208

media/java/android/media/midi/IMidiDeviceServer.aidl
media/java/android/media/midi/MidiDevice.java
media/java/android/media/midi/MidiDeviceServer.java
media/java/android/media/midi/MidiInputPort.java

index 3331aae..642078a 100644 (file)
@@ -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);
 }
index af0737d..569f7c6 100644 (file)
@@ -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) {
index 3b4b6f0..6531052 100644 (file)
@@ -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,
index 74e1fa4..752075e 100644 (file)
@@ -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);