Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b755253f authored by Paul McLean's avatar Paul McLean Committed by Android Git Automerger
Browse files

am fd7d3108: Merge "Implement USB Audio across Nexus Devices Fix issues with...

am fd7d3108: Merge "Implement USB Audio across Nexus Devices Fix issues with connecting non-audio USB devices."

* commit 'fd7d3108':
  Implement USB Audio across Nexus Devices Fix issues with connecting non-audio USB devices.
parents 1f396ec3 fd7d3108
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
@@ -205,14 +205,49 @@ public class AlsaDevicesParser {
        return mHasPlaybackDevices;
    }

    public boolean hasPlaybackDevices(int card) {
        for (int index = 0; index < deviceRecords_.size(); index++) {
            AlsaDeviceRecord deviceRecord = deviceRecords_.get(index);
            if (deviceRecord.mCardNum == card &&
                deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio &&
                deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Playback) {
                return true;
            }
        }
        return false;
    }

    public boolean hasCaptureDevices() {
        return mHasCaptureDevices;
    }

    public boolean hasCaptureDevices(int card) {
        for (int index = 0; index < deviceRecords_.size(); index++) {
            AlsaDeviceRecord deviceRecord = deviceRecords_.get(index);
            if (deviceRecord.mCardNum == card &&
                deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio &&
                deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Capture) {
                return true;
            }
        }
        return false;
    }

    public boolean hasMIDIDevices() {
        return mHasMIDIDevices;
    }

    public boolean hasMIDIDevices(int card) {
        for (int index = 0; index < deviceRecords_.size(); index++) {
            AlsaDeviceRecord deviceRecord = deviceRecords_.get(index);
            if (deviceRecord.mCardNum == card &&
                deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_MIDI) {
                return true;
            }
        }
        return false;
    }

    public void scan() {
        deviceRecords_.clear();

+22 −14
Original line number Diff line number Diff line
@@ -4083,8 +4083,19 @@ public class AudioService extends IAudioService.Stub {
                        }
                    }
                }
            } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ||
                           action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
            } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG)) {
                state = intent.getIntExtra("state", 0);

                int alsaCard = intent.getIntExtra("card", -1);
                int alsaDevice = intent.getIntExtra("device", -1);

                String params = (alsaCard == -1 && alsaDevice == -1 ? ""
                                    : "card=" + alsaCard + ";device=" + alsaDevice);

                // Playback Device
                device = AudioSystem.DEVICE_OUT_USB_ACCESSORY;
                setWiredDeviceConnectionState(device, state, params);
            } else if (action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
                state = intent.getIntExtra("state", 0);

                int alsaCard = intent.getIntExtra("card", -1);
@@ -4096,21 +4107,18 @@ public class AudioService extends IAudioService.Stub {
                String params = (alsaCard == -1 && alsaDevice == -1 ? ""
                                    : "card=" + alsaCard + ";device=" + alsaDevice);

                //TODO(pmclean) - Ignore for now the hasPlayback & hasCapture flags since we
                // still need to call setWiredDeviceConnectionState() on disconnect (when we
                // don't have card/device files to parse for this info). We will need to store
                // that info here when we get smarter about multiple USB card/devices.
                // Playback Device
                device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
                        AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE;
                if (hasPlayback) {
                    device = AudioSystem.DEVICE_OUT_USB_DEVICE;
                    setWiredDeviceConnectionState(device, state, params);
                }

                // Capture Device
                device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
                    AudioSystem.DEVICE_IN_USB_ACCESSORY : AudioSystem.DEVICE_IN_USB_DEVICE;
                if (hasCapture) {
                    device = AudioSystem.DEVICE_IN_USB_DEVICE;
                    setWiredDeviceConnectionState(device, state, params);
                }
            else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
                boolean broadcast = false;
                int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
                synchronized (mScoClients) {
+128 −69
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import android.os.Parcelable;
import android.os.UserHandle;
import android.util.Slog;

import com.android.alsascan.AlsaCardsParser;
import com.android.alsascan.AlsaDevicesParser;
import com.android.internal.annotations.GuardedBy;

import java.io.File;
@@ -37,7 +39,6 @@ import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;

/**
 * UsbHostManager manages USB state in host mode.
@@ -62,6 +63,15 @@ public class UsbHostManager {
    private ArrayList<UsbInterface> mNewInterfaces;
    private ArrayList<UsbEndpoint> mNewEndpoints;

    // Attributes of any connected USB audio device.
    //TODO(pmclean) When we extend to multiple, USB Audio devices, we will need to get
    // more clever about this.
    private int mConnectedUsbCard = -1;
    private int mConnectedUsbDeviceNum = -1;
    private boolean mConnectedHasPlayback = false;
    private boolean mConnectedHasCapture = false;
    private boolean mConnectedHasMIDI = false;

    @GuardedBy("mLock")
    private UsbSettingsManager mCurrentSettings;

@@ -126,9 +136,27 @@ public class UsbHostManager {
        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    }

    static boolean isBuiltInUsbDevice(String deviceName) {
      // This may be too broad an assumption
      return deviceName.equals("/dev/bus/usb/001/001");
    private boolean waitForAlsaFile(int card, int device, boolean capture) {
        // These values were empirically determined.
        final int kNumRetries = 5;
        final int kSleepTime = 500; // ms
        String alsaDevPath = "/dev/snd/pcmC" + card + "D" + device + (capture ? "c" : "p");
        File alsaDevFile = new File(alsaDevPath);
        boolean exists = false;
        for (int retry = 0; !exists && retry < kNumRetries; retry++) {
            exists = alsaDevFile.exists();
            if (!exists) {
                try {
                    Thread.sleep(kSleepTime);
                } catch (IllegalThreadStateException ex) {
                    Slog.d(TAG, "usb: IllegalThreadStateException while waiting for ALSA file.");
                } catch (java.lang.InterruptedException ex) {
                    Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file.");
                }
            }
        }

        return exists;
    }

    /* Called from JNI in monitorUsbHostBus() to report new USB devices
@@ -151,47 +179,13 @@ public class UsbHostManager {
            // midi streaming: 0x03

            // some useful debugging info
          Slog.d(TAG, "usb:UsbHostManager.usbDeviceAdded()");
          Slog.d(TAG, "usb: nm:" + deviceName +
              " vnd:" + vendorID +
              " prd:" + productID +
              " cls:" + deviceClass +
              " sub:" + deviceSubclass +
              " proto:" + deviceProtocol);
      }

      if (!isBuiltInUsbDevice(deviceName)) {
          //TODO(pmclean) we will need this when we need to support USB interfaces
          // beyond card1, device0 but turn them off for now
          //com.android.alsascan.AlsaCardsParser cardsParser =
          //    new com.android.alsascan.AlsaCardsParser();
          //cardsParser.scan();
          //cardsParser.Log();

          // But we need to parse the device to determine its capabilities.
          com.android.alsascan.AlsaDevicesParser devicesParser =
              new com.android.alsascan.AlsaDevicesParser();
          devicesParser.scan();
          //devicesParser.Log();

          boolean hasPlaybackDevices = devicesParser.hasPlaybackDevices();
          boolean hasCaptureDevices = devicesParser.hasCaptureDevices();
          boolean hasMIDI = devicesParser.hasMIDIDevices();

          if (DEBUG_AUDIO) {
              Slog.d(TAG, "usb: hasPlayback:" + hasPlaybackDevices
                      + " hasCapture:" + hasCaptureDevices);
            Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:"
                    + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol);
        }

          //TODO(pmclean)
          // For now just assume that any USB device that is attached is:
          // 1. An audio interface and
          // 2. is card:1 device:0
          int cardNum = 1;
          int deviceNum = 0;
          sendDeviceNotification(cardNum, deviceNum, true,
                                 hasPlaybackDevices, hasCaptureDevices, hasMIDI);
        }
        // OK this is non-obvious, but true. One can't tell if the device being attached is even
        // potentially an audio device without parsing the interface descriptors, so punt on any
        // such test until endUsbDeviceAdded() when we have that info.

        if (isBlackListed(deviceName) ||
                isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
@@ -217,6 +211,7 @@ public class UsbHostManager {
            mNewInterfaces = new ArrayList<UsbInterface>();
            mNewEndpoints = new ArrayList<UsbEndpoint>();
        }

        return true;
    }

@@ -270,6 +265,17 @@ public class UsbHostManager {
                    mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
        }

        // Is there an audio interface in there?
        final int kUsbClassId_Audio = 0x01;
        boolean isAudioDevice = false;
        for (int ntrfaceIndex = 0; !isAudioDevice && ntrfaceIndex < mNewInterfaces.size();
                ntrfaceIndex++) {
            UsbInterface ntrface = mNewInterfaces.get(ntrfaceIndex);
            if (ntrface.getInterfaceClass() == kUsbClassId_Audio) {
                isAudioDevice = true;
            }
        }

        synchronized (mLock) {
            if (mNewDevice != null) {
                mNewDevice.setConfigurations(
@@ -285,6 +291,48 @@ public class UsbHostManager {
            mNewInterfaces = null;
            mNewEndpoints = null;
        }

        if (!isAudioDevice) {
            return; // bail
        }

        //TODO(pmclean) The "Parser" objects inspect files in "/proc/asound" which we presume is
        // present, unlike the waitForAlsaFile() which waits on a file in /dev/snd. It is not
        // clear why this works, or that it can be relied on going forward.  Needs further
        // research.
        AlsaCardsParser cardsParser = new AlsaCardsParser();
        cardsParser.scan();
        // cardsParser.Log();

        // But we need to parse the device to determine its capabilities.
        AlsaDevicesParser devicesParser = new AlsaDevicesParser();
        devicesParser.scan();
        // devicesParser.Log();

        // The protocol for now will be to select the last-connected (highest-numbered)
        // Alsa Card.
        mConnectedUsbCard = cardsParser.getNumCardRecords() - 1;
        mConnectedUsbDeviceNum = 0;

        if (!waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, false)) {
            return;
        }

        mConnectedHasPlayback = devicesParser.hasPlaybackDevices(mConnectedUsbCard);
        mConnectedHasCapture = devicesParser.hasCaptureDevices(mConnectedUsbCard);
        mConnectedHasMIDI = devicesParser.hasMIDIDevices(mConnectedUsbCard);

        if (DEBUG_AUDIO) {
            Slog.d(TAG,
                    "usb: hasPlayback:" + mConnectedHasPlayback + " hasCapture:" + mConnectedHasCapture);
        }

        sendDeviceNotification(mConnectedUsbCard,
                mConnectedUsbDeviceNum,
                true,
                mConnectedHasPlayback,
                mConnectedHasCapture,
                mConnectedHasMIDI);
    }

    /* Called from JNI in monitorUsbHostBus to report USB device removal */
@@ -293,8 +341,19 @@ public class UsbHostManager {
          Slog.d(TAG, "usb:UsbHostManager.usbDeviceRemoved() nm:" + deviceName);
        }

        // Same assumptions as the fake-out above
        sendDeviceNotification(1, 0, false, /*NA*/false, /*NA*/false, /*NA*/false);
        if (mConnectedUsbCard != -1 && mConnectedUsbDeviceNum != -1) {
            sendDeviceNotification(mConnectedUsbCard,
                    mConnectedUsbDeviceNum,
                    false,
                    mConnectedHasPlayback,
                    mConnectedHasCapture,
                    mConnectedHasMIDI);
            mConnectedUsbCard = -1;
            mConnectedUsbDeviceNum = -1;
            mConnectedHasPlayback = false;
            mConnectedHasCapture = false;
            mConnectedHasMIDI = false;
        }

        synchronized (mLock) {
            UsbDevice device = mDevices.remove(deviceName);