Add MIDI 2.0 host mode support

Adding MIDI 2.0 host mode support to Android. MIDI 2.0 peripherals
create an alternative setting for Universal Midi Packets (UMP).
UMP packets can be passed directly through as the host has full
control. Instead of going through the ALSA, UMP packets can be
sent directly through a UsbDeviceConnection.

This CL also adds public apis to expose whether the pipe supports
UMP. This for MidiDeviceInfo in Java and AMidi in c++.

Bug: 201003646
Bug: 214447324
Test: Verified that the MIDI 1.0 path still works as before.
Test: Tested that MIDI 2.0 packets are passed through the pipe
with MIDIScope and MIDIKeyboard
Test: atest MidiSoloTest
Test: atest CtsMidiTestCases
Test: NativeMidiEchoTest

Change-Id: Idff8a3b9bdd05857239260fc3e6af7253f8f992f
diff --git a/media/java/android/media/midi/IMidiManager.aidl b/media/java/android/media/midi/IMidiManager.aidl
index d3c8e0a..b03f785 100644
--- a/media/java/android/media/midi/IMidiManager.aidl
+++ b/media/java/android/media/midi/IMidiManager.aidl
@@ -31,6 +31,8 @@
 {
     MidiDeviceInfo[] getDevices();
 
+    MidiDeviceInfo[] getDevicesForTransport(int transport);
+
     // for device creation & removal notifications
     void registerListener(IBinder clientToken, in IMidiDeviceListener listener);
     void unregisterListener(IBinder clientToken, in IMidiDeviceListener listener);
@@ -43,7 +45,7 @@
     // for registering built-in MIDI devices
     MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts,
             int numOutputPorts, in String[] inputPortNames, in String[] outputPortNames,
-            in Bundle properties, int type);
+            in Bundle properties, int type, int defaultProtocol);
 
     // for unregistering built-in MIDI devices
     void unregisterDeviceServer(in IMidiDeviceServer server);
diff --git a/media/java/android/media/midi/MidiDeviceInfo.java b/media/java/android/media/midi/MidiDeviceInfo.java
index dd3b6db..b888fb0 100644
--- a/media/java/android/media/midi/MidiDeviceInfo.java
+++ b/media/java/android/media/midi/MidiDeviceInfo.java
@@ -16,11 +16,15 @@
 
 package android.media.midi;
 
+import android.annotation.IntDef;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * This class contains information to describe a MIDI device.
  * For now we only have information that can be retrieved easily for USB devices,
@@ -54,6 +58,110 @@
     public static final int TYPE_BLUETOOTH = 3;
 
     /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use UMP to negotiate with the device with MIDI-CI.
+     * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_USE_MIDI_CI = 0;
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 64 bits.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1;
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 64 bits and jitter reduction timestamps.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2;
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 128 bits.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3;
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 128 bits and jitter reduction timestamps.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4;
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 2.0 through UMP.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_2_0 = 17;
+
+     /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 2.0 through UMP and jitter reduction timestamps.
+     * Call {@link MidiManager#getDevicesForTransport} with parameter
+     * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18;
+
+    /**
+     * Constant representing a device with an unknown default protocol.
+     * If Universal MIDI Packets (UMP) are needed, use MIDI-CI through MIDI 1.0.
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+     * @see MidiDeviceInfo#getDefaultProtocol
+     */
+    public static final int PROTOCOL_UNKNOWN = -1;
+
+    /**
+     * @see MidiDeviceInfo#getDefaultProtocol
+     * @hide
+     */
+    @IntDef(prefix = { "PROTOCOL_" }, value = {
+            PROTOCOL_UMP_USE_MIDI_CI,
+            PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS,
+            PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS,
+            PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS,
+            PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS,
+            PROTOCOL_UMP_MIDI_2_0,
+            PROTOCOL_UMP_MIDI_2_0_AND_JRTS,
+            PROTOCOL_UNKNOWN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DefaultProtocol {}
+
+    /**
      * Bundle key for the device's user visible name property.
      * The value for this property is of type {@link java.lang.String}.
      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}.
@@ -196,6 +304,7 @@
     private final String[] mOutputPortNames;
     private final Bundle mProperties;
     private final boolean mIsPrivate;
+    private final int mDefaultProtocol;
 
     /**
      * MidiDeviceInfo should only be instantiated by MidiService implementation
@@ -203,7 +312,7 @@
      */
     public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
             String[] inputPortNames, String[] outputPortNames, Bundle properties,
-            boolean isPrivate) {
+            boolean isPrivate, int defaultProtocol) {
         // Check num ports for out-of-range values. Typical values will be
         // between zero and three. More than 16 would be very unlikely
         // because the port index field in the USB packet is only 4 bits.
@@ -234,6 +343,7 @@
         }
         mProperties = properties;
         mIsPrivate = isPrivate;
+        mDefaultProtocol = defaultProtocol;
     }
 
     /**
@@ -312,6 +422,18 @@
         return mIsPrivate;
     }
 
+    /**
+     * Returns the default protocol. For most devices, this will be {@link #PROTOCOL_UNKNOWN}.
+     * Returning {@link #PROTOCOL_UNKNOWN} is not an error; the device just doesn't support
+     * Universal MIDI Packets by default.
+     *
+     * @return the device's default protocol.
+     */
+    @DefaultProtocol
+    public int getDefaultProtocol() {
+        return mDefaultProtocol;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (o instanceof MidiDeviceInfo) {
@@ -331,11 +453,12 @@
         // This is a hack to force the mProperties Bundle to unparcel so we can
         // print all the names and values.
         mProperties.getString(PROPERTY_NAME);
-        return ("MidiDeviceInfo[mType=" + mType +
-                ",mInputPortCount=" + mInputPortCount +
-                ",mOutputPortCount=" + mOutputPortCount +
-                ",mProperties=" + mProperties +
-                ",mIsPrivate=" + mIsPrivate);
+        return ("MidiDeviceInfo[mType=" + mType
+                + ",mInputPortCount=" + mInputPortCount
+                + ",mOutputPortCount=" + mOutputPortCount
+                + ",mProperties=" + mProperties
+                + ",mIsPrivate=" + mIsPrivate
+                + ",mDefaultProtocol=" + mDefaultProtocol);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<MidiDeviceInfo> CREATOR =
@@ -349,10 +472,12 @@
             String[] inputPortNames = in.createStringArray();
             String[] outputPortNames = in.createStringArray();
             boolean isPrivate = (in.readInt() == 1);
+            int defaultProtocol = in.readInt();
             Bundle basicPropertiesIgnored = in.readBundle();
             Bundle properties = in.readBundle();
             return new MidiDeviceInfo(type, id, inputPortCount, outputPortCount,
-                    inputPortNames, outputPortNames, properties, isPrivate);
+                    inputPortNames, outputPortNames, properties, isPrivate,
+                    defaultProtocol);
         }
 
         public MidiDeviceInfo[] newArray(int size) {
@@ -390,6 +515,7 @@
         parcel.writeStringArray(mInputPortNames);
         parcel.writeStringArray(mOutputPortNames);
         parcel.writeInt(mIsPrivate ? 1 : 0);
+        parcel.writeInt(mDefaultProtocol);
         // "Basic" properties only contain properties of primitive types
         // and thus can be read back by native code. "Extra" properties is
         // a superset that contains all properties.
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index dee94c68..a049a88 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -16,18 +16,25 @@
 
 package android.media.midi;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.SystemService;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
-import android.os.IBinder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -39,6 +46,39 @@
     private static final String TAG = "MidiManager";
 
     /**
+     * Constant representing MIDI devices.
+     * These devices do NOT support Universal MIDI Packets by default.
+     * These support the original MIDI 1.0 byte stream.
+     * When communicating to a USB device, a raw byte stream will be padded for USB.
+     * Likewise, for a Bluetooth device, the raw bytes will be converted for Bluetooth.
+     * For virtual devices, the byte stream will be passed directly.
+     * If Universal MIDI Packets are needed, please use MIDI-CI.
+     * @see MidiManager#getDevicesForTransport
+     */
+    public static final int TRANSPORT_MIDI_BYTE_STREAM = 1;
+
+    /**
+     * Constant representing Universal MIDI devices.
+     * These devices do support Universal MIDI Packets (UMP) by default.
+     * When sending data to these devices, please send UMP.
+     * Packets should always be a multiple of 4 bytes.
+     * UMP is defined in the USB MIDI 2.0 spec. Please read the standard for more info.
+     * @see MidiManager#getDevicesForTransport
+     */
+    public static final int TRANSPORT_UNIVERSAL_MIDI_PACKETS = 2;
+
+    /**
+     * @see MidiManager#getDevicesForTransport
+     * @hide
+     */
+    @IntDef(prefix = { "TRANSPORT_" }, value = {
+            TRANSPORT_MIDI_BYTE_STREAM,
+            TRANSPORT_UNIVERSAL_MIDI_PACKETS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Transport {}
+
+    /**
      * Intent for starting BluetoothMidiService
      * @hide
      */
@@ -68,37 +108,43 @@
     private class DeviceListener extends IMidiDeviceListener.Stub {
         private final DeviceCallback mCallback;
         private final Handler mHandler;
+        private final int mTransport;
 
-        public DeviceListener(DeviceCallback callback, Handler handler) {
+        DeviceListener(DeviceCallback callback, Handler handler, int transport) {
             mCallback = callback;
             mHandler = handler;
+            mTransport = transport;
         }
 
         @Override
         public void onDeviceAdded(MidiDeviceInfo device) {
-            if (mHandler != null) {
-                final MidiDeviceInfo deviceF = device;
-                mHandler.post(new Runnable() {
-                        @Override public void run() {
-                            mCallback.onDeviceAdded(deviceF);
-                        }
-                    });
-            } else {
-                mCallback.onDeviceAdded(device);
+            if (shouldInvokeCallback(device)) {
+                if (mHandler != null) {
+                    final MidiDeviceInfo deviceF = device;
+                    mHandler.post(new Runnable() {
+                            @Override public void run() {
+                                mCallback.onDeviceAdded(deviceF);
+                            }
+                        });
+                } else {
+                    mCallback.onDeviceAdded(device);
+                }
             }
         }
 
         @Override
         public void onDeviceRemoved(MidiDeviceInfo device) {
-            if (mHandler != null) {
-                final MidiDeviceInfo deviceF = device;
-                mHandler.post(new Runnable() {
-                        @Override public void run() {
-                            mCallback.onDeviceRemoved(deviceF);
-                        }
-                    });
-            } else {
-                mCallback.onDeviceRemoved(device);
+            if (shouldInvokeCallback(device)) {
+                if (mHandler != null) {
+                    final MidiDeviceInfo deviceF = device;
+                    mHandler.post(new Runnable() {
+                            @Override public void run() {
+                                mCallback.onDeviceRemoved(deviceF);
+                            }
+                        });
+                } else {
+                    mCallback.onDeviceRemoved(device);
+                }
             }
         }
 
@@ -115,6 +161,25 @@
                 mCallback.onDeviceStatusChanged(status);
             }
         }
+
+        /**
+         * Used to figure out whether callbacks should be invoked. Only invoke callbacks of
+         * the correct type.
+         *
+         * @param MidiDeviceInfo the device to check
+         * @return whether to invoke a callback
+         */
+        private boolean shouldInvokeCallback(MidiDeviceInfo device) {
+            // UMP devices have protocols that are not PROTOCOL_UNKNOWN
+            if (mTransport == TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
+                return (device.getDefaultProtocol() != MidiDeviceInfo.PROTOCOL_UNKNOWN);
+            } else if (mTransport == TRANSPORT_MIDI_BYTE_STREAM) {
+                return (device.getDefaultProtocol() == MidiDeviceInfo.PROTOCOL_UNKNOWN);
+            } else {
+                Log.e(TAG, "Invalid transport type: " + mTransport);
+                return false;
+            }
+        }
     }
 
     /**
@@ -167,8 +232,10 @@
     }
 
     /**
-     * Registers a callback to receive notifications when MIDI devices are added and removed.
-     *
+     * Registers a callback to receive notifications when MIDI 1.0 devices are added and removed.
+     * These are devices that do not default to Universal MIDI Packets. To register for a callback
+     * for those, call {@link #registerDeviceCallbackForTransport} instead.
+
      * The {@link  DeviceCallback#onDeviceStatusChanged} method will be called immediately
      * for any devices that have open ports. This allows applications to know which input
      * ports are already in use and, therefore, unavailable.
@@ -182,7 +249,30 @@
      *                callback is unspecified.
      */
     public void registerDeviceCallback(DeviceCallback callback, Handler handler) {
-        DeviceListener deviceListener = new DeviceListener(callback, handler);
+        registerDeviceCallbackForTransport(callback, handler, TRANSPORT_MIDI_BYTE_STREAM);
+    }
+
+    /**
+     * Registers a callback to receive notifications when MIDI devices are added and removed
+     * for a specific transport type.
+     *
+     * The {@link  DeviceCallback#onDeviceStatusChanged} method will be called immediately
+     * for any devices that have open ports. This allows applications to know which input
+     * ports are already in use and, therefore, unavailable.
+     *
+     * Applications should call {@link #getDevicesForTransport} before registering the callback
+     * to get a list of devices already added.
+     *
+     * @param callback a {@link DeviceCallback} for MIDI device notifications
+     * @param handler The {@link android.os.Handler Handler} that will be used for delivering the
+     *                device notifications. If handler is null, then the thread used for the
+     *                callback is unspecified.
+     * @param transport The transport to be used. This is either TRANSPORT_MIDI_BYTE_STREAM or
+     *            TRANSPORT_UNIVERSAL_MIDI_PACKETS.
+     */
+    public void registerDeviceCallbackForTransport(@NonNull DeviceCallback callback,
+            @Nullable Handler handler, @Transport int transport) {
+        DeviceListener deviceListener = new DeviceListener(callback, handler, transport);
         try {
             mService.registerListener(mToken, deviceListener);
         } catch (RemoteException e) {
@@ -208,9 +298,11 @@
     }
 
     /**
-     * Gets the list of all connected MIDI devices.
+     * Gets a list of connected MIDI devices. This returns all devices that do
+     * not default to Universal MIDI Packets. To get those instead, please call
+     * {@link #getDevicesForTransport} instead.
      *
-     * @return an array of all MIDI devices
+     * @return an array of MIDI devices
      */
     public MidiDeviceInfo[] getDevices() {
         try {
@@ -220,6 +312,29 @@
         }
     }
 
+    /**
+     * Gets a list of connected MIDI devices by transport. TRANSPORT_MIDI_BYTE_STREAM
+     * is used for MIDI 1.0 and is the most common.
+     * For devices with built in Universal MIDI Packet support, use
+     * TRANSPORT_UNIVERSAL_MIDI_PACKETS instead.
+     *
+     * @param transport The transport to be used. This is either TRANSPORT_MIDI_BYTE_STREAM or
+     *                  TRANSPORT_UNIVERSAL_MIDI_PACKETS.
+     * @return a collection of MIDI devices
+     */
+    public @NonNull Collection<MidiDeviceInfo> getDevicesForTransport(@Transport int transport) {
+        try {
+            MidiDeviceInfo[] devices = mService.getDevicesForTransport(transport);
+            Collection<MidiDeviceInfo> out = new ArrayList<MidiDeviceInfo>(devices.length);
+            for (int i = 0; i < devices.length; i++) {
+                out.add(devices[i]);
+            }
+            return out;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private void sendOpenDeviceResponse(final MidiDevice device,
             final OnDeviceOpenedListener listener, Handler handler) {
         if (handler != null) {
@@ -311,13 +426,14 @@
     /** @hide */
     public MidiDeviceServer createDeviceServer(MidiReceiver[] inputPortReceivers,
             int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
-            Bundle properties, int type, MidiDeviceServer.Callback callback) {
+            Bundle properties, int type, int defaultProtocol,
+            MidiDeviceServer.Callback callback) {
         try {
             MidiDeviceServer server = new MidiDeviceServer(mService, inputPortReceivers,
                     numOutputPorts, callback);
             MidiDeviceInfo deviceInfo = mService.registerDeviceServer(server.getBinderInterface(),
                     inputPortReceivers.length, numOutputPorts, inputPortNames, outputPortNames,
-                    properties, type);
+                    properties, type, defaultProtocol);
             if (deviceInfo == null) {
                 Log.e(TAG, "registerVirtualDevice failed");
                 return null;
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index 33c5490..67df1b2 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -405,5 +405,46 @@
 <a href="#get_list_of_already_plugged_in_entities">MIDI device discovery calls described above</a>.
 </p>
 
+<h1 id=using_midi_2_0_over_usb>Using MIDI 2.0 over USB</h1>
+
+<p>An app can use MIDI 2.0 over USB starting in Android T. MIDI 2.0 packets are embedded in
+Universal MIDI Packets, or UMP for short. A MIDI 2.0 USB device should create two interfaces,
+one endpoint that accepts only MIDI 1.0 packets and one that accepts only UMP packets.
+For more info about MIDI 2.0 and UMP, please read the MIDI 2.0 USB spec.</p>
+
+<p>MidiManager.getDevices() would simply return the 1.0 interface. This interface should work
+exactly the same as before. In order to use the new UMP interface, retrieve the device with the
+following code snippet.</p>
+
+<pre class=prettyprint>
+Collection&#60;MidiDeviceInfo&#62; universalDeviceInfos = midiManager.getDevicesForTransport(
+        MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS);
+</pre>
+
+<p>UMP Packets are always in multiple of 4 bytes. For each set of 4 bytes, they are sent in network
+order. Compare the following NoteOn code snippet with the NoteOn code snippet above. </p>
+
+<pre class=prettyprint>
+byte[] buffer = new byte[32];
+int numBytes = 0;
+int channel = 3; // MIDI channels 1-16 are encoded as 0-15.
+int group = 0;
+buffer[numBytes++] = (byte)(0x20 + group); // MIDI 1.0 voice message
+buffer[numBytes++] = (byte)(0x90 + (channel - 1)); // note on
+buffer[numBytes++] = (byte)60; // pitch is middle C
+buffer[numBytes++] = (byte)127; // max velocity
+int offset = 0;
+// post is non-blocking
+inputPort.send(buffer, offset, numBytes);
+</pre>
+
+<p>MIDI 2.0 messages can be sent through UMP after negotiating with the device. This is called
+MIDI-CI and is documented in the MIDI 2.0 spec. Some USB devices support pre-negotiated MIDI 2.0.
+For a MidiDeviceInfo, you can query the defaultProtocol.</p>
+
+<pre class=prettyprint>
+int defaultProtocol = info.getDefaultProtocol();
+</pre>
+
 </body>
 </html>
diff --git a/media/native/midi/MidiDeviceInfo.cpp b/media/native/midi/MidiDeviceInfo.cpp
index 8a573fb..1452488 100644
--- a/media/native/midi/MidiDeviceInfo.cpp
+++ b/media/native/midi/MidiDeviceInfo.cpp
@@ -64,6 +64,7 @@
     RETURN_IF_FAILED(writeStringVector(parcel, mInputPortNames));
     RETURN_IF_FAILED(writeStringVector(parcel, mOutputPortNames));
     RETURN_IF_FAILED(parcel->writeInt32(mIsPrivate ? 1 : 0));
+    RETURN_IF_FAILED(parcel->writeInt32(mDefaultProtocol));
     RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
     // This corresponds to "extra" properties written by Java code
     RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
@@ -83,6 +84,7 @@
     int32_t isPrivate;
     RETURN_IF_FAILED(parcel->readInt32(&isPrivate));
     mIsPrivate = isPrivate == 1;
+    RETURN_IF_FAILED(parcel->readInt32(&mDefaultProtocol));
     RETURN_IF_FAILED(mProperties.readFromParcel(parcel));
     // Ignore "extra" properties as they may contain Java Parcelables
     return OK;
@@ -130,7 +132,8 @@
             areVectorsEqual(lhs.mInputPortNames, rhs.mInputPortNames) &&
             areVectorsEqual(lhs.mOutputPortNames, rhs.mOutputPortNames) &&
             lhs.mProperties == rhs.mProperties &&
-            lhs.mIsPrivate == rhs.mIsPrivate);
+            lhs.mIsPrivate == rhs.mIsPrivate &&
+            lhs.mDefaultProtocol == rhs.mDefaultProtocol);
 }
 
 }  // namespace midi
diff --git a/media/native/midi/MidiDeviceInfo.h b/media/native/midi/MidiDeviceInfo.h
index 5b4a241..23e1cb4 100644
--- a/media/native/midi/MidiDeviceInfo.h
+++ b/media/native/midi/MidiDeviceInfo.h
@@ -38,6 +38,7 @@
     int getType() const { return mType; }
     int getUid() const { return mId; }
     bool isPrivate() const { return mIsPrivate; }
+    int getDefaultProtocol() const { return mDefaultProtocol; }
     const Vector<String16>& getInputPortNames() const { return mInputPortNames; }
     const Vector<String16>&  getOutputPortNames() const { return mOutputPortNames; }
     String16 getProperty(const char* propertyName);
@@ -48,6 +49,18 @@
         TYPE_VIRTUAL = 2,
         TYPE_BLUETOOTH = 3,
     };
+
+    enum {
+        PROTOCOL_UMP_USE_MIDI_CI = 0,
+        PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1,
+        PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2,
+        PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3,
+        PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4,
+        PROTOCOL_UMP_MIDI_2_0 = 17,
+        PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18,
+        PROTOCOL_UNKNOWN = -1,
+    };
+
     static const char* const PROPERTY_NAME;
     static const char* const PROPERTY_MANUFACTURER;
     static const char* const PROPERTY_PRODUCT;
@@ -72,6 +85,7 @@
     Vector<String16> mOutputPortNames;
     os::PersistableBundle mProperties;
     bool mIsPrivate;
+    int32_t mDefaultProtocol;
 };
 
 }  // namespace midi
diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp
index f90796e..aa076e8 100644
--- a/media/native/midi/amidi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -138,6 +138,7 @@
     outDeviceInfoPtr->type = deviceInfo.getType();
     outDeviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
     outDeviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
+    outDeviceInfoPtr->defaultProtocol = deviceInfo.getDefaultProtocol();
 
     return AMEDIA_OK;
 }
@@ -238,6 +239,13 @@
     return device->deviceInfo.outputPortCount;
 }
 
+AMidiDevice_Protocol AMIDI_API AMidiDevice_getDefaultProtocol(const AMidiDevice *device) {
+    if (device == nullptr) {
+        return AMIDI_DEVICE_PROTOCOL_UNKNOWN;
+    }
+    return static_cast<AMidiDevice_Protocol>(device->deviceInfo.defaultProtocol);
+}
+
 /*
  * Port Helpers
  */
diff --git a/media/native/midi/amidi_internal.h b/media/native/midi/amidi_internal.h
index fce8596..023a6f5 100644
--- a/media/native/midi/amidi_internal.h
+++ b/media/native/midi/amidi_internal.h
@@ -25,6 +25,7 @@
     int32_t type;            /* one of AMIDI_DEVICE_TYPE_* constants */
     int32_t inputPortCount;  /* number of input (send) ports associated with the device */
     int32_t outputPortCount; /* number of output (received) ports associated with the device */
+    int32_t defaultProtocol; /* one of the AMIDI_DEVICE_PROTOCOL_* constants */
 } AMidiDeviceInfo;
 
 struct AMidiDevice {
diff --git a/media/native/midi/include/amidi/AMidi.h b/media/native/midi/include/amidi/AMidi.h
index 742db34..fbb7fb3 100644
--- a/media/native/midi/include/amidi/AMidi.h
+++ b/media/native/midi/include/amidi/AMidi.h
@@ -62,6 +62,78 @@
 };
 
 /*
+ * Protocol IDs for various MIDI devices.
+ *
+ * Introduced in API 33.
+ */
+enum AMidiDevice_Protocol : int32_t {
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use UMP to negotiate with the device with MIDI-CI.
+     * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_USE_MIDI_CI = 0,
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 64 bits.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1,
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 64 bits and jitter reduction timestamps.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2,
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 128 bits.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3,
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 1.0 through UMP with packet sizes up to 128 bits and jitter reduction timestamps.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4,
+
+    /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 2.0 through UMP.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0 = 17,
+
+     /**
+     * Constant representing a default protocol with Universal MIDI Packets (UMP).
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * All UMP data should be a multiple of 4 bytes.
+     * Use MIDI 2.0 through UMP and jitter reduction timestamps.
+     */
+    AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18,
+
+    /**
+     * Constant representing a device with an unknown default protocol.
+     * If Universal MIDI Packets (UMP) are needed, use MIDI-CI through MIDI 1.0.
+     * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+     * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+     */
+    AMIDI_DEVICE_PROTOCOL_UNKNOWN = -1
+};
+
+/*
  * Device API
  */
 /**
@@ -134,6 +206,30 @@
  */
 ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
 
+/**
+ * Gets the MIDI device default protocol.
+ *
+ * @param device Specifies the MIDI device.
+ *
+ * @return The identifier of the MIDI device default protocol:
+ * AMIDI_DEVICE_PROTOCOL_UMP_USE_MIDI_CI
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0_AND_JRTS
+ * AMIDI_DEVICE_PROTOCOL_UNKNOWN
+ *
+ * Most devices should return PROTOCOL_UNKNOWN (-1). This is intentional as devices
+ * with default UMP support are not backwards compatible. When the device is null,
+ * return AMIDI_DEVICE_PROTOCOL_UNKNOWN.
+ *
+ * Available since API 33.
+ */
+AMidiDevice_Protocol AMIDI_API AMidiDevice_getDefaultProtocol(const AMidiDevice *device)
+        __INTRODUCED_IN(33);
+
 /*
  * API for receiving data from the Output port of a device.
  */
diff --git a/media/native/midi/libamidi.map.txt b/media/native/midi/libamidi.map.txt
index 62627f8..f25f977 100644
--- a/media/native/midi/libamidi.map.txt
+++ b/media/native/midi/libamidi.map.txt
@@ -2,6 +2,7 @@
   global:
     AMidiDevice_fromJava;
     AMidiDevice_release;
+    AMidiDevice_getDefaultProtocol; # introduced=Tiramisu
     AMidiDevice_getType;
     AMidiDevice_getNumInputPorts;
     AMidiDevice_getNumOutputPorts;
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 62c313a..2dd9525 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -260,7 +260,8 @@
         inputPortReceivers[0] = mEventScheduler.getReceiver();
 
         mDeviceServer = mMidiManager.createDeviceServer(inputPortReceivers, 1,
-                null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, mDeviceServerCallback);
+                null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH,
+                MidiDeviceInfo.PROTOCOL_UNKNOWN, mDeviceServerCallback);
 
         mOutputReceiver = mDeviceServer.getOutputPortReceivers()[0];