MidiManager: Add USB peripheral MIDI support

This allows the android device to appear as a USB MIDI device to
a USB host (like a Mac or a PC) using the f_midi USB gadget driver.

Change-Id: I14f1ba73bcce2c894e77efb8810beac4ffe246d8
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index f64ef87..f283051 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -68,6 +68,8 @@
      * accessory function is enabled
      * <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the
      * audio source function is enabled
+     * <li> {@link #USB_FUNCTION_MIDI} boolean extra indicating whether the
+     * MIDI function is enabled
      * </ul>
      *
      * {@hide}
@@ -188,6 +190,14 @@
     public static final String USB_FUNCTION_AUDIO_SOURCE = "audio_source";
 
     /**
+     * Name of the MIDI USB function.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+     *
+     * {@hide}
+     */
+    public static final String USB_FUNCTION_MIDI = "midi";
+
+    /**
      * Name of the Accessory USB function.
      * Used in extras for the {@link #ACTION_USB_STATE} broadcast
      *
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f51e82c9..efdc3c8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5188,4 +5188,9 @@
     <string name="stk_cc_ss_to_ussd">SS request is modified to USSD request.</string>
     <string name="stk_cc_ss_to_ss">SS request is modified to new SS request.</string>
 
+    <!-- Manufacturer name for USB MIDI Peripheral port -->
+    <string name="usb_midi_peripheral_manufacturer_name">Android</string>
+    <!-- Model name for USB MIDI Peripheral port -->
+    <string name="usb_midi_peripheral_model_name">USB Peripheral Port</string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b04349e..607744f 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2156,4 +2156,7 @@
   <java-symbol type="bool" name="config_use_sim_language_file" />
   <java-symbol type="bool" name="config_LTE_eri_for_network_name" />
   <java-symbol type="bool" name="config_defaultInTouchMode" />
+
+  <java-symbol type="string" name="usb_midi_peripheral_manufacturer_name" />
+  <java-symbol type="string" name="usb_midi_peripheral_model_name" />
 </resources>
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 01a044e..ce3b6e3 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -20,12 +20,15 @@
 import android.alsa.AlsaDevicesParser;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.hardware.usb.UsbConstants;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbInterface;
 import android.media.AudioSystem;
 import android.media.IAudioService;
+import android.midi.MidiDeviceInfo;
 import android.os.FileObserver;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -73,6 +76,9 @@
 
     private UsbAudioDevice mSelectedAudioDevice = null;
 
+    // UsbMidiDevice for USB peripheral mode (gadget) device
+    private UsbMidiDevice mPeripheralMidiDevice = null;
+
     private final class AlsaDevice {
         public static final int TYPE_UNKNOWN = 0;
         public static final int TYPE_PLAYBACK = 1;
@@ -391,7 +397,17 @@
                 int device = mDevicesParser.getDefaultDeviceNum(addedCard);
                 AlsaDevice alsaDevice = waitForAlsaDevice(addedCard, device, AlsaDevice.TYPE_MIDI);
                 if (alsaDevice != null) {
-                    UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, usbDevice,
+                    Bundle properties = new Bundle();
+                    properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER,
+                            usbDevice.getManufacturerName());
+                    properties.putString(MidiDeviceInfo.PROPERTY_MODEL, usbDevice.getProductName());
+                    properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
+                            usbDevice.getSerialNumber());
+                    properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, alsaDevice.mCard);
+                    properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, alsaDevice.mDevice);
+                    properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
+
+                    UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
                             alsaDevice.mCard, alsaDevice.mDevice);
                     if (usbMidiDevice != null) {
                         mMidiDevices.put(usbDevice, usbMidiDevice);
@@ -437,6 +453,23 @@
         }
     }
 
+   /* package */ void setPeripheralMidiState(boolean enabled, int card, int device) {
+        if (enabled) {
+            Bundle properties = new Bundle();
+            Resources r = mContext.getResources();
+            properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, r.getString(
+                    com.android.internal.R.string.usb_midi_peripheral_manufacturer_name));
+            properties.putString(MidiDeviceInfo.PROPERTY_MODEL, r.getString(
+                    com.android.internal.R.string.usb_midi_peripheral_model_name));
+            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card);
+            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device);
+            mPeripheralMidiDevice = UsbMidiDevice.create(mContext, properties, card, device);
+        } else if (mPeripheralMidiDevice != null) {
+            IoUtils.closeQuietly(mPeripheralMidiDevice);
+            mPeripheralMidiDevice = null;
+        }
+   }
+
     //
     // Devices List
     //
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 1426551..2fb6dbf 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -31,6 +31,7 @@
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbManager;
 import android.media.AudioManager;
+import android.midi.MidiDeviceInfo;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
@@ -85,6 +86,8 @@
             "/sys/class/android_usb/android0/f_rndis/ethaddr";
     private static final String AUDIO_SOURCE_PCM_PATH =
             "/sys/class/android_usb/android0/f_audio_source/pcm";
+    private static final String MIDI_ALSA_PATH =
+            "/sys/class/android_usb/android0/f_midi/alsa";
 
     private static final int MSG_UPDATE_STATE = 0;
     private static final int MSG_ENABLE_ADB = 1;
@@ -124,6 +127,7 @@
     private boolean mUseUsbNotification;
     private boolean mAdbEnabled;
     private boolean mAudioSourceEnabled;
+    private boolean mMidiEnabled;
     private Map<String, List<Pair<String, String>>> mOemModeMap;
     private String[] mAccessoryStrings;
     private UsbDebuggingManager mDebuggingManager;
@@ -618,6 +622,31 @@
             }
         }
 
+        private void updateMidiFunction() {
+            boolean enabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MIDI);
+            if (enabled != mMidiEnabled) {
+                int card = -1;
+                int device = -1;
+
+                if (enabled) {
+                    Scanner scanner = null;
+                    try {
+                        scanner = new Scanner(new File(MIDI_ALSA_PATH));
+                        card = scanner.nextInt();
+                        device = scanner.nextInt();
+                    } catch (FileNotFoundException e) {
+                        Slog.e(TAG, "could not open MIDI PCM file", e);
+                    } finally {
+                        if (scanner != null) {
+                            scanner.close();
+                        }
+                    }
+                }
+                mUsbAlsaManager.setPeripheralMidiState(enabled, card, device);
+                mMidiEnabled = enabled;
+            }
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -636,6 +665,7 @@
                     if (mBootCompleted) {
                         updateUsbState();
                         updateAudioSourceFunction();
+                        updateMidiFunction();
                     }
                     break;
                 case MSG_ENABLE_ADB:
@@ -651,6 +681,7 @@
                     updateAdbNotification();
                     updateUsbState();
                     updateAudioSourceFunction();
+                    updateMidiFunction();
                     break;
                 case MSG_BOOT_COMPLETED:
                     mBootCompleted = true;
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index 0ca52f6..8d44905 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -17,7 +17,6 @@
 package com.android.server.usb;
 
 import android.content.Context;
-import android.hardware.usb.UsbDevice;
 import android.midi.MidiDeviceInfo;
 import android.midi.MidiDeviceServer;
 import android.midi.MidiManager;
@@ -50,7 +49,7 @@
     // streams for writing to ALSA driver
     private final FileOutputStream[] mOutputStreams;
 
-    public static UsbMidiDevice create(Context context, UsbDevice usbDevice, int card, int device) {
+    public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) {
         MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
         if (midiManager == null) {
             Log.e(TAG, "No MidiManager in UsbMidiDevice.create()");
@@ -71,13 +70,6 @@
             return null;
         }
 
-        Bundle properties = new Bundle();
-        properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, usbDevice.getManufacturerName());
-        properties.putString(MidiDeviceInfo.PROPERTY_MODEL, usbDevice.getProductName());
-        properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER, usbDevice.getSerialNumber());
-        properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card);
-        properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device);
-        properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
         MidiDeviceServer server = midiManager.createDeviceServer(subDevices, subDevices, properties,
                 false, MidiDeviceInfo.TYPE_USB);
         if (server == null) {