diff options
| -rw-r--r-- | services/midi/java/com/android/server/midi/MidiService.java | 108 | ||||
| -rw-r--r-- | services/usb/java/com/android/server/usb/UsbAlsaManager.java | 59 | ||||
| -rw-r--r-- | services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java (renamed from services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java) | 213 | ||||
| -rw-r--r-- | services/usb/java/com/android/server/usb/UsbHostManager.java | 61 | ||||
| -rw-r--r-- | services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java | 237 | ||||
| -rw-r--r-- | services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java | 99 | ||||
| -rw-r--r-- | services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java | 11 |
7 files changed, 616 insertions, 172 deletions
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index 6e724792b6e9..046573d87dd2 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -62,6 +62,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -114,6 +115,19 @@ public class MidiService extends IMidiManager.Stub { 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; @@ -246,6 +260,12 @@ public class MidiService extends IMidiManager.Stub { } for (DeviceConnection connection : mDeviceConnections.values()) { + if (connection.getDevice().getDeviceInfo().getType() + == MidiDeviceInfo.TYPE_USB) { + synchronized (mUsbMidiLock) { + removeUsbMidiDeviceLocked(connection.getDevice().getDeviceInfo()); + } + } connection.getDevice().removeDeviceConnection(connection); } } @@ -698,6 +718,7 @@ public class MidiService extends IMidiManager.Stub { 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() @@ -734,6 +755,15 @@ public class MidiService extends IMidiManager.Stub { } } + 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 { @@ -1145,4 +1175,82 @@ public class MidiService extends IMidiManager.Stub { } 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 1c72eb8db708..6d934a985e4e 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 libcore.io.IoUtils; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; /** @@ -107,12 +106,6 @@ public final class UsbAlsaManager { 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; @@ -255,45 +248,6 @@ public final class UsbAlsaManager { } } - // 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) { @@ -314,13 +268,6 @@ public final class UsbAlsaManager { 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()"); } @@ -376,12 +323,6 @@ public final class UsbAlsaManager { 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 index 13d404caf6a2..0fa79df3e008 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.content.Context; 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.io.IOException; 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 @@ public final class UsbUniversalMidiDevice implements Closeable { // 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 @@ public final class UsbUniversalMidiDevice implements Closeable { 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 @@ public final class UsbUniversalMidiDevice implements Closeable { } /** - * 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 @@ public final class UsbUniversalMidiDevice implements Closeable { 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; + + // Set interface should only be called when alternate interfaces exist. + // Otherwise, USB devices may not handle this gracefully. + mShouldCallSetInterface = (parser.calculateMidiInterfaceDescriptorsCount() > 1); - mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors(); + if (isUniversalMidiDevice) { + mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors(); + } else { + mUsbInterfaces = parser.findLegacyMidiInterfaceDescriptors(); + } int numInputs = 0; int numOutputs = 0; @@ -182,8 +207,8 @@ public final class UsbUniversalMidiDevice implements Closeable { 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 @@ public final class UsbUniversalMidiDevice implements Closeable { 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 @@ public final class UsbUniversalMidiDevice implements Closeable { } 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 @@ public final class UsbUniversalMidiDevice implements Closeable { 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 @@ public final class UsbUniversalMidiDevice implements Closeable { 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, + logByteArray("Input before conversion ", inputBuffer, + 0, nRead); + } + 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); } - outputReceivers[portF].send(inputBuffer, 0, nRead, - timestamp); + + if (DEBUG) { + logByteArray("Input after conversion ", convertedArray, + 0, convertedArray.length); + } + + outputReceivers[portFinal].send(convertedArray, 0, + convertedArray.length, timestamp); } } } @@ -333,19 +379,20 @@ public final class UsbUniversalMidiDevice implements Closeable { 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 @@ public final class UsbUniversalMidiDevice implements Closeable { 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 @@ public final class UsbUniversalMidiDevice implements Closeable { 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 @@ public final class UsbUniversalMidiDevice implements Closeable { } 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 @@ public final class UsbUniversalMidiDevice implements Closeable { } 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 @@ public final class UsbUniversalMidiDevice implements Closeable { mIsOpen = false; } - private void swapEndiannessPerWord(byte[] array, int size) { + 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) { - 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; + 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 @@ public final class UsbUniversalMidiDevice implements Closeable { } 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 9ac270f17fc4..f09b61bc52c2 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 com.android.server.usb.descriptors.tree.UsbDescriptorsTree; 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 @@ public class UsbHostManager { 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; /* @@ -425,15 +430,35 @@ public class UsbHostManager { 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 @@ -471,10 +496,13 @@ public class UsbHostManager { 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); @@ -608,6 +636,19 @@ public class UsbHostManager { 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 000000000000..7c93c7668fa2 --- /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 3412a6f80cc7..9f2fef2f1d9b 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 @@ public final class UsbDescriptorParser { 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 @@ public final class UsbDescriptorParser { Log.w(TAG, " Unparsed Class-specific"); break; } - mCurInterfaceDescriptor.setClassSpecificInterfaceDescriptor(descriptor); } break; @@ -674,6 +679,23 @@ public final class UsbDescriptorParser { 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 @@ public final class UsbDescriptorParser { * @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 @@ public final class UsbDescriptorParser { 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 @@ public final class UsbDescriptorParser { + " 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 @@ public final class UsbDescriptorParser { 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 @@ public final class UsbDescriptorParser { /** * @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 ca4613b17873..9ddcb10e96e4 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 @@ public class UsbInterfaceDescriptor extends UsbDescriptor { 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 @@ public class UsbInterfaceDescriptor extends UsbDescriptor { 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; } /** |