diff options
5 files changed, 180 insertions, 9 deletions
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java index 42a5af7fdce3..17c354a8a80e 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java @@ -36,6 +36,7 @@ import libcore.io.IoUtils; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; /** @@ -106,6 +107,12 @@ 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; @@ -249,6 +256,8 @@ public final class UsbAlsaManager { } } + addMidiDevice(deviceAddress, usbDevice, parser, cardRec); + logDevices("deviceAdded()"); if (DEBUG) { @@ -256,6 +265,54 @@ public final class UsbAlsaManager { } } + private void addMidiDevice(String deviceAddress, UsbDevice usbDevice, + UsbDescriptorParser parser, AlsaCardsParser.AlsaCardRecord cardRec) { + boolean hasMidi = parser.hasMIDIInterface(); + // UsbHostManager will create UsbDirectMidiDevices instead if MIDI 2 is supported. + boolean hasMidi2 = parser.containsUniversalMidiDeviceEndpoint(); + if (DEBUG) { + Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature); + Slog.d(TAG, "hasMidi2: " + hasMidi2); + } + if (mHasMidiFeature && hasMidi && !hasMidi2) { + 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); + + int numLegacyMidiInputs = parser.calculateNumLegacyMidiInputs(); + int numLegacyMidiOutputs = parser.calculateNumLegacyMidiOutputs(); + if (DEBUG) { + Slog.d(TAG, "numLegacyMidiInputs: " + numLegacyMidiInputs); + Slog.d(TAG, "numLegacyMidiOutputs:" + numLegacyMidiOutputs); + } + + UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties, + cardRec.getCardNum(), 0 /*device*/, numLegacyMidiInputs, + numLegacyMidiOutputs); + if (usbMidiDevice != null) { + mMidiDevices.put(deviceAddress, usbMidiDevice); + } + } + } + /* package */ synchronized void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) { if (DEBUG) { Slog.d(TAG, "deviceRemoved(" + deviceAddress + ")"); @@ -269,6 +326,13 @@ 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: " + deviceAddress); + IoUtils.closeQuietly(usbMidiDevice); + } + logDevices("usbDeviceRemoved()"); } @@ -324,6 +388,12 @@ 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/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java index f3892768921c..b3eb28552796 100644 --- a/services/usb/java/com/android/server/usb/UsbHostManager.java +++ b/services/usb/java/com/android/server/usb/UsbHostManager.java @@ -444,14 +444,19 @@ public class UsbHostManager { } 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."); + + // Use UsbDirectMidiDevice only if this supports MIDI 2.0 as well. + // ALSA removes the audio sound card if MIDI interfaces are removed. + // This means that as long as ALSA is used for audio, MIDI 1.0 USB + // devices should use the ALSA path for MIDI. + if (parser.containsLegacyMidiDeviceEndpoint()) { + midiDevice = UsbDirectMidiDevice.create(mContext, + newDevice, parser, false, uniqueUsbDeviceIdentifier); + if (midiDevice != null) { + midiDevices.add(midiDevice); + } else { + Slog.e(TAG, "Legacy Midi Device is null."); + } } } diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java index 3f2d8c8ef47c..c6ea22856dc7 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java @@ -79,6 +79,10 @@ public final class UsbConfigDescriptor extends UsbDescriptor { mInterfaceDescriptors.add(interfaceDesc); } + ArrayList<UsbInterfaceDescriptor> getInterfaceDescriptors() { + return mInterfaceDescriptors; + } + private boolean isAudioInterface(UsbInterfaceDescriptor descriptor) { return descriptor.getUsbClass() == UsbDescriptor.CLASSID_AUDIO && descriptor.getUsbSubclass() == UsbDescriptor.AUDIO_AUDIOSTREAMING; 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 f13fcd8d81a4..10b7952acab2 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java @@ -40,6 +40,7 @@ public final class UsbDescriptorParser { private UsbDeviceDescriptor mDeviceDescriptor; private UsbConfigDescriptor mCurConfigDescriptor; private UsbInterfaceDescriptor mCurInterfaceDescriptor; + private UsbEndpointDescriptor mCurEndpointDescriptor; // The AudioClass spec implemented by the AudioClass Interfaces // This may well be different than the overall USB Spec. @@ -165,7 +166,7 @@ public final class UsbDescriptorParser { break; case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT: - descriptor = new UsbEndpointDescriptor(length, type); + descriptor = mCurEndpointDescriptor = new UsbEndpointDescriptor(length, type); if (mCurInterfaceDescriptor != null) { mCurInterfaceDescriptor.addEndpointDescriptor( (UsbEndpointDescriptor) descriptor); @@ -265,6 +266,9 @@ public final class UsbDescriptorParser { + Integer.toHexString(subClass)); break; } + if (mCurEndpointDescriptor != null && descriptor != null) { + mCurEndpointDescriptor.setClassSpecificEndpointDescriptor(descriptor); + } } break; @@ -798,6 +802,84 @@ public final class UsbDescriptorParser { /** * @hide */ + private int calculateNumLegacyMidiPorts(boolean isOutput) { + // Only look at the first config. + UsbConfigDescriptor configDescriptor = null; + for (UsbDescriptor descriptor : mDescriptors) { + if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_CONFIG) { + if (descriptor instanceof UsbConfigDescriptor) { + configDescriptor = (UsbConfigDescriptor) descriptor; + break; + } else { + Log.w(TAG, "Unrecognized Config l: " + descriptor.getLength() + + " t:0x" + Integer.toHexString(descriptor.getType())); + } + } + } + if (configDescriptor == null) { + Log.w(TAG, "Config not found"); + return 0; + } + + ArrayList<UsbInterfaceDescriptor> legacyMidiInterfaceDescriptors = + new ArrayList<UsbInterfaceDescriptor>(); + for (UsbInterfaceDescriptor interfaceDescriptor + : configDescriptor.getInterfaceDescriptors()) { + if (interfaceDescriptor.getUsbClass() == UsbDescriptor.CLASSID_AUDIO) { + if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) { + UsbDescriptor midiHeaderDescriptor = + interfaceDescriptor.getMidiHeaderInterfaceDescriptor(); + if (midiHeaderDescriptor != null) { + if (midiHeaderDescriptor instanceof UsbMSMidiHeader) { + UsbMSMidiHeader midiHeader = + (UsbMSMidiHeader) midiHeaderDescriptor; + if (midiHeader.getMidiStreamingClass() == MS_MIDI_1_0) { + legacyMidiInterfaceDescriptors.add(interfaceDescriptor); + } + } + } + } + } + } + + int count = 0; + for (UsbInterfaceDescriptor interfaceDescriptor : legacyMidiInterfaceDescriptors) { + 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) { + UsbDescriptor classSpecificEndpointDescriptor = + endpoint.getClassSpecificEndpointDescriptor(); + if (classSpecificEndpointDescriptor != null + && (classSpecificEndpointDescriptor instanceof UsbACMidi10Endpoint)) { + UsbACMidi10Endpoint midiEndpoint = + (UsbACMidi10Endpoint) classSpecificEndpointDescriptor; + count += midiEndpoint.getNumJacks(); + } + } + } + } + return count; + } + + /** + * @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/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java index ab07ce7fdb7a..1f448acac5e8 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java @@ -79,6 +79,8 @@ public class UsbEndpointDescriptor extends UsbDescriptor { private byte mRefresh; private byte mSyncAddress; + private UsbDescriptor mClassSpecificEndpointDescriptor; + public UsbEndpointDescriptor(int length, byte type) { super(length, type); mHierarchyLevel = 4; @@ -112,6 +114,14 @@ public class UsbEndpointDescriptor extends UsbDescriptor { return mEndpointAddress & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION; } + void setClassSpecificEndpointDescriptor(UsbDescriptor descriptor) { + mClassSpecificEndpointDescriptor = descriptor; + } + + UsbDescriptor getClassSpecificEndpointDescriptor() { + return mClassSpecificEndpointDescriptor; + } + /** * Returns a UsbEndpoint that this UsbEndpointDescriptor is describing. */ |