Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c76a3ac2 authored by Glenn Kasten's avatar Glenn Kasten Committed by Gerrit Code Review
Browse files

Merge "Squashed commit of the following:"

parents 67d47e0c 1fda5339
Loading
Loading
Loading
Loading
+37 −4
Original line number Diff line number Diff line
@@ -26,11 +26,23 @@ import java.util.concurrent.CopyOnWriteArrayList;
 * Utility class for dispatching MIDI data to a list of {@link android.media.midi.MidiReceiver}s.
 * This class subclasses {@link android.media.midi.MidiReceiver} and dispatches any data it receives
 * to its receiver list. Any receivers that throw an exception upon receiving data will
 * be automatically removed from the receiver list, but no IOException will be returned
 * from the dispatcher's {@link android.media.midi.MidiReceiver#onSend} in that case.
 * be automatically removed from the receiver list. If a MidiReceiverFailureHandler has been
 * provided to the MidiDispatcher, it will be notified about the failure, but the exception
 * itself will be swallowed.
 */
public final class MidiDispatcher extends MidiReceiver {

    // MidiDispatcher's client and MidiReceiver's owner can be different
    // classes (e.g. MidiDeviceService is a client, but MidiDeviceServer is
    // the owner), and errors occuring during sending need to be reported
    // to the owner rather than to the sender.
    //
    // Note that the callbacks will be called on the sender's thread.
    public interface MidiReceiverFailureHandler {
        void onReceiverFailure(MidiReceiver receiver, IOException failure);
    }

    private final MidiReceiverFailureHandler mFailureHandler;
    private final CopyOnWriteArrayList<MidiReceiver> mReceivers
            = new CopyOnWriteArrayList<MidiReceiver>();

@@ -46,6 +58,14 @@ public final class MidiDispatcher extends MidiReceiver {
        }
    };

    public MidiDispatcher() {
        this(null);
    }

    public MidiDispatcher(MidiReceiverFailureHandler failureHandler) {
        mFailureHandler = failureHandler;
    }

    /**
     * Returns the number of {@link android.media.midi.MidiReceiver}s this dispatcher contains.
     * @return the number of receivers
@@ -70,8 +90,13 @@ public final class MidiDispatcher extends MidiReceiver {
            try {
                receiver.send(msg, offset, count, timestamp);
            } catch (IOException e) {
                // if the receiver fails we remove the receiver but do not propagate the exception
                // If the receiver fails we remove the receiver but do not propagate the exception.
                // Note that this may also happen if the client code stalls, and thus underlying
                // MidiInputPort.onSend has raised IOException for EAGAIN / EWOULDBLOCK error.
                mReceivers.remove(receiver);
                if (mFailureHandler != null) {
                    mFailureHandler.onReceiverFailure(receiver, e);
                }
            }
        }
    }
@@ -79,7 +104,15 @@ public final class MidiDispatcher extends MidiReceiver {
    @Override
    public void onFlush() throws IOException {
       for (MidiReceiver receiver : mReceivers) {
            try {
                receiver.flush();
            } catch (IOException e) {
                // This is just a special case of 'send' thus handle in the same way.
                mReceivers.remove(receiver);
                if (mFailureHandler != null) {
                    mFailureHandler.onReceiverFailure(receiver, e);
                }
            }
       }
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -28,7 +28,8 @@ interface IMidiDeviceServer
    void closeDevice();

    // connects the input port pfd to the specified output port
    void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);
    // Returns the PID of the called process.
    int connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);

    MidiDeviceInfo getDeviceInfo();
    void setDeviceInfo(in MidiDeviceInfo deviceInfo);
+30 −9
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.media.midi;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;

@@ -41,6 +42,7 @@ public final class MidiDevice implements Closeable {
    private final IMidiManager mMidiManager;
    private final IBinder mClientToken;
    private final IBinder mDeviceToken;
    private boolean mIsDeviceClosed;

    private final CloseGuard mGuard = CloseGuard.get();

@@ -122,6 +124,9 @@ public final class MidiDevice implements Closeable {
     *         or null in case of failure.
     */
    public MidiInputPort openInputPort(int portNumber) {
        if (mIsDeviceClosed) {
            return null;
        }
        try {
            IBinder token = new Binder();
            ParcelFileDescriptor pfd = mDeviceServer.openInputPort(token, portNumber);
@@ -145,6 +150,9 @@ public final class MidiDevice implements Closeable {
     *         or null in case of failure.
     */
    public MidiOutputPort openOutputPort(int portNumber) {
        if (mIsDeviceClosed) {
            return null;
        }
        try {
            IBinder token = new Binder();
            ParcelFileDescriptor pfd = mDeviceServer.openOutputPort(token, portNumber);
@@ -174,6 +182,9 @@ public final class MidiDevice implements Closeable {
        if (outputPortNumber < 0 || outputPortNumber >= mDeviceInfo.getOutputPortCount()) {
            throw new IllegalArgumentException("outputPortNumber out of range");
        }
        if (mIsDeviceClosed) {
            return null;
        }

        ParcelFileDescriptor pfd = inputPort.claimFileDescriptor();
        if (pfd == null) {
@@ -181,9 +192,16 @@ public final class MidiDevice implements Closeable {
        }
        try {
            IBinder token = new Binder();
            mDeviceServer.connectPorts(token, pfd, outputPortNumber);
            int calleePid = mDeviceServer.connectPorts(token, pfd, outputPortNumber);
            // If the service is a different Process then it will duplicate the pfd
            // and we can safely close this one.
            // But if the service is in the same Process then closing the pfd will
            // kill the connection. So don't do that.
            if (calleePid != Process.myPid()) {
                // close our copy of the file descriptor
                IoUtils.closeQuietly(pfd);
            }

            return new MidiConnection(token, inputPort);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in connectPorts");
@@ -194,7 +212,9 @@ public final class MidiDevice implements Closeable {
    @Override
    public void close() throws IOException {
        synchronized (mGuard) {
            if (!mIsDeviceClosed) {
                mGuard.close();
                mIsDeviceClosed = true;
                try {
                    mMidiManager.closeDevice(mClientToken, mDeviceToken);
                } catch (RemoteException e) {
@@ -202,6 +222,7 @@ public final class MidiDevice implements Closeable {
                }
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
+49 −4
Original line number Diff line number Diff line
@@ -73,6 +73,10 @@ public final class MidiDeviceServer implements Closeable {

    private final Callback mCallback;

    private final HashMap<IBinder, PortClient> mPortClients = new HashMap<IBinder, PortClient>();
    private final HashMap<MidiInputPort, PortClient> mInputPortClients =
            new HashMap<MidiInputPort, PortClient>();

    public interface Callback {
        /**
         * Called to notify when an our device status has changed
@@ -102,6 +106,10 @@ public final class MidiDeviceServer implements Closeable {

        abstract void close();

        MidiInputPort getInputPort() {
            return null;
        }

        @Override
        public void binderDied() {
            close();
@@ -152,9 +160,12 @@ public final class MidiDeviceServer implements Closeable {
            mInputPorts.remove(mInputPort);
            IoUtils.closeQuietly(mInputPort);
        }
    }

    private final HashMap<IBinder, PortClient> mPortClients = new HashMap<IBinder, PortClient>();
        @Override
        MidiInputPort getInputPort() {
            return mInputPort;
        }
    }

    // Binder interface stub for receiving connection requests from clients
    private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() {
@@ -215,6 +226,12 @@ public final class MidiDeviceServer implements Closeable {
                ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair(
                                                    OsConstants.SOCK_SEQPACKET);
                MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber);
                // Undo the default blocking-mode of the server-side socket for
                // physical devices to avoid stalling the Java device handler if
                // client app code gets stuck inside 'onSend' handler.
                if (mDeviceInfo.getType() != MidiDeviceInfo.TYPE_VIRTUAL) {
                    IoUtils.setBlocking(pair[0].getFileDescriptor(), false);
                }
                MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber];
                synchronized (dispatcher) {
                    dispatcher.getSender().connect(inputPort);
@@ -228,6 +245,9 @@ public final class MidiDeviceServer implements Closeable {
                synchronized (mPortClients) {
                    mPortClients.put(token, client);
                }
                synchronized (mInputPortClients) {
                    mInputPortClients.put(inputPort, client);
                }
                return pair[1];
            } catch (IOException e) {
                Log.e(TAG, "unable to create ParcelFileDescriptors in openOutputPort");
@@ -237,12 +257,19 @@ public final class MidiDeviceServer implements Closeable {

        @Override
        public void closePort(IBinder token) {
            MidiInputPort inputPort = null;
            synchronized (mPortClients) {
                PortClient client = mPortClients.remove(token);
                if (client != null) {
                    inputPort = client.getInputPort();
                    client.close();
                }
            }
            if (inputPort != null) {
                synchronized (mInputPortClients) {
                    mInputPortClients.remove(inputPort);
                }
            }
        }

        @Override
@@ -254,7 +281,7 @@ public final class MidiDeviceServer implements Closeable {
        }

        @Override
        public void connectPorts(IBinder token, ParcelFileDescriptor pfd,
        public int connectPorts(IBinder token, ParcelFileDescriptor pfd,
                int outputPortNumber) {
            MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber);
            MidiDispatcher dispatcher = mOutputPortDispatchers[outputPortNumber];
@@ -270,6 +297,10 @@ public final class MidiDeviceServer implements Closeable {
            synchronized (mPortClients) {
                mPortClients.put(token, client);
            }
            synchronized (mInputPortClients) {
                mInputPortClients.put(inputPort, client);
            }
            return Process.myPid(); // for caller to detect same process ID
        }

        @Override
@@ -302,7 +333,7 @@ public final class MidiDeviceServer implements Closeable {

        mOutputPortDispatchers = new MidiDispatcher[numOutputPorts];
        for (int i = 0; i < numOutputPorts; i++) {
            mOutputPortDispatchers[i] = new MidiDispatcher();
            mOutputPortDispatchers[i] = new MidiDispatcher(mInputPortFailureHandler);
        }

        mInputPortOpen = new boolean[mInputPortCount];
@@ -311,6 +342,20 @@ public final class MidiDeviceServer implements Closeable {
        mGuard.open("close");
    }

    private final MidiDispatcher.MidiReceiverFailureHandler mInputPortFailureHandler =
            new MidiDispatcher.MidiReceiverFailureHandler() {
                public void onReceiverFailure(MidiReceiver receiver, IOException failure) {
                    Log.e(TAG, "MidiInputPort failed to send data", failure);
                    PortClient client = null;
                    synchronized (mInputPortClients) {
                        client = mInputPortClients.remove(receiver);
                    }
                    if (client != null) {
                        client.close();
                    }
                }
            };

    // Constructor for MidiDeviceService.onCreate()
    /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers,
           MidiDeviceInfo deviceInfo, Callback callback) {
+1 −1
Original line number Diff line number Diff line
@@ -83,7 +83,7 @@ public final class MidiOutputPort extends MidiSender implements Closeable {
                }
            } catch (IOException e) {
                // FIXME report I/O failure?
                Log.e(TAG, "read failed");
                Log.e(TAG, "read failed", e);
            } finally {
                IoUtils.closeQuietly(mInputStream);
            }
Loading