Fix MidiDevice.MidiConnection lifecycle

Update device server's MidiDeviceStatus when a connection is made to one of its output ports.

After connecting an input port to an output port using MidiDevice.connectPorts(),
do not call IMidiDeviceServer.closePort() until MidiDevice.MidiConnection.close() is called.

While I was in there, added missing CloseGuard support to the MidiDevice.MidiConnection class.

This fixes a problem resulting in UsbMidiDevice closing the device's ALSA driver too soon.

Bug: 21850709

Change-Id: I0c120f76b42eec8a143161e46dba73fbec5e4f31
This commit is contained in:
Mike Lockwood
2015-06-18 10:05:17 -07:00
parent 13e54ff0eb
commit 2aef7e3559
3 changed files with 58 additions and 13 deletions

View File

@@ -50,21 +50,43 @@ public final class MidiDevice implements Closeable {
* Close this object to terminate the connection.
*/
public class MidiConnection implements Closeable {
private final IBinder mToken;
private final MidiInputPort mInputPort;
private final IMidiDeviceServer mInputPortDeviceServer;
private final IBinder mInputPortToken;
private final IBinder mOutputPortToken;
private final CloseGuard mGuard = CloseGuard.get();
private boolean mIsClosed;
MidiConnection(IBinder token, MidiInputPort inputPort) {
mToken = token;
mInputPort = inputPort;
MidiConnection(IBinder outputPortToken, MidiInputPort inputPort) {
mInputPortDeviceServer = inputPort.getDeviceServer();
mInputPortToken = inputPort.getToken();
mOutputPortToken = outputPortToken;
mGuard.open("close");
}
@Override
public void close() throws IOException {
synchronized (mGuard) {
if (mIsClosed) return;
mGuard.close();
try {
// close input port
mInputPortDeviceServer.closePort(mInputPortToken);
// close output port
mDeviceServer.closePort(mOutputPortToken);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MidiConnection.close");
}
mIsClosed = true;
}
}
@Override
protected void finalize() throws Throwable {
try {
mDeviceServer.closePort(mToken);
IoUtils.closeQuietly(mInputPort);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MidiConnection.close");
mGuard.warnIfOpen();
close();
} finally {
super.finalize();
}
}
}

View File

@@ -257,7 +257,14 @@ public final class MidiDeviceServer implements Closeable {
public void connectPorts(IBinder token, ParcelFileDescriptor pfd,
int outputPortNumber) {
MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber);
mOutputPortDispatchers[outputPortNumber].getSender().connect(inputPort);
MidiDispatcher dispatcher = mOutputPortDispatchers[outputPortNumber];
synchronized (dispatcher) {
dispatcher.getSender().connect(inputPort);
int openCount = dispatcher.getReceiverCount();
mOutputPortOpenCount[outputPortNumber] = openCount;
updateDeviceStatus();
}
mInputPorts.add(inputPort);
OutputPortClient client = new OutputPortClient(token, inputPort);
synchronized (mPortClients) {

View File

@@ -103,17 +103,33 @@ public final class MidiInputPort extends MidiReceiver implements Closeable {
// used by MidiDevice.connectInputPort() to connect our socket directly to another device
/* package */ ParcelFileDescriptor claimFileDescriptor() {
synchronized (mBuffer) {
ParcelFileDescriptor pfd = mParcelFileDescriptor;
if (pfd != null) {
synchronized (mGuard) {
ParcelFileDescriptor pfd;
synchronized (mBuffer) {
pfd = mParcelFileDescriptor;
if (pfd == null) return null;
IoUtils.closeQuietly(mOutputStream);
mParcelFileDescriptor = null;
mOutputStream = null;
}
// Set mIsClosed = true so we will not call mDeviceServer.closePort() in close().
// MidiDevice.MidiConnection.close() will do the cleanup instead.
mIsClosed = true;
return pfd;
}
}
// used by MidiDevice.MidiConnection to close this port after the connection is closed
/* package */ IBinder getToken() {
return mToken;
}
// used by MidiDevice.MidiConnection to close this port after the connection is closed
/* package */ IMidiDeviceServer getDeviceServer() {
return mDeviceServer;
}
@Override
public void close() throws IOException {
synchronized (mGuard) {