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;
}
/**