Merge "Rewrite MIDI 1.0 USB Host mode in Java"
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index f72b23c..2d082e1 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -63,6 +63,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
@@ -128,6 +129,19 @@
 
     private final PackageManager mPackageManager;
 
+    private static final String MIDI_LEGACY_STRING = "MIDI 1.0";
+    private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0";
+
+    // Used to lock mUsbMidiLegacyDeviceOpenCount and mUsbMidiUniversalDeviceInUse.
+    private final Object mUsbMidiLock = new Object();
+
+    // Number of times a USB MIDI 1.0 device has opened, based on the device name.
+    private final HashMap<String, Integer> mUsbMidiLegacyDeviceOpenCount =
+            new HashMap<String, Integer>();
+
+    // Whether a USB MIDI device has opened, based on the device name.
+    private final HashSet<String> mUsbMidiUniversalDeviceInUse = new HashSet<String>();
+
     // UID of BluetoothMidiService
     private int mBluetoothServiceUid;
 
@@ -271,6 +285,12 @@
             }
 
             for (DeviceConnection connection : mDeviceConnections.values()) {
+                if (connection.getDevice().getDeviceInfo().getType()
+                        == MidiDeviceInfo.TYPE_USB) {
+                    synchronized (mUsbMidiLock) {
+                        removeUsbMidiDeviceLocked(connection.getDevice().getDeviceInfo());
+                    }
+                }
                 connection.getDevice().removeDeviceConnection(connection);
             }
         }
@@ -748,6 +768,7 @@
         synchronized (mDevicesByInfo) {
             for (Device device : mDevicesByInfo.values()) {
                 if (device.isUidAllowed(uid)) {
+                    deviceInfos.add(device.getDeviceInfo());
                     // UMP devices have protocols that are not PROTOCOL_UNKNOWN
                     if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
                         if (device.getDeviceInfo().getDefaultProtocol()
@@ -784,6 +805,15 @@
             }
         }
 
+        if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) {
+            synchronized (mUsbMidiLock) {
+                if (isUsbMidiDeviceInUseLocked(deviceInfo)) {
+                    throw new IllegalArgumentException("device already in use: " + deviceInfo);
+                }
+                addUsbMidiDeviceLocked(deviceInfo);
+            }
+        }
+
         // clear calling identity so bindService does not fail
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -1216,4 +1246,82 @@
         }
         pw.decreaseIndent();
     }
+
+    // hold mUsbMidiLock before calling this
+    private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return false;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        // Only one MIDI 2.0 device can be used at once.
+        // Multiple MIDI 1.0 devices can be used at once.
+        if (mUsbMidiUniversalDeviceInUse.contains(deviceName)
+                || ((tagName).equals(MIDI_UNIVERSAL_STRING)
+                && (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)))) {
+            return true;
+        }
+        return false;
+    }
+
+    // hold mUsbMidiLock before calling this
+    void addUsbMidiDeviceLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
+            mUsbMidiUniversalDeviceInUse.add(deviceName);
+        } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
+            int count = mUsbMidiLegacyDeviceOpenCount.getOrDefault(deviceName, 0) + 1;
+            mUsbMidiLegacyDeviceOpenCount.put(deviceName, count);
+        }
+    }
+
+    // hold mUsbMidiLock before calling this
+    void removeUsbMidiDeviceLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
+            mUsbMidiUniversalDeviceInUse.remove(deviceName);
+        } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
+            if (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)) {
+                int count = mUsbMidiLegacyDeviceOpenCount.get(deviceName);
+                if (count > 1) {
+                    mUsbMidiLegacyDeviceOpenCount.put(deviceName, count - 1);
+                } else {
+                    mUsbMidiLegacyDeviceOpenCount.remove(deviceName);
+                }
+            }
+        }
+    }
+
+    // The USB property name is in the form "manufacturer product#Id MIDI 1.0".
+    // This is defined in UsbDirectMidiDevice.java.
+    // This function extracts out the "manufacturer product#Id " part.
+    // Two devices would have the same device name if they had the following property name:
+    // "manufacturer product#Id MIDI 1.0"
+    // "manufacturer product#Id MIDI 2.0"
+    // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length.
+    String extractUsbDeviceName(String propertyName) {
+        return propertyName.substring(0, propertyName.length() - MIDI_LEGACY_STRING.length());
+    }
+
+    // The USB property name is in the form "manufacturer product#Id MIDI 1.0".
+    // This is defined in UsbDirectMidiDevice.java.
+    // This function extracts the "MIDI 1.0" part.
+    // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length.
+    String extractUsbDeviceTag(String propertyName) {
+        return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length());
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index fd9b995..42a5af7 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -36,7 +36,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -107,12 +106,6 @@
         return false;
     }
 
-    /**
-     * List of connected MIDI devices
-     */
-    private final HashMap<String, UsbMidiDevice>
-            mMidiDevices = new HashMap<String, UsbMidiDevice>();
-
     // UsbMidiDevice for USB peripheral mode (gadget) device
     private UsbMidiDevice mPeripheralMidiDevice = null;
 
@@ -256,45 +249,6 @@
             }
         }
 
-        // look for MIDI devices
-        boolean hasMidi = parser.hasMIDIInterface();
-        int midiNumInputs = parser.calculateNumLegacyMidiInputs();
-        int midiNumOutputs = parser.calculateNumLegacyMidiOutputs();
-        if (DEBUG) {
-            Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
-            Slog.d(TAG, "midiNumInputs: " + midiNumInputs + " midiNumOutputs:" + midiNumOutputs);
-        }
-        if (hasMidi && mHasMidiFeature) {
-            int device = 0;
-            Bundle properties = new Bundle();
-            String manufacturer = usbDevice.getManufacturerName();
-            String product = usbDevice.getProductName();
-            String version = usbDevice.getVersion();
-            String name;
-            if (manufacturer == null || manufacturer.isEmpty()) {
-                name = product;
-            } else if (product == null || product.isEmpty()) {
-                name = manufacturer;
-            } else {
-                name = manufacturer + " " + product;
-            }
-            properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
-            properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
-            properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
-            properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
-            properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
-                    usbDevice.getSerialNumber());
-            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum());
-            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/);
-            properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
-
-            UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
-                    cardRec.getCardNum(), 0 /*device*/, midiNumInputs, midiNumOutputs);
-            if (usbMidiDevice != null) {
-                mMidiDevices.put(deviceAddress, usbMidiDevice);
-            }
-        }
-
         logDevices("deviceAdded()");
 
         if (DEBUG) {
@@ -315,13 +269,6 @@
             selectDefaultDevice(); // if there any external devices left, select one of them
         }
 
-        // MIDI
-        UsbMidiDevice usbMidiDevice = mMidiDevices.remove(deviceAddress);
-        if (usbMidiDevice != null) {
-            Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice);
-            IoUtils.closeQuietly(usbMidiDevice);
-        }
-
         logDevices("usbDeviceRemoved()");
 
     }
@@ -377,12 +324,6 @@
             usbAlsaDevice.dump(dump, "alsa_devices", UsbAlsaManagerProto.ALSA_DEVICES);
         }
 
-        for (String deviceAddr : mMidiDevices.keySet()) {
-            // A UsbMidiDevice does not have a handle to the UsbDevice anymore
-            mMidiDevices.get(deviceAddr).dump(deviceAddr, dump, "midi_devices",
-                    UsbAlsaManagerProto.MIDI_DEVICES);
-        }
-
         dump.end(token);
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
similarity index 65%
rename from services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
rename to services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
index 13d404c..0fa79df 100644
--- a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
@@ -20,6 +20,7 @@
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbDeviceConnection;
 import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceServer;
@@ -43,16 +44,20 @@
 import java.util.ArrayList;
 
 /**
- * A MIDI device that opens device connections to MIDI 2.0 endpoints.
+ * Opens device connections to MIDI 1.0 or MIDI 2.0 endpoints.
+ * This endpoint will not use ALSA and opens a UsbDeviceConnection directly.
  */
-public final class UsbUniversalMidiDevice implements Closeable {
-    private static final String TAG = "UsbUniversalMidiDevice";
+public final class UsbDirectMidiDevice implements Closeable {
+    private static final String TAG = "UsbDirectMidiDevice";
     private static final boolean DEBUG = false;
 
     private Context mContext;
     private UsbDevice mUsbDevice;
     private UsbDescriptorParser mParser;
     private ArrayList<UsbInterfaceDescriptor> mUsbInterfaces;
+    private final boolean mIsUniversalMidiDevice;
+    private final String mUniqueUsbDeviceIdentifier;
+    private final boolean mShouldCallSetInterface;
 
     // USB outputs are MIDI inputs
     private final InputReceiverProxy[] mMidiInputPortReceivers;
@@ -64,6 +69,11 @@
     // event schedulers for each input port of the physical device
     private MidiEventScheduler[] mEventSchedulers;
 
+    // Arbitrary number for timeout to not continue sending/receiving number from
+    // an inactive device. This number tries to balances the number of cycles and
+    // not being permanently stuck.
+    private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 100;
+
     private ArrayList<UsbDeviceConnection> mUsbDeviceConnections;
     private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints;
     private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints;
@@ -74,6 +84,8 @@
     private final Object mLock = new Object();
     private boolean mIsOpen;
 
+    private final UsbMidiPacketConverter mUsbMidiPacketConverter = new UsbMidiPacketConverter();
+
     private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
 
         @Override
@@ -140,13 +152,15 @@
     }
 
     /**
-     * Creates an UsbUniversalMidiDevice based on the input parameters. Read/Write streams
+     * Creates an UsbDirectMidiDevice based on the input parameters. Read/Write streams
      * will be created individually as some devices don't have the same number of
      * inputs and outputs.
      */
-    public static UsbUniversalMidiDevice create(Context context, UsbDevice usbDevice,
-            UsbDescriptorParser parser) {
-        UsbUniversalMidiDevice midiDevice = new UsbUniversalMidiDevice(usbDevice, parser);
+    public static UsbDirectMidiDevice create(Context context, UsbDevice usbDevice,
+            UsbDescriptorParser parser, boolean isUniversalMidiDevice,
+            String uniqueUsbDeviceIdentifier) {
+        UsbDirectMidiDevice midiDevice = new UsbDirectMidiDevice(usbDevice, parser,
+                isUniversalMidiDevice, uniqueUsbDeviceIdentifier);
         if (!midiDevice.register(context)) {
             IoUtils.closeQuietly(midiDevice);
             Log.e(TAG, "createDeviceServer failed");
@@ -155,11 +169,22 @@
         return midiDevice;
     }
 
-    private UsbUniversalMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser) {
+    private UsbDirectMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser,
+            boolean isUniversalMidiDevice, String uniqueUsbDeviceIdentifier) {
         mUsbDevice = usbDevice;
         mParser = parser;
+        mUniqueUsbDeviceIdentifier = uniqueUsbDeviceIdentifier;
+        mIsUniversalMidiDevice = isUniversalMidiDevice;
 
-        mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+        // Set interface should only be called when alternate interfaces exist.
+        // Otherwise, USB devices may not handle this gracefully.
+        mShouldCallSetInterface = (parser.calculateMidiInterfaceDescriptorsCount() > 1);
+
+        if (isUniversalMidiDevice) {
+            mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+        } else {
+            mUsbInterfaces = parser.findLegacyMidiInterfaceDescriptors();
+        }
 
         int numInputs = 0;
         int numOutputs = 0;
@@ -182,8 +207,8 @@
         mNumInputs = numInputs;
         mNumOutputs = numOutputs;
 
-        Log.d(TAG, "Created UsbUniversalMidiDevice with " + numInputs + " inputs and "
-                + numOutputs + " outputs");
+        Log.d(TAG, "Created UsbDirectMidiDevice with " + numInputs + " inputs and "
+                + numOutputs + " outputs. isUniversalMidiDevice: " + isUniversalMidiDevice);
 
         // Create MIDI port receivers based on the number of output ports. The
         // output of USB is the input of MIDI.
@@ -218,23 +243,25 @@
             if (doesInterfaceContainInput
                     && doesInterfaceContainOutput) {
                 UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
-
-                // The ALSA does not handle switching to the MIDI 2.0 interface correctly
-                // and stops exposing /dev/snd/midiC1D0 after calling connection.setInterface().
-                // Thus, simply use the control interface (interface zero).
+                UsbInterface usbInterface = interfaceDescriptor.toAndroid(mParser);
+                if (!updateUsbInterface(usbInterface, connection)) {
+                    continue;
+                }
                 int defaultMidiProtocol = mMidiBlockParser.calculateMidiType(connection,
-                        0,
+                        interfaceDescriptor.getInterfaceNumber(),
                         interfaceDescriptor.getAlternateSetting());
+
                 connection.close();
                 return defaultMidiProtocol;
             }
         }
 
-        Log.d(TAG, "Cannot find interface with both input and output endpoints");
+        Log.w(TAG, "Cannot find interface with both input and output endpoints");
         return MidiDeviceInfo.PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS;
     }
 
     private boolean openLocked() {
+        Log.d(TAG, "openLocked()");
         UsbManager manager = mContext.getSystemService(UsbManager.class);
 
         mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(mUsbInterfaces.size());
@@ -258,8 +285,10 @@
             }
             if (!outputEndpoints.isEmpty() || !inputEndpoints.isEmpty()) {
                 UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
-                connection.setInterface(interfaceDescriptor.toAndroid(mParser));
-                connection.claimInterface(interfaceDescriptor.toAndroid(mParser), true);
+                UsbInterface usbInterface = interfaceDescriptor.toAndroid(mParser);
+                if (!updateUsbInterface(usbInterface, connection)) {
+                    continue;
+                }
                 mUsbDeviceConnections.add(connection);
                 mInputUsbEndpoints.add(inputEndpoints);
                 mOutputUsbEndpoints.add(outputEndpoints);
@@ -283,14 +312,17 @@
             for (int endpointIndex = 0;
                     endpointIndex < mInputUsbEndpoints.get(connectionIndex).size();
                     endpointIndex++) {
-                final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
-                final UsbEndpoint epF = mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
-                final int portF = portNumber;
+                final UsbDeviceConnection connectionFinal =
+                        mUsbDeviceConnections.get(connectionIndex);
+                final UsbEndpoint endpointFinal =
+                        mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
+                final int portFinal = portNumber;
 
-                new Thread("UsbUniversalMidiDevice input thread " + portF) {
+                new Thread("UsbDirectMidiDevice input thread " + portFinal) {
                     @Override
                     public void run() {
-                        byte[] inputBuffer = new byte[epF.getMaxPacketSize()];
+                        byte[] inputBuffer = new byte[endpointFinal.getMaxPacketSize()];
+                        Log.d(TAG, "input buffer size: " + inputBuffer.length);
                         try {
                             while (true) {
                                 // Record time of event immediately after waking.
@@ -298,20 +330,34 @@
                                 synchronized (mLock) {
                                     if (!mIsOpen) break;
 
-                                    int nRead = connectionF.bulkTransfer(epF, inputBuffer,
-                                            inputBuffer.length, 0);
-
-                                    // For USB, each 32 bit word of a UMP is
-                                    // sent with the least significant byte first.
-                                    swapEndiannessPerWord(inputBuffer, inputBuffer.length);
+                                    int nRead = connectionFinal.bulkTransfer(endpointFinal,
+                                            inputBuffer, inputBuffer.length,
+                                            BULK_TRANSFER_TIMEOUT_MILLISECONDS);
 
                                     if (nRead > 0) {
                                         if (DEBUG) {
-                                            logByteArray("Input ", inputBuffer, 0,
-                                                    nRead);
+                                            logByteArray("Input before conversion ", inputBuffer,
+                                                    0, nRead);
                                         }
-                                        outputReceivers[portF].send(inputBuffer, 0, nRead,
-                                                timestamp);
+                                        byte[] convertedArray;
+                                        if (mIsUniversalMidiDevice) {
+                                            // For USB, each 32 bit word of a UMP is
+                                            // sent with the least significant byte first.
+                                            convertedArray = swapEndiannessPerWord(inputBuffer,
+                                                    nRead);
+                                        } else {
+                                            convertedArray =
+                                                    mUsbMidiPacketConverter.usbMidiToRawMidi(
+                                                             inputBuffer, nRead);
+                                        }
+
+                                        if (DEBUG) {
+                                            logByteArray("Input after conversion ", convertedArray,
+                                                    0, convertedArray.length);
+                                        }
+
+                                        outputReceivers[portFinal].send(convertedArray, 0,
+                                                convertedArray.length, timestamp);
                                     }
                                 }
                             }
@@ -333,19 +379,20 @@
             for (int endpointIndex = 0;
                     endpointIndex < mOutputUsbEndpoints.get(connectionIndex).size();
                     endpointIndex++) {
-                final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
-                final UsbEndpoint epF =
+                final UsbDeviceConnection connectionFinal =
+                        mUsbDeviceConnections.get(connectionIndex);
+                final UsbEndpoint endpointFinal =
                         mOutputUsbEndpoints.get(connectionIndex).get(endpointIndex);
-                final int portF = portNumber;
-                final MidiEventScheduler eventSchedulerF = mEventSchedulers[portF];
+                final int portFinal = portNumber;
+                final MidiEventScheduler eventSchedulerFinal = mEventSchedulers[portFinal];
 
-                new Thread("UsbUniversalMidiDevice output thread " + portF) {
+                new Thread("UsbDirectMidiDevice output thread " + portFinal) {
                     @Override
                     public void run() {
                         while (true) {
                             MidiEvent event;
                             try {
-                                event = (MidiEvent) eventSchedulerF.waitNextEvent();
+                                event = (MidiEvent) eventSchedulerFinal.waitNextEvent();
                             } catch (InterruptedException e) {
                                 // try again
                                 continue;
@@ -354,16 +401,32 @@
                                 break;
                             }
 
-                            // For USB, each 32 bit word of a UMP is
-                            // sent with the least significant byte first.
-                            swapEndiannessPerWord(event.data, event.count);
-
                             if (DEBUG) {
-                                logByteArray("Output ", event.data, 0,
+                                logByteArray("Output before conversion ", event.data, 0,
                                         event.count);
                             }
-                            connectionF.bulkTransfer(epF, event.data, event.count, 0);
-                            eventSchedulerF.addEventToPool(event);
+
+                            byte[] convertedArray;
+                            if (mIsUniversalMidiDevice) {
+                                // For USB, each 32 bit word of a UMP is
+                                // sent with the least significant byte first.
+                                convertedArray = swapEndiannessPerWord(event.data,
+                                        event.count);
+                            } else {
+                                convertedArray =
+                                        mUsbMidiPacketConverter.rawMidiToUsbMidi(
+                                                 event.data, event.count);
+                            }
+
+                            if (DEBUG) {
+                                logByteArray("Output after conversion ", convertedArray, 0,
+                                        convertedArray.length);
+                            }
+
+                            connectionFinal.bulkTransfer(endpointFinal, convertedArray,
+                                    convertedArray.length,
+                                    BULK_TRANSFER_TIMEOUT_MILLISECONDS);
+                            eventSchedulerFinal.addEventToPool(event);
                         }
                         Log.d(TAG, "output thread exit");
                     }
@@ -381,11 +444,15 @@
         mContext = context;
         MidiManager midiManager = context.getSystemService(MidiManager.class);
         if (midiManager == null) {
-            Log.e(TAG, "No MidiManager in UsbUniversalMidiDevice.create()");
+            Log.e(TAG, "No MidiManager in UsbDirectMidiDevice.create()");
             return false;
         }
 
-        mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+        if (mIsUniversalMidiDevice) {
+            mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+        } else {
+            mDefaultMidiProtocol = MidiDeviceInfo.PROTOCOL_UNKNOWN;
+        }
 
         Bundle properties = new Bundle();
         String manufacturer = mUsbDevice.getManufacturerName();
@@ -397,8 +464,15 @@
         } else if (product == null || product.isEmpty()) {
             name = manufacturer;
         } else {
-            name = manufacturer + " " + product + " MIDI 2.0";
+            name = manufacturer + " " + product;
         }
+        name += "#" + mUniqueUsbDeviceIdentifier;
+        if (mIsUniversalMidiDevice) {
+            name += " MIDI 2.0";
+        } else {
+            name += " MIDI 1.0";
+        }
+        Log.e(TAG, name);
         properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
         properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
         properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
@@ -430,10 +504,13 @@
     }
 
     private void closeLocked() {
+        Log.d(TAG, "closeLocked()");
         for (int i = 0; i < mEventSchedulers.length; i++) {
             mMidiInputPortReceivers[i].setReceiver(null);
             mEventSchedulers[i].close();
         }
+        mEventSchedulers = null;
+
         for (UsbDeviceConnection connection : mUsbDeviceConnections) {
             connection.close();
         }
@@ -444,15 +521,19 @@
         mIsOpen = false;
     }
 
-    private void swapEndiannessPerWord(byte[] array, int size) {
-        for (int i = 0; i + 3 < size; i += 4) {
-            byte tmp = array[i];
-            array[i] = array[i + 3];
-            array[i + 3] = tmp;
-            tmp = array[i + 1];
-            array[i + 1] = array[i + 2];
-            array[i + 2] = tmp;
+    private byte[] swapEndiannessPerWord(byte[] inputArray, int size) {
+        int numberOfExcessBytes = size & 3;
+        if (numberOfExcessBytes != 0) {
+            Log.e(TAG, "size not multiple of 4: " + size);
         }
+        byte[] outputArray = new byte[size - numberOfExcessBytes];
+        for (int i = 0; i + 3 < size; i += 4) {
+            outputArray[i] = inputArray[i + 3];
+            outputArray[i + 1] = inputArray[i + 2];
+            outputArray[i + 2] = inputArray[i + 1];
+            outputArray[i + 3] = inputArray[i];
+        }
+        return outputArray;
     }
 
     private static void logByteArray(String prefix, byte[] value, int offset, int count) {
@@ -465,4 +546,24 @@
         }
         Log.d(TAG, builder.toString());
     }
+
+    private boolean updateUsbInterface(UsbInterface usbInterface,
+            UsbDeviceConnection connection) {
+        if (usbInterface == null) {
+            Log.e(TAG, "Usb Interface is null");
+            return false;
+        }
+        if (!connection.claimInterface(usbInterface, true)) {
+            Log.e(TAG, "Can't claim interface");
+            return false;
+        }
+        if (mShouldCallSetInterface) {
+            if (!connection.setInterface(usbInterface)) {
+                Log.w(TAG, "Can't set interface");
+            }
+        } else {
+            Log.w(TAG, "no alternate interface");
+        }
+        return true;
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 94cc826..a70b0332 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -50,9 +50,12 @@
 import libcore.io.IoUtils;
 
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.Random;
 
 /**
  * UsbHostManager manages USB state in host mode.
@@ -94,10 +97,12 @@
     private final ArrayMap<String, ConnectionRecord> mConnected = new ArrayMap<>();
 
     /**
-     * List of connected MIDI devices
+     * List of connected MIDI devices. Key on deviceAddress.
      */
-    private final HashMap<String, UsbUniversalMidiDevice>
-            mMidiDevices = new HashMap<String, UsbUniversalMidiDevice>();
+    private final HashMap<String, ArrayList<UsbDirectMidiDevice>>
+            mMidiDevices = new HashMap<String, ArrayList<UsbDirectMidiDevice>>();
+    private final HashSet<String> mMidiUniqueCodes = new HashSet<String>();
+    private final Random mRandom = new Random();
     private final boolean mHasMidiFeature;
 
     /*
@@ -423,15 +428,35 @@
                 mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
 
                 if (mHasMidiFeature) {
+                    // Use a 3 digit code to associate MIDI devices with one another.
+                    // Each MIDI device already has mId for uniqueness. mId is generated
+                    // sequentially. For clarity, this code is not generated sequentially.
+                    String uniqueUsbDeviceIdentifier = generateNewUsbDeviceIdentifier();
+
+                    ArrayList<UsbDirectMidiDevice> midiDevices =
+                            new ArrayList<UsbDirectMidiDevice>();
                     if (parser.containsUniversalMidiDeviceEndpoint()) {
-                        UsbUniversalMidiDevice midiDevice = UsbUniversalMidiDevice.create(mContext,
-                                newDevice, parser);
+                        UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext,
+                                newDevice, parser, true, uniqueUsbDeviceIdentifier);
                         if (midiDevice != null) {
-                            mMidiDevices.put(deviceAddress, midiDevice);
+                            midiDevices.add(midiDevice);
                         } else {
                             Slog.e(TAG, "Universal Midi Device is null.");
                         }
                     }
+                    if (parser.containsLegacyMidiDeviceEndpoint()) {
+                        UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext,
+                                newDevice, parser, false, uniqueUsbDeviceIdentifier);
+                        if (midiDevice != null) {
+                            midiDevices.add(midiDevice);
+                        } else {
+                            Slog.e(TAG, "Legacy Midi Device is null.");
+                        }
+                    }
+
+                    if (!midiDevices.isEmpty()) {
+                        mMidiDevices.put(deviceAddress, midiDevices);
+                    }
                 }
 
                 // Tracking
@@ -469,10 +494,13 @@
                 mPermissionManager.usbDeviceRemoved(device);
 
                 // MIDI
-                UsbUniversalMidiDevice midiDevice = mMidiDevices.remove(deviceAddress);
-                if (midiDevice != null) {
-                    Slog.i(TAG, "USB Universal MIDI Device Removed: " + deviceAddress);
-                    IoUtils.closeQuietly(midiDevice);
+                ArrayList<UsbDirectMidiDevice> midiDevices =
+                        mMidiDevices.remove(deviceAddress);
+                for (UsbDirectMidiDevice midiDevice : midiDevices) {
+                    if (midiDevice != null) {
+                        Slog.i(TAG, "USB MIDI Device Removed: " + deviceAddress);
+                        IoUtils.closeQuietly(midiDevice);
+                    }
                 }
 
                 getCurrentUserSettings().usbDeviceRemoved(device);
@@ -606,6 +634,19 @@
         return true;
     }
 
+    // Generate a 3 digit code.
+    private String generateNewUsbDeviceIdentifier() {
+        String code;
+        do {
+            code = "";
+            for (int i = 0; i < 3; i++) {
+                code += mRandom.nextInt(10);
+            }
+        } while (mMidiUniqueCodes.contains(code));
+        mMidiUniqueCodes.add(code);
+        return code;
+    }
+
     private native void monitorUsbHostBus();
     private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
 }
diff --git a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
new file mode 100644
index 0000000..7c93c76
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Converts between MIDI packets and USB MIDI 1.0 packets.
+ */
+public class UsbMidiPacketConverter {
+
+    // Refer to Table 4-1 in USB MIDI 1.0 spec.
+    private static final int[] PAYLOAD_SIZE = new int[]{
+            /* 0x00 */ -1, // Miscellaneous function codes. Reserved for future extensions.
+            /* 0x01 */ -1, // Cable events. Reserved for future expansion.
+            /* 0x02 */  2, // Two-byte System Common messages like MTC, SongSelect, etc
+            /* 0x03 */  3, // Three-byte System Common messages like SPP, etc.
+            /* 0x04 */  3, // SysEx starts or continues
+            /* 0x05 */  1, // Single-byte System Common Message or single-byte SysEx ends.
+            /* 0x06 */  2, // SysEx ends with following two bytes.
+            /* 0x07 */  3, // SysEx ends with following three bytes.
+            /* 0x08 */  3, // Note-off
+            /* 0x09 */  3, // Note-on
+            /* 0x0a */  3, // Poly-KeyPress
+            /* 0x0b */  3, // Control Change
+            /* 0x0c */  2, // Program Change
+            /* 0x0d */  2, // Channel Pressure
+            /* 0x0e */  3, // PitchBend Change
+            /* 0x0f */  1  // Single Byte
+    };
+
+    // Each System MIDI message is a certain size. These can be mapped to a
+    // Code Index number defined in Table 4-1 of USB MIDI 1.0.
+    private static final int[] CODE_INDEX_NUMBER_FROM_SYSTEM_TYPE = new int[]{
+            /* 0x00 */ -1, // Start of Exclusive. Special case.
+            /* 0x01 */  2, // MIDI Time Code. Two byte message
+            /* 0x02 */  3, // Song Point Pointer. Three byte message
+            /* 0x03 */  2, // Song Select. Two byte message
+            /* 0x04 */ -1, // Undefined MIDI System Common
+            /* 0x05 */ -1, // Undefined MIDI System Common
+            /* 0x06 */  5, // Tune Request. One byte message
+            /* 0x07 */ -1, // End of Exclusive. Special case.
+            /* 0x08 */  5, // Timing clock. One byte message
+            /* 0x09 */ -1, // Undefined MIDI System Real-time
+            /* 0x0a */  5, // Start. One byte message
+            /* 0x0b */  5, // Continue. One byte message
+            /* 0x0c */  5, // Stop. One byte message
+            /* 0x0d */ -1, // Undefined MIDI System Real-time
+            /* 0x0e */  5, // Active Sensing. One byte message
+            /* 0x0f */  5  // System Reset. One byte message
+    };
+
+    // These code index numbers also come from Table 4-1 in USB MIDI 1.0 spec.
+    private static final byte CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES = 0x4;
+    private static final byte CODE_INDEX_NUMBER_SINGLE_BYTE = 0xF;
+    private static final byte CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE = (byte) 0x5;
+
+    // System messages are defined in MIDI.
+    private static final byte FIRST_SYSTEM_MESSAGE_VALUE = (byte) 0xF0;
+    private static final byte SYSEX_START_EXCLUSIVE = (byte) 0xF0;
+    private static final byte SYSEX_END_EXCLUSIVE = (byte) 0xF7;
+
+    private UsbMidiEncoder mUsbMidiEncoder = new UsbMidiEncoder();
+    private UsbMidiDecoder mUsbMidiDecoder = new UsbMidiDecoder();
+
+    /**
+     * Converts a USB MIDI array into a raw MIDI array.
+     *
+     * @param usbMidiBytes the USB MIDI bytes to convert
+     * @param size the size of usbMidiBytes
+     * @return byte array of raw MIDI packets
+     */
+    public byte[] usbMidiToRawMidi(byte[] usbMidiBytes, int size) {
+        return mUsbMidiDecoder.decode(usbMidiBytes, size);
+    }
+
+    /**
+     * Converts a raw MIDI array into a USB MIDI array.
+     *
+     * @param midiBytes the raw MIDI bytes to convert
+     * @param size the size of usbMidiBytes
+     * @return byte array of USB MIDI packets
+     */
+    public byte[] rawMidiToUsbMidi(byte[] midiBytes, int size) {
+        return mUsbMidiEncoder.encode(midiBytes, size);
+    }
+
+    private class UsbMidiDecoder {
+        // Decodes the data from USB MIDI to raw MIDI.
+        // Each valid 4 byte input maps to a 1-3 byte output.
+        // Reference the USB MIDI 1.0 spec for more info.
+        public byte[] decode(byte[] usbMidiBytes, int size) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            for (int i = 0; i + 3 < size; i += 4) {
+                int codeIndex = usbMidiBytes[i] & 0x0f;
+                int numPayloadBytes = PAYLOAD_SIZE[codeIndex];
+                if (numPayloadBytes < 0) {
+                    continue;
+                }
+                outputStream.write(usbMidiBytes, i + 1, numPayloadBytes);
+            }
+            return outputStream.toByteArray();
+        }
+    }
+
+    private class UsbMidiEncoder {
+        // In order to facilitate large scale transfers, SysEx can be sent in multiple packets.
+        // If encode() is called without an SysEx end, we must continue SysEx for the next packet.
+        // All other packets should be 3 bytes or less and must be not be broken between packets.
+        private byte[] mStoredSystemExclusiveBytes = new byte[3];
+        private int mNumStoredSystemExclusiveBytes = 0;
+        private boolean mHasSystemExclusiveStarted = false;
+
+        private byte[] mEmptyBytes = new byte[3]; // Used to fill out extra data
+
+        // Encodes the data from raw MIDI to USB MIDI.
+        // Each valid 1-3 byte input maps to a 4 byte output.
+        // Reference the USB MIDI 1.0 spec for more info.
+        // MidiFramer is not needed here as this code handles partial packets.
+        // Long SysEx messages split between packets will encode and return a
+        // byte stream even if the SysEx end has not been sent.
+        // If there are less than 3 remaining data bytes in a SysEx message left,
+        // these bytes will be combined with the next set of packets.
+        public byte[] encode(byte[] midiBytes, int size) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            int curLocation = 0;
+            while (curLocation < size) {
+                if (midiBytes[curLocation] >= 0) { // Data byte
+                    if (mHasSystemExclusiveStarted) {
+                        mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] =
+                                midiBytes[curLocation];
+                        mNumStoredSystemExclusiveBytes++;
+                        if (mNumStoredSystemExclusiveBytes == 3) {
+                            outputStream.write(CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES);
+                            outputStream.write(mStoredSystemExclusiveBytes, 0, 3);
+                            mNumStoredSystemExclusiveBytes = 0;
+                        }
+                    } else {
+                        writeSingleByte(outputStream, midiBytes[curLocation]);
+                    }
+                    curLocation++;
+                    continue;
+                } else if (midiBytes[curLocation] != SYSEX_END_EXCLUSIVE) {
+                    // SysEx operation was interrupted. Pass the data directly down.
+                    if (mHasSystemExclusiveStarted) {
+                        int index = 0;
+                        while (index < mNumStoredSystemExclusiveBytes) {
+                            writeSingleByte(outputStream, mStoredSystemExclusiveBytes[index]);
+                            index++;
+                        }
+                        mNumStoredSystemExclusiveBytes = 0;
+                        mHasSystemExclusiveStarted = false;
+                    }
+                }
+
+                if (midiBytes[curLocation] < FIRST_SYSTEM_MESSAGE_VALUE) { // Channel message
+                    byte codeIndexNumber = (byte) ((midiBytes[curLocation] >> 4) & 0x0f);
+                    int channelMessageSize = PAYLOAD_SIZE[codeIndexNumber];
+                    if (curLocation + channelMessageSize <= size) {
+                        outputStream.write(codeIndexNumber);
+                        outputStream.write(midiBytes, curLocation, channelMessageSize);
+                        // Fill in the rest of the bytes with 0.
+                        outputStream.write(mEmptyBytes, 0, 3 - channelMessageSize);
+                        curLocation += channelMessageSize;
+                    } else { // The packet is missing data. Use single byte messages.
+                        while (curLocation < size) {
+                            writeSingleByte(outputStream, midiBytes[curLocation]);
+                            curLocation++;
+                        }
+                    }
+                } else if (midiBytes[curLocation] == SYSEX_START_EXCLUSIVE) {
+                    mHasSystemExclusiveStarted = true;
+                    mStoredSystemExclusiveBytes[0] = midiBytes[curLocation];
+                    mNumStoredSystemExclusiveBytes = 1;
+                    curLocation++;
+                } else if (midiBytes[curLocation] == SYSEX_END_EXCLUSIVE) {
+                    // 1 byte is 0x05, 2 bytes is 0x06, and 3 bytes is 0x07
+                    outputStream.write(CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE
+                            + mNumStoredSystemExclusiveBytes);
+                    mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] =
+                            midiBytes[curLocation];
+                    mNumStoredSystemExclusiveBytes++;
+                    outputStream.write(mStoredSystemExclusiveBytes, 0,
+                             mNumStoredSystemExclusiveBytes);
+                    // Fill in the rest of the bytes with 0.
+                    outputStream.write(mEmptyBytes, 0, 3 - mNumStoredSystemExclusiveBytes);
+                    mHasSystemExclusiveStarted = false;
+                    mNumStoredSystemExclusiveBytes = 0;
+                    curLocation++;
+                } else {
+                    int systemType = midiBytes[curLocation] & 0x0f;
+                    int codeIndexNumber = CODE_INDEX_NUMBER_FROM_SYSTEM_TYPE[systemType];
+                    if (codeIndexNumber < 0) { // Unknown type. Use single byte messages.
+                        writeSingleByte(outputStream, midiBytes[curLocation]);
+                        curLocation++;
+                    } else {
+                        int systemMessageSize = PAYLOAD_SIZE[codeIndexNumber];
+                        if (curLocation + systemMessageSize <= size) {
+                            outputStream.write(codeIndexNumber);
+                            outputStream.write(midiBytes, curLocation, systemMessageSize);
+                            // Fill in the rest of the bytes with 0.
+                            outputStream.write(mEmptyBytes, 0, 3 - systemMessageSize);
+                            curLocation += systemMessageSize;
+                        } else { // The packet is missing data. Use single byte messages.
+                            while (curLocation < size) {
+                                writeSingleByte(outputStream, midiBytes[curLocation]);
+                                curLocation++;
+                            }
+                        }
+                    }
+                }
+            }
+            return outputStream.toByteArray();
+        }
+
+        private void writeSingleByte(ByteArrayOutputStream outputStream, byte byteToWrite) {
+            outputStream.write(CODE_INDEX_NUMBER_SINGLE_BYTE);
+            outputStream.write(byteToWrite);
+            outputStream.write(0);
+            outputStream.write(0);
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 6e68a91..cd6ea68 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -198,14 +198,20 @@
                 if (mCurInterfaceDescriptor != null) {
                     switch (mCurInterfaceDescriptor.getUsbClass()) {
                         case UsbDescriptor.CLASSID_AUDIO:
-                            descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+                            descriptor =
+                                    UsbACInterface.allocDescriptor(this, stream, length, type);
+                            if (descriptor instanceof UsbMSMidiHeader) {
+                                mCurInterfaceDescriptor.setMidiHeaderInterfaceDescriptor(
+                                        descriptor);
+                            }
                             break;
 
                         case UsbDescriptor.CLASSID_VIDEO:
                             if (DEBUG) {
                                 Log.d(TAG, "  UsbDescriptor.CLASSID_VIDEO");
                             }
-                            descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type);
+                            descriptor =
+                                    UsbVCInterface.allocDescriptor(this, stream, length, type);
                             break;
 
                         case UsbDescriptor.CLASSID_AUDIOVIDEO:
@@ -218,7 +224,6 @@
                             Log.w(TAG, "  Unparsed Class-specific");
                             break;
                     }
-                    mCurInterfaceDescriptor.setClassSpecificInterfaceDescriptor(descriptor);
                 }
                 break;
 
@@ -674,6 +679,23 @@
     public boolean containsUniversalMidiDeviceEndpoint() {
         ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
                 findUniversalMidiInterfaceDescriptors();
+        return doesInterfaceContainEndpoint(interfaceDescriptors);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean containsLegacyMidiDeviceEndpoint() {
+        ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
+                findLegacyMidiInterfaceDescriptors();
+        return doesInterfaceContainEndpoint(interfaceDescriptors);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean doesInterfaceContainEndpoint(
+            ArrayList<UsbInterfaceDescriptor> interfaceDescriptors) {
         int outputCount = 0;
         int inputCount = 0;
         for (int interfaceIndex = 0; interfaceIndex < interfaceDescriptors.size();
@@ -698,10 +720,24 @@
      * @hide
      */
     public ArrayList<UsbInterfaceDescriptor> findUniversalMidiInterfaceDescriptors() {
+        return findMidiInterfaceDescriptors(MS_MIDI_2_0);
+    }
+
+    /**
+     * @hide
+     */
+    public ArrayList<UsbInterfaceDescriptor> findLegacyMidiInterfaceDescriptors() {
+        return findMidiInterfaceDescriptors(MS_MIDI_1_0);
+    }
+
+    /**
+     * @hide
+     */
+    private ArrayList<UsbInterfaceDescriptor> findMidiInterfaceDescriptors(int type) {
         int count = 0;
         ArrayList<UsbDescriptor> descriptors =
                 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
-        ArrayList<UsbInterfaceDescriptor> universalMidiInterfaces =
+        ArrayList<UsbInterfaceDescriptor> midiInterfaces =
                 new ArrayList<UsbInterfaceDescriptor>();
 
         for (UsbDescriptor descriptor : descriptors) {
@@ -709,14 +745,14 @@
             if (descriptor instanceof UsbInterfaceDescriptor) {
                 UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
                 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
-                    UsbDescriptor classSpecificDescriptor =
-                            interfaceDescriptor.getClassSpecificInterfaceDescriptor();
-                    if (classSpecificDescriptor != null) {
-                        if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+                    UsbDescriptor midiHeaderDescriptor =
+                            interfaceDescriptor.getMidiHeaderInterfaceDescriptor();
+                    if (midiHeaderDescriptor != null) {
+                        if (midiHeaderDescriptor instanceof UsbMSMidiHeader) {
                             UsbMSMidiHeader midiHeader =
-                                    (UsbMSMidiHeader) classSpecificDescriptor;
-                            if (midiHeader.getMidiStreamingClass() == MS_MIDI_2_0) {
-                                universalMidiInterfaces.add(interfaceDescriptor);
+                                    (UsbMSMidiHeader) midiHeaderDescriptor;
+                            if (midiHeader.getMidiStreamingClass() == type) {
+                                midiInterfaces.add(interfaceDescriptor);
                             }
                         }
                     }
@@ -726,10 +762,13 @@
                         + " t:0x" + Integer.toHexString(descriptor.getType()));
             }
         }
-        return universalMidiInterfaces;
+        return midiInterfaces;
     }
 
-    private int calculateNumLegacyMidiPorts(boolean isOutput) {
+    /**
+     * @hide
+     */
+    public int calculateMidiInterfaceDescriptorsCount() {
         int count = 0;
         ArrayList<UsbDescriptor> descriptors =
                 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
@@ -738,22 +777,12 @@
             if (descriptor instanceof UsbInterfaceDescriptor) {
                 UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
                 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
-                    UsbDescriptor classSpecificDescriptor =
-                            interfaceDescriptor.getClassSpecificInterfaceDescriptor();
-                    if (classSpecificDescriptor != null) {
-                        if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+                    UsbDescriptor midiHeaderDescriptor =
+                            interfaceDescriptor.getMidiHeaderInterfaceDescriptor();
+                    if (midiHeaderDescriptor != null) {
+                        if (midiHeaderDescriptor instanceof UsbMSMidiHeader) {
                             UsbMSMidiHeader midiHeader =
-                                    (UsbMSMidiHeader) classSpecificDescriptor;
-                            if (midiHeader.getMidiStreamingClass() != MS_MIDI_1_0) {
-                                continue;
-                            }
-                        }
-                    }
-                    for (int i = 0; i < interfaceDescriptor.getNumEndpoints(); i++) {
-                        UsbEndpointDescriptor endpoint =
-                                interfaceDescriptor.getEndpointDescriptor(i);
-                        // 0 is output, 1 << 7 is input.
-                        if ((endpoint.getDirection() == 0) == isOutput) {
+                                    (UsbMSMidiHeader) midiHeaderDescriptor;
                             count++;
                         }
                     }
@@ -769,20 +798,6 @@
     /**
      * @hide
      */
-    public int calculateNumLegacyMidiInputs() {
-        return calculateNumLegacyMidiPorts(false /*isOutput*/);
-    }
-
-    /**
-     * @hide
-     */
-    public int calculateNumLegacyMidiOutputs() {
-        return calculateNumLegacyMidiPorts(true /*isOutput*/);
-    }
-
-    /**
-     * @hide
-     */
     public float getInputHeadsetProbability() {
         if (hasMIDIInterface()) {
             return 0.0f;
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index ca4613b..9ddcb10 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -42,7 +42,8 @@
     private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors =
             new ArrayList<UsbEndpointDescriptor>();
 
-    private UsbDescriptor mClassSpecificInterfaceDescriptor;
+    // Used for MIDI only.
+    private UsbDescriptor mMidiHeaderInterfaceDescriptor;
 
     UsbInterfaceDescriptor(int length, byte type) {
         super(length, type);
@@ -107,12 +108,12 @@
         mEndpointDescriptors.add(endpoint);
     }
 
-    public void setClassSpecificInterfaceDescriptor(UsbDescriptor descriptor) {
-        mClassSpecificInterfaceDescriptor = descriptor;
+    public void setMidiHeaderInterfaceDescriptor(UsbDescriptor descriptor) {
+        mMidiHeaderInterfaceDescriptor = descriptor;
     }
 
-    public UsbDescriptor getClassSpecificInterfaceDescriptor() {
-        return mClassSpecificInterfaceDescriptor;
+    public UsbDescriptor getMidiHeaderInterfaceDescriptor() {
+        return mMidiHeaderInterfaceDescriptor;
     }
 
     /**