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

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

Merge "MidiManager updates:"

parents 568e54be 10024b3d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -174,6 +174,7 @@ LOCAL_SRC_FILES += \
	core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \
	core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl \
	core/java/android/hardware/usb/IUsbManager.aidl \
	core/java/android/midi/IMidiDeviceServer.aidl \
	core/java/android/midi/IMidiListener.aidl \
	core/java/android/midi/IMidiManager.aidl \
	core/java/android/net/IConnectivityManager.aidl \
+10 −3
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014, The Android Open Source Project
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -16,4 +16,11 @@

package android.midi;

parcelable MidiDevice;
import android.os.ParcelFileDescriptor;

/** @hide */
interface IMidiDeviceServer
{
    ParcelFileDescriptor openInputPort(int portNumber);
    ParcelFileDescriptor openOutputPort(int portNumber);
}
+5 −11
Original line number Diff line number Diff line
@@ -16,13 +16,11 @@

package android.midi;

import android.hardware.usb.UsbDevice;
import android.midi.IMidiDeviceServer;
import android.midi.IMidiListener;
import android.midi.MidiDevice;
import android.midi.MidiDeviceInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;

/** @hide */
interface IMidiManager
@@ -34,14 +32,10 @@ interface IMidiManager
    void unregisterListener(IBinder token, in IMidiListener listener);

    // for communicating with MIDI devices
    ParcelFileDescriptor openDevice(IBinder token, in MidiDeviceInfo device);
    IMidiDeviceServer openDevice(IBinder token, in MidiDeviceInfo device);

    // for implementing virtual MIDI devices
    MidiDevice registerVirtualDevice(IBinder token, int numInputPorts, int numOutputPorts,
            in Bundle properties);
    void unregisterVirtualDevice(IBinder token, in MidiDeviceInfo device);

    // for use by UsbAudioManager
    void alsaDeviceAdded(int card, int device, in UsbDevice usbDevice);
    void alsaDeviceRemoved(in UsbDevice usbDevice);
    MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts,
            int numOutputPorts, in Bundle properties, boolean isPrivate, int type);
    void unregisterDeviceServer(in IMidiDeviceServer server);
}
+30 −278
Original line number Diff line number Diff line
@@ -16,9 +16,8 @@

package android.midi;

import android.os.Parcel;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;

import java.io.FileDescriptor;
@@ -30,114 +29,31 @@ import java.util.ArrayList;
/**
 * 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}.
 * This class can also be used to provide the implementation for a virtual device.
 *
 * This class implements Parcelable so it can be returned from MidiService when creating
 * virtual MIDI devices.
 *
 * @hide
 */
public final class MidiDevice implements Parcelable {
public final class MidiDevice {
    private static final String TAG = "MidiDevice";

    private final MidiDeviceInfo mDeviceInfo;
    private ParcelFileDescriptor mParcelFileDescriptor;
    private FileInputStream mInputStream;
    private FileOutputStream mOutputStream;

    // lazily populated lists of ports
    private final MidiInputPort[] mInputPorts;
    private final MidiOutputPort[] mOutputPorts;

    // array of receiver lists, indexed by port number
    private final ArrayList<MidiReceiver>[] mReceivers;

    private int mReceiverCount; // total number of receivers for all ports

    /**
     * Minimum size of packed message as sent through our ParcelFileDescriptor
     * 8 bytes for timestamp, 1 byte for port number and 1 to 3 bytes for message
     * @hide
     */
    public static final int MIN_PACKED_MESSAGE_SIZE = 10;
    private final IMidiDeviceServer mServer;

   /**
     * Maximum size of packed message as sent through our ParcelFileDescriptor
     * 8 bytes for timestamp, 1 byte for port number and 1 to 3 bytes for message
     * MidiDevice should only be instantiated by MidiManager
     * @hide
     */
    public static final int MAX_PACKED_MESSAGE_SIZE = 12;

    // This thread reads MIDI events from a socket and distributes them to the list of
    // MidiReceivers attached to this device.
    private final Thread mThread = new Thread() {
        @Override
        public void run() {
            byte[] buffer = new byte[MAX_PACKED_MESSAGE_SIZE];
            ArrayList<MidiReceiver> deadReceivers = new ArrayList<MidiReceiver>();

            try {
                while (true) {
                    // read next event
                    int count = mInputStream.read(buffer);
                    if (count < MIN_PACKED_MESSAGE_SIZE || count > MAX_PACKED_MESSAGE_SIZE) {
                        Log.e(TAG, "Number of bytes read out of range: " + count);
                        break;
                    }

                    int offset = getMessageOffset(buffer, count);
                    int size = getMessageSize(buffer, count);
                    long timestamp = getMessageTimeStamp(buffer, count);
                    int port = getMessagePortNumber(buffer, count);

                    synchronized (mReceivers) {
                        ArrayList<MidiReceiver> receivers = mReceivers[port];
                        if (receivers != null) {
                            for (int i = 0; i < receivers.size(); i++) {
                                MidiReceiver receiver = receivers.get(i);
                                try {
                                    receivers.get(i).onPost(buffer, offset, size, timestamp);
                                } catch (IOException e) {
                                    Log.e(TAG, "post failed");
                                    deadReceivers.add(receiver);
                                }
                            }
                            // remove any receivers that failed
                            if (deadReceivers.size() > 0) {
                                for (MidiReceiver receiver: deadReceivers) {
                                    receivers.remove(receiver);
                                    mReceiverCount--;
                                }
                                deadReceivers.clear();
                            }
                            if (receivers.size() == 0) {
                                mReceivers[port] = null;
                            }
                            // exit if we have no receivers left
                            if (mReceiverCount == 0) {
                                break;
                            }
                        }
                    }
                }
            } catch (IOException e) {
                Log.e(TAG, "read failed");
            }
    public MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server) {
        mDeviceInfo = deviceInfo;
        mServer = server;
    }
    };

    /**
     * MidiDevice should only be instantiated by MidiManager or MidiService
     * @hide
     * Returns a {@link MidiDeviceInfo} object, which describes this device.
     *
     * @return the {@link MidiDeviceInfo} object
     */
    public MidiDevice(MidiDeviceInfo deviceInfo, ParcelFileDescriptor pfd) {
        mDeviceInfo = deviceInfo;
        mParcelFileDescriptor = pfd;
        int inputPorts = deviceInfo.getInputPortCount();
        int outputPorts = deviceInfo.getOutputPortCount();
        mInputPorts = new MidiInputPort[inputPorts];
        mOutputPorts = new MidiOutputPort[outputPorts];
        mReceivers = new ArrayList[outputPorts];
    public MidiDeviceInfo getInfo() {
        return mDeviceInfo;
    }

    /**
@@ -147,14 +63,15 @@ public final class MidiDevice implements Parcelable {
     * @return the {@link MidiInputPort}
     */
    public MidiInputPort openInputPort(int portNumber) {
        if (portNumber < 0 || portNumber >= mDeviceInfo.getInputPortCount()) {
            throw new IllegalArgumentException("input port number out of range");
        }
        synchronized (mInputPorts) {
            if (mInputPorts[portNumber] == null) {
                mInputPorts[portNumber] = new MidiInputPort(mOutputStream, portNumber);
        try {
            ParcelFileDescriptor pfd = mServer.openInputPort(portNumber);
            if (pfd == null) {
                return null;
            }
            return mInputPorts[portNumber];
            return new MidiInputPort(pfd, portNumber);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in openInputPort");
            return null;
        }
    }

@@ -165,185 +82,20 @@ public final class MidiDevice implements Parcelable {
     * @return the {@link MidiOutputPort}
     */
    public MidiOutputPort openOutputPort(int portNumber) {
        if (portNumber < 0 || portNumber >= mDeviceInfo.getOutputPortCount()) {
            throw new IllegalArgumentException("output port number out of range");
        }
        synchronized (mOutputPorts) {
            if (mOutputPorts[portNumber] == null) {
                mOutputPorts[portNumber] = new MidiOutputPort(this, portNumber);
            }
            return mOutputPorts[portNumber];
        }
    }

    /* package */ void connect(MidiReceiver receiver, int portNumber) {
        synchronized (mReceivers) {
            if (mReceivers[portNumber] == null) {
                mReceivers[portNumber] = new  ArrayList<MidiReceiver>();
            }
            mReceivers[portNumber].add(receiver);
            if (mReceiverCount++ == 0) {
                mThread.start();
            }
        }
    }

    /* package */ void disconnect(MidiReceiver receiver, int portNumber) {
        synchronized (mReceivers) {
            ArrayList<MidiReceiver> receivers = mReceivers[portNumber];
            if (receivers != null && receivers.remove(receiver)) {
                mReceiverCount--;
            }
        }
    }

    /* package */ boolean open() {
        FileDescriptor fd = mParcelFileDescriptor.getFileDescriptor();
        try {
            mInputStream = new FileInputStream(fd);
        } catch (Exception e) {
            Log.e(TAG, "could not create mInputStream", e);
            return false;
        }

        try {
            mOutputStream = new FileOutputStream(fd);
        } catch (Exception e) {
            Log.e(TAG, "could not create mOutputStream", e);
            return false;
        }
        return true;
    }

    /* package */ void close() {
        try {
            if (mInputStream != null) {
                mInputStream.close();
            }
            if (mOutputStream != null) {
                mOutputStream.close();
            ParcelFileDescriptor pfd = mServer.openOutputPort(portNumber);
            if (pfd == null) {
                return null;
            }
            mParcelFileDescriptor.close();
        } catch (IOException e) {
            return new MidiOutputPort(pfd, portNumber);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in openOutputPort");
            return null;
        }
    }

    /**
     * Returns a {@link MidiDeviceInfo} object, which describes this device.
     *
     * @return the {@link MidiDeviceInfo} object
     */
    public MidiDeviceInfo getInfo() {
        return mDeviceInfo;
    }

    @Override
    public String toString() {
        return ("MidiDevice: " + mDeviceInfo.toString() + " fd: " + mParcelFileDescriptor);
    }

    public static final Parcelable.Creator<MidiDevice> CREATOR =
        new Parcelable.Creator<MidiDevice>() {
        public MidiDevice createFromParcel(Parcel in) {
            MidiDeviceInfo deviceInfo = (MidiDeviceInfo)in.readParcelable(null);
            ParcelFileDescriptor pfd = (ParcelFileDescriptor)in.readParcelable(null);
            return new MidiDevice(deviceInfo, pfd);
        }

        public MidiDevice[] newArray(int size) {
            return new MidiDevice[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeParcelable(mDeviceInfo, flags);
        parcel.writeParcelable(mParcelFileDescriptor, flags);
   }

    /**
     * Utility function for packing a MIDI message to be sent through our ParcelFileDescriptor
     *
     * message byte array contains variable length MIDI message.
     * messageSize is size of variable length MIDI message
     * timestamp is message timestamp to pack
     * dest is buffer to pack into
     * returns size of packed message
     *
     * @hide
     */
    public static int packMessage(byte[] message, int offset, int size, long timestamp,
            int portNumber, byte[] dest) {
        // pack variable length message first
        System.arraycopy(message, offset, dest, 0, size);
        int destOffset = size;
        // timestamp takes 8 bytes
        for (int i = 0; i < 8; i++) {
            dest[destOffset++] = (byte)timestamp;
            timestamp >>= 8;
        }
        // portNumber is last
        dest[destOffset++] = (byte)portNumber;

        return destOffset;
    }

    /**
     * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor
     * returns the offet of of MIDI message in packed buffer
     *
     * @hide
     */
    public static int getMessageOffset(byte[] buffer, int bufferLength) {
        // message is at start of buffer
        return 0;
    }

    /**
     * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor
     * returns size of MIDI message in packed buffer
     *
     * @hide
     */
    public static int getMessageSize(byte[] buffer, int bufferLength) {
        // message length is total buffer length minus size of the timestamp and port number
        return bufferLength - 9 /* (sizeof(timestamp) + sizeof(portNumber)) */;
    }

    /**
     * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor
     * unpacks timestamp from packed buffer
     *
     * @hide
     */
    public static long getMessageTimeStamp(byte[] buffer, int bufferLength) {
        long timestamp = 0;

        // timestamp follows variable length message data
        int dataLength = getMessageSize(buffer, bufferLength);
        for (int i = dataLength + 7; i >= dataLength; i--) {
            // why can't Java deal with unsigned ints?
            int b = buffer[i];
            if (b < 0) b += 256;
            timestamp = (timestamp << 8) | b;
        }
        return timestamp;
     }

    /**
     * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor
     * unpacks port number from packed buffer
     *
     * @hide
     */
    public static int getMessagePortNumber(byte[] buffer, int bufferLength) {
        // timestamp follows variable length message data and timestamp
        int dataLength = getMessageSize(buffer, bufferLength);
        return buffer[dataLength + 8 /* sizeof(timestamp) */];
        return ("MidiDevice: " + mDeviceInfo.toString());
    }
}
+14 −41
Original line number Diff line number Diff line
@@ -50,10 +50,6 @@ public class MidiDeviceInfo implements Parcelable {
    private final int mOutputPortCount;
    private final Bundle mProperties;

    // used for USB devices only
    private final int mAlsaCard;
    private final int mAlsaDevice;

    /**
     * Bundle key for the device's manufacturer name property.
     * Used with the {@link android.os.Bundle} returned by {@link #getProperties}.
@@ -83,33 +79,30 @@ public class MidiDeviceInfo implements Parcelable {
    public static final String PROPERTY_USB_DEVICE = "usb_device";

    /**
     * MidiDeviceInfo should only be instantiated by MidiService implementation
     * @hide
     * Bundle key for the device's ALSA card number.
     * Only set for USB MIDI devices.
     * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
     */
    public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
            Bundle properties) {
        mType = type;
        mId = id;
        mInputPortCount = numInputPorts;
        mOutputPortCount = numOutputPorts;
        mProperties = properties;
        mAlsaCard = -1;
        mAlsaDevice = -1;
    }
    public static final String PROPERTY_ALSA_CARD = "alsa_card";

    /**
     * Bundle key for the device's ALSA device number.
     * Only set for USB MIDI devices.
     * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
     */
    public static final String PROPERTY_ALSA_DEVICE = "alsa_device";

    /**
     * MidiDeviceInfo should only be instantiated by MidiService implementation
     * @hide
     */
    public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
            Bundle properties, int alsaCard, int alsaDevice) {
            Bundle properties) {
        mType = type;
        mId = id;
        mInputPortCount = numInputPorts;
        mOutputPortCount = numOutputPorts;
        mProperties = properties;
        mAlsaCard = alsaCard;
        mAlsaDevice = alsaDevice;
    }

    /**
@@ -158,20 +151,6 @@ public class MidiDeviceInfo implements Parcelable {
        return mProperties;
    }

    /**
     * @hide
     */
    public int getAlsaCard() {
        return mAlsaCard;
    }

    /**
     * @hide
     */
    public int getAlsaDevice() {
        return mAlsaDevice;
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof MidiDeviceInfo) {
@@ -191,9 +170,7 @@ public class MidiDeviceInfo implements Parcelable {
        return ("MidiDeviceInfo[mType=" + mType +
                ",mInputPortCount=" + mInputPortCount +
                ",mOutputPortCount=" + mOutputPortCount +
                ",mProperties=" + mProperties +
                ",mAlsaCard=" + mAlsaCard +
                ",mAlsaDevice=" + mAlsaDevice);
                ",mProperties=" + mProperties);
    }

    public static final Parcelable.Creator<MidiDeviceInfo> CREATOR =
@@ -204,9 +181,7 @@ public class MidiDeviceInfo implements Parcelable {
            int inputPorts = in.readInt();
            int outputPorts = in.readInt();
            Bundle properties = in.readBundle();
            int card = in.readInt();
            int device = in.readInt();
            return new MidiDeviceInfo(type, id, inputPorts, outputPorts, properties, card, device);
            return new MidiDeviceInfo(type, id, inputPorts, outputPorts, properties);
        }

        public MidiDeviceInfo[] newArray(int size) {
@@ -224,7 +199,5 @@ public class MidiDeviceInfo implements Parcelable {
        parcel.writeInt(mInputPortCount);
        parcel.writeInt(mOutputPortCount);
        parcel.writeBundle(mProperties);
        parcel.writeInt(mAlsaCard);
        parcel.writeInt(mAlsaDevice);
   }
}
Loading