MidiDevice: Add support for making direct connections between ports

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
This commit is contained in:
Mike Lockwood
2015-03-06 10:48:51 -08:00
parent 0c7342f015
commit 46326e59a0
4 changed files with 98 additions and 3 deletions

View 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);
}

View 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) {

View 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,

View 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);