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

Commit d73ff872 authored by Mike Lockwood's avatar Mike Lockwood Committed by Android (Google) Code Review
Browse files

Merge changes Ib5cc22db,I93e0c449,I65cd4cfd,I1cef3bd4,I628c0468

* changes:
  MIDI Manager: Add explicit close mechanism for input and output ports
  MidiDeviceService: Add getDeviceInfo() accessor method
  Add MidiDevice.close() method so we can clean up our ServiceConnection
  Make MidiSender and MidiReceiver abstract classes, rename MidiReceiver.post() to receive()
  Eliminate MidiPort base class for MidiInputPort and MidiOutputPort
parents 6c4c58d6 4a3d7ed4
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.os.ParcelFileDescriptor;
/** @hide */
interface IMidiDeviceServer
{
    ParcelFileDescriptor openInputPort(int portNumber);
    ParcelFileDescriptor openOutputPort(int portNumber);
    ParcelFileDescriptor openInputPort(IBinder token, int portNumber);
    ParcelFileDescriptor openOutputPort(IBinder token, int portNumber);
    void closePort(IBinder token);
}
+38 −12
Original line number Diff line number Diff line
@@ -16,10 +16,17 @@

package android.media.midi;

import android.content.Context;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;

import java.io.Closeable;
import java.io.IOException;

/**
 * This class is used for sending and receiving data to and from an MIDI device
 * Instances of this class are created by {@link MidiManager#openDevice}.
@@ -27,19 +34,27 @@ import android.util.Log;
 * CANDIDATE FOR PUBLIC API
 * @hide
 */
public final class MidiDevice {
public final class MidiDevice implements Closeable {
    private static final String TAG = "MidiDevice";

    private final MidiDeviceInfo mDeviceInfo;
    private final IMidiDeviceServer mServer;
    private final IMidiDeviceServer mDeviceServer;
    private Context mContext;
    private ServiceConnection mServiceConnection;

   /**
     * MidiDevice should only be instantiated by MidiManager
     * @hide
     */
    public MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server) {
    /* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server) {
        mDeviceInfo = deviceInfo;
        mServer = server;
        mDeviceServer = server;
        mContext = null;
        mServiceConnection = null;
    }

    /* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server,
            Context context, ServiceConnection serviceConnection) {
        mDeviceInfo = deviceInfo;
        mDeviceServer = server;
        mContext = context;
        mServiceConnection = serviceConnection;
    }

    /**
@@ -59,11 +74,12 @@ public final class MidiDevice {
     */
    public MidiInputPort openInputPort(int portNumber) {
        try {
            ParcelFileDescriptor pfd = mServer.openInputPort(portNumber);
            IBinder token = new Binder();
            ParcelFileDescriptor pfd = mDeviceServer.openInputPort(token, portNumber);
            if (pfd == null) {
                return null;
            }
            return new MidiInputPort(pfd, portNumber);
            return new MidiInputPort(mDeviceServer, token, pfd, portNumber);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in openInputPort");
            return null;
@@ -78,17 +94,27 @@ public final class MidiDevice {
     */
    public MidiOutputPort openOutputPort(int portNumber) {
        try {
            ParcelFileDescriptor pfd = mServer.openOutputPort(portNumber);
            IBinder token = new Binder();
            ParcelFileDescriptor pfd = mDeviceServer.openOutputPort(token, portNumber);
            if (pfd == null) {
                return null;
            }
            return new MidiOutputPort(pfd, portNumber);
            return new MidiOutputPort(mDeviceServer, token, pfd, portNumber);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in openOutputPort");
            return null;
        }
    }

    @Override
    public void close() throws IOException {
        if (mContext != null && mServiceConnection != null) {
            mContext.unbindService(mServiceConnection);
            mContext = null;
            mServiceConnection = null;
        }
    }

    @Override
    public String toString() {
        return ("MidiDevice: " + mDeviceInfo.toString());
+82 −36
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import libcore.io.IoUtils;

import java.io.Closeable;
import java.io.IOException;
import java.util.HashMap;

/**
 * Internal class used for providing an implementation for a MIDI device.
@@ -53,11 +54,68 @@ public final class MidiDeviceServer implements Closeable {
    // MidiOutputPorts for clients connected to our input ports
    private final MidiOutputPort[] mInputPortOutputPorts;

    abstract private class PortClient implements IBinder.DeathRecipient {
        final IBinder mToken;

        PortClient(IBinder token) {
            mToken = token;

            try {
                token.linkToDeath(this, 0);
            } catch (RemoteException e) {
                close();
            }
        }

        abstract void close();

        @Override
        public void binderDied() {
            close();
        }
    }

    private class InputPortClient extends PortClient {
        private final MidiOutputPort mOutputPort;

        InputPortClient(IBinder token, MidiOutputPort outputPort) {
            super(token);
            mOutputPort = outputPort;
        }

        @Override
        void close() {
            mToken.unlinkToDeath(this, 0);
            synchronized (mInputPortOutputPorts) {
                mInputPortOutputPorts[mOutputPort.getPortNumber()] = null;
            }
            IoUtils.closeQuietly(mOutputPort);
        }
    }

    private class OutputPortClient extends PortClient {
        private final MidiInputPort mInputPort;

        OutputPortClient(IBinder token, MidiInputPort inputPort) {
            super(token);
            mInputPort = inputPort;
        }

        @Override
        void close() {
            mToken.unlinkToDeath(this, 0);
            mOutputPortDispatchers[mInputPort.getPortNumber()].getSender().disconnect(mInputPort);
            IoUtils.closeQuietly(mInputPort);
        }
    }

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

    // Binder interface stub for receiving connection requests from clients
    private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() {

        @Override
        public ParcelFileDescriptor openInputPort(int portNumber) {
        public ParcelFileDescriptor openInputPort(IBinder token, int portNumber) {
            if (mDeviceInfo.isPrivate()) {
                if (Binder.getCallingUid() != Process.myUid()) {
                    throw new SecurityException("Can't access private device from different UID");
@@ -78,25 +136,13 @@ public final class MidiDeviceServer implements Closeable {
                try {
                    ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair(
                                                        OsConstants.SOCK_SEQPACKET);
                    final MidiOutputPort outputPort = new MidiOutputPort(pair[0], portNumber);
                    MidiOutputPort outputPort = new MidiOutputPort(pair[0], portNumber);
                    mInputPortOutputPorts[portNumber] = outputPort;
                    final int portNumberF = portNumber;
                    final MidiReceiver inputPortReceviver = mInputPortReceivers[portNumber];

                    outputPort.connect(new MidiReceiver() {
                        @Override
                        public void post(byte[] msg, int offset, int count, long timestamp)
                                throws IOException {
                            try {
                                inputPortReceviver.post(msg, offset, count, timestamp);
                            } catch (IOException e) {
                                IoUtils.closeQuietly(mInputPortOutputPorts[portNumberF]);
                                mInputPortOutputPorts[portNumberF] = null;
                                // FIXME also flush the receiver
                            }
                    outputPort.connect(mInputPortReceivers[portNumber]);
                    InputPortClient client = new InputPortClient(token, outputPort);
                    synchronized (mPortClients) {
                        mPortClients.put(token, client);
                    }
                    });

                    return pair[1];
                } catch (IOException e) {
                    Log.e(TAG, "unable to create ParcelFileDescriptors in openInputPort");
@@ -106,7 +152,7 @@ public final class MidiDeviceServer implements Closeable {
        }

        @Override
        public ParcelFileDescriptor openOutputPort(int portNumber) {
        public ParcelFileDescriptor openOutputPort(IBinder token, int portNumber) {
            if (mDeviceInfo.isPrivate()) {
                if (Binder.getCallingUid() != Process.myUid()) {
                    throw new SecurityException("Can't access private device from different UID");
@@ -121,28 +167,28 @@ public final class MidiDeviceServer implements Closeable {
            try {
                ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair(
                                                    OsConstants.SOCK_SEQPACKET);
                final MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber);
                final MidiSender sender = mOutputPortDispatchers[portNumber].getSender();
                sender.connect(new MidiReceiver() {
                        @Override
                        public void post(byte[] msg, int offset, int count, long timestamp)
                                throws IOException {
                            try {
                                inputPort.post(msg, offset, count, timestamp);
                            } catch (IOException e) {
                                IoUtils.closeQuietly(inputPort);
                                sender.disconnect(this);
                                // FIXME also flush the receiver?
                            }
                MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber);
                mOutputPortDispatchers[portNumber].getSender().connect(inputPort);
                OutputPortClient client = new OutputPortClient(token, inputPort);
                synchronized (mPortClients) {
                    mPortClients.put(token, client);
                }
                    });

                return pair[1];
            } catch (IOException e) {
                Log.e(TAG, "unable to create ParcelFileDescriptors in openOutputPort");
                return null;
            }
        }

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

    /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers,
+14 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ abstract public class MidiDeviceService extends Service {

    private IMidiManager mMidiManager;
    private MidiDeviceServer mServer;
    private MidiDeviceInfo mDeviceInfo;

    @Override
    public void onCreate() {
@@ -64,6 +65,11 @@ abstract public class MidiDeviceService extends Service {
        try {
            MidiDeviceInfo deviceInfo = mMidiManager.getServiceDeviceInfo(getPackageName(),
                    this.getClass().getName());
            if (deviceInfo == null) {
                Log.e(TAG, "Could not find MidiDeviceInfo for MidiDeviceService " + this);
                return;
            }
            mDeviceInfo = deviceInfo;
            MidiReceiver[] inputPortReceivers = getInputPortReceivers();
            if (inputPortReceivers == null) {
                inputPortReceivers = new MidiReceiver[0];
@@ -100,6 +106,14 @@ abstract public class MidiDeviceService extends Service {
        }
    }

    /**
     * returns the {@link MidiDeviceInfo} instance for this service
     * @return our MidiDeviceInfo
     */
    public MidiDeviceInfo getDeviceInfo() {
        return mDeviceInfo;
    }

    @Override
    public IBinder onBind(Intent intent) {
        if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
+4 −4
Original line number Diff line number Diff line
@@ -24,12 +24,12 @@ import java.util.ArrayList;
 * This class subclasses {@link 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 #post} in that case.
 * from the dispatcher's {@link #receive} in that case.
 *
 * CANDIDATE FOR PUBLIC API
 * @hide
 */
public class MidiDispatcher implements MidiReceiver {
public class MidiDispatcher extends MidiReceiver {

    private final ArrayList<MidiReceiver> mReceivers = new ArrayList<MidiReceiver>();

@@ -71,12 +71,12 @@ public class MidiDispatcher implements MidiReceiver {
    }

    @Override
    public void post(byte[] msg, int offset, int count, long timestamp) throws IOException {
    public void receive(byte[] msg, int offset, int count, long timestamp) throws IOException {
        synchronized (mReceivers) {
           for (int i = 0; i < mReceivers.size(); ) {
                MidiReceiver receiver = mReceivers.get(i);
                try {
                    receiver.post(msg, offset, count, timestamp);
                    receiver.receive(msg, offset, count, timestamp);
                    i++;    // increment only on success. on failure we remove the receiver
                            // so i should not be incremented
                } catch (IOException e) {
Loading