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

Commit 82c57ac4 authored by Jiabin Huang's avatar Jiabin Huang Committed by Android (Google) Code Review
Browse files

Merge "Monitor alsa device files to notify USB devices connection."

parents 53ea2252 e2707bf8
Loading
Loading
Loading
Loading
+111 −0
Original line number Diff line number Diff line
@@ -23,7 +23,9 @@ import android.hardware.usb.UsbDevice;
import android.media.IAudioService;
import android.media.midi.MidiDeviceInfo;
import android.os.Bundle;
import android.os.FileObserver;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.service.usb.UsbAlsaManagerProto;
import android.util.Slog;
@@ -34,9 +36,11 @@ import com.android.server.usb.descriptors.UsbDescriptorParser;

import libcore.io.IoUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

/**
@@ -52,6 +56,11 @@ public final class UsbAlsaManager {

    private static final String ALSA_DIRECTORY = "/dev/snd/";

    private static final int ALSA_DEVICE_TYPE_UNKNOWN = 0;
    private static final int ALSA_DEVICE_TYPE_PLAYBACK = 1;
    private static final int ALSA_DEVICE_TYPE_CAPTURE = 2;
    private static final int ALSA_DEVICE_TYPE_MIDI = 3;

    private final Context mContext;
    private IAudioService mAudioService;
    private final boolean mHasMidiFeature;
@@ -116,6 +125,21 @@ public final class UsbAlsaManager {
    // UsbMidiDevice for USB peripheral mode (gadget) device
    private UsbMidiDevice mPeripheralMidiDevice = null;

    private final HashSet<Integer> mAlsaCards = new HashSet<>();
    private final FileObserver mAlsaObserver = new FileObserver(new File(ALSA_DIRECTORY),
            FileObserver.CREATE | FileObserver.DELETE) {
        public void onEvent(int event, String path) {
            switch (event) {
                case FileObserver.CREATE:
                    alsaFileAdded(path);
                    break;
                case FileObserver.DELETE:
                    alsaFileRemoved(path);
                    break;
            }
        }
    };

    /* package */ UsbAlsaManager(Context context) {
        mContext = context;
        mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
@@ -124,6 +148,7 @@ public final class UsbAlsaManager {
    public void systemReady() {
        mAudioService = IAudioService.Stub.asInterface(
                        ServiceManager.getService(Context.AUDIO_SERVICE));
        mAlsaObserver.startWatching();
    }

    /**
@@ -224,6 +249,8 @@ public final class UsbAlsaManager {
            return;
        }

        waitForAlsaDevice(cardRec.getCardNum(), true /*isAdded*/);

        // Add it to the devices list
        boolean hasInput = parser.hasInput()
                && !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(),
@@ -322,6 +349,7 @@ public final class UsbAlsaManager {
        UsbAlsaDevice alsaDevice = removeAlsaDeviceFromList(deviceAddress);
        Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice);
        if (alsaDevice != null && alsaDevice == mSelectedDevice) {
            waitForAlsaDevice(alsaDevice.getCardNum(), false /*isAdded*/);
            deselectAlsaDevice();
            selectDefaultDevice(); // if there any external devices left, select one of them
        }
@@ -361,6 +389,89 @@ public final class UsbAlsaManager {
        }
   }

    private boolean waitForAlsaDevice(int card, boolean isAdded) {
        if (DEBUG) {
            Slog.e(TAG, "waitForAlsaDevice(c:" + card + ")");
        }

        // This value was empirically determined.
        final int kWaitTimeMs = 2500;

        synchronized (mAlsaCards) {
            long timeoutMs = SystemClock.elapsedRealtime() + kWaitTimeMs;
            while ((isAdded ^ mAlsaCards.contains(card))
                    && timeoutMs > SystemClock.elapsedRealtime()) {
                long waitTimeMs = timeoutMs - SystemClock.elapsedRealtime();
                if (waitTimeMs > 0) {
                    try {
                        mAlsaCards.wait(waitTimeMs);
                    } catch (InterruptedException e) {
                        Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file.");
                    }
                }
            }
            final boolean cardFound = mAlsaCards.contains(card);
            if ((isAdded ^ cardFound) && timeoutMs > SystemClock.elapsedRealtime()) {
                Slog.e(TAG, "waitForAlsaDevice(" + card + ") timeout");
            } else {
                Slog.i(TAG, "waitForAlsaDevice for device card=" + card + ", isAdded=" + isAdded
                        + ", found=" + cardFound);
            }
            return cardFound;
        }
    }

    private int getCardNumberFromAlsaFilePath(String path) {
        int type = ALSA_DEVICE_TYPE_UNKNOWN;
        if (path.startsWith("pcmC")) {
            if (path.endsWith("p")) {
                type = ALSA_DEVICE_TYPE_PLAYBACK;
            } else if (path.endsWith("c")) {
                type = ALSA_DEVICE_TYPE_CAPTURE;
            }
        } else if (path.startsWith("midiC")) {
            type = ALSA_DEVICE_TYPE_MIDI;
        }

        if (type == ALSA_DEVICE_TYPE_UNKNOWN) {
            Slog.i(TAG, "Unknown type file(" + path + ") added.");
            return -1;
        }
        try {
            int c_index = path.indexOf('C');
            int d_index = path.indexOf('D');
            return Integer.parseInt(path.substring(c_index + 1, d_index));
        } catch (Exception e) {
            Slog.e(TAG, "Could not parse ALSA file name " + path, e);
            return -1;
        }
    }

    private void alsaFileAdded(String path) {
        Slog.i(TAG, "alsaFileAdded(" + path + ")");
        final int card = getCardNumberFromAlsaFilePath(path);
        if (card == -1) {
            return;
        }
        synchronized (mAlsaCards) {
            if (!mAlsaCards.contains(card)) {
                Slog.d(TAG, "Adding ALSA device card=" + card);
                mAlsaCards.add(card);
                mAlsaCards.notifyAll();
            }
        }
    }

    private void alsaFileRemoved(String path) {
        final int card = getCardNumberFromAlsaFilePath(path);
        if (card == -1) {
            return;
        }
        synchronized (mAlsaCards) {
            mAlsaCards.remove(card);
        }
    }

    //
    // Devices List
    //