Audio deamon: Add support of audio device events

Audio daemon will catch the events regarding the audio device changes and
transfer them to audio HAL.

Change-Id: Ida6b26142ece472a6e127904a43890ee44632d75
diff --git a/audiod/AudioDaemon.cpp b/audiod/AudioDaemon.cpp
index 2bfe391..b4857c4 100644
--- a/audiod/AudioDaemon.cpp
+++ b/audiod/AudioDaemon.cpp
@@ -30,6 +30,7 @@
 #define LOG_NDEBUG 0
 #define LOG_NDDEBUG 0
 
+#include <dirent.h>
 #include <media/AudioSystem.h>
 #include <sys/poll.h>
 
@@ -117,6 +118,89 @@
         sndcardFdPair.clear();
     }
 
+    bool AudioDaemon::getDeviceEventFDs()
+    {
+        const char* events_dir = "/sys/class/switch/";
+        DIR *dp;
+        struct dirent* in_file;
+        int fd;
+        String8 path;
+
+        if ((dp = opendir(events_dir)) == NULL) {
+            ALOGE("Cannot open switch directory to get list of audio events %s", events_dir);
+            return false;
+        }
+
+        mAudioEvents.clear();
+        mAudioEventsStatus.clear();
+
+        while ((in_file = readdir(dp)) != NULL) {
+
+            if (!strstr(in_file->d_name, "qc_"))
+                continue;
+            ALOGD(" Found event file = %s", in_file->d_name);
+            path = "/sys/class/switch/";
+            path += in_file->d_name;
+            path += "/state";
+
+            ALOGE("Opening audio event state : %s ", path.string());
+            fd = open(path.string(), O_RDONLY);
+            if (fd == -1) {
+                ALOGE("Open %s failed : %s", path.string(), strerror(errno));
+            } else {
+                mAudioEvents.push_back(std::make_pair(in_file->d_name, fd));
+                mAudioEventsStatus.push_back(std::make_pair(in_file->d_name, 0));
+                ALOGD("event status mAudioEventsStatus= %s",
+                          mAudioEventsStatus[0].first.string());
+            }
+        }
+
+        ALOGV("%s: %d audio device event detected",
+                  __func__,
+                  mAudioEvents.size());
+
+        closedir(dp);
+        return mAudioEvents.size() > 0 ? true : false;
+
+    }
+
+    void  AudioDaemon::putDeviceEventFDs()
+    {
+        unsigned int i;
+        for (i = 0; i < mAudioEvents.size(); i++) {
+            close(mAudioEvents[i].second);
+            delete(mAudioEvents[i].first);
+        }
+        mAudioEvents.clear();
+        mAudioEventsStatus.clear();
+    }
+
+    void AudioDaemon::checkEventState(int fd, int index)
+    {
+        char state_buf[2];
+        audio_event_status event_cur_state = audio_event_off;
+
+        if (!read(fd, (void *)state_buf, 1)) {
+            ALOGE("Error receiving device state event (%s)", strerror(errno));
+        } else {
+             state_buf[1] = '\0';
+            if (atoi(state_buf) != mAudioEventsStatus[index].second) {
+                ALOGD("notify audio HAL %s",
+                        mAudioEvents[index].first.string());
+                mAudioEventsStatus[index].second = atoi(state_buf);
+
+                if (mAudioEventsStatus[index].second == 1)
+                    event_cur_state = audio_event_on;
+                else
+                    event_cur_state = audio_event_off;
+                notifyAudioSystemEventStatus(
+                               mAudioEventsStatus[index].first.string(),
+                               event_cur_state);
+            }
+        }
+        lseek(fd, 0, SEEK_SET);
+    }
+
     status_t AudioDaemon::readyToRun() {
 
         ALOGV("readyToRun: open snd card state node files");
@@ -151,6 +235,10 @@
             }
         }
 
+        if (!getDeviceEventFDs()) {
+            ALOGE("No audio device events detected");
+        }
+
         if (audioInitDone == false) {
             ALOGE("Sound Card is empty!!!");
             goto thread_exit;
@@ -177,13 +265,20 @@
         }
         ALOGD("number of sndcards %d CPEs %d", i, cpe_cnt - CPE_MAGIC_NUM);
 
-        pfd = new pollfd[mSndCardFd.size()];
-        bzero(pfd, sizeof(*pfd) * mSndCardFd.size());
+        pfd = new pollfd[mSndCardFd.size() + mAudioEvents.size()];
+        bzero(pfd, (sizeof(*pfd) * mSndCardFd.size() +
+                    sizeof(*pfd) * mAudioEvents.size()));
         for (i = 0; i < mSndCardFd.size(); i++) {
             pfd[i].fd = mSndCardFd[i].second;
             pfd[i].events = POLLPRI;
         }
 
+        /*insert all audio events*/
+        for(i = 0; i < mAudioEvents.size(); i++) {
+            pfd[i+mSndCardFd.size()].fd = mAudioEvents[i].second;
+            pfd[i+mSndCardFd.size()].events = POLLPRI;
+        }
+
         ALOGD("read for sound card state change before while");
         for (i = 0; i < mSndCardFd.size(); i++) {
             if (!read(pfd[i].fd, (void *)rd_buf, 8)) {
@@ -233,9 +328,14 @@
             }
         }
 
+       ALOGE("read for event state change before while");
+       for (i = 0; i < mAudioEvents.size(); i++){
+           checkEventState(pfd[i+mSndCardFd.size()].fd, i);
+       }
+
         while (1) {
            ALOGD("poll() for sound card state change ");
-           if (poll(pfd, mSndCardFd.size(), -1) < 0) {
+           if (poll(pfd, (mSndCardFd.size() + mAudioEvents.size()), -1) < 0) {
               ALOGE("poll() failed (%s)", strerror(errno));
               ret = false;
               break;
@@ -290,9 +390,19 @@
                    }
                }
            }
+           for (i = 0; i < mAudioEvents.size(); i++) {
+               if (pfd[i + mSndCardFd.size()].revents & POLLPRI) {
+                   ALOGE("EVENT recieved pfd[i].revents= 0x%x %d",
+                       pfd[i + mSndCardFd.size()].revents,
+                       mAudioEvents[i].second);
+
+                   checkEventState(pfd[i + mSndCardFd.size()].fd, i);
+               }
+           }
        }
 
        putStateFDs(mSndCardFd);
+       putDeviceEventFDs();
        delete [] pfd;
 
     thread_exit:
@@ -329,4 +439,20 @@
         ALOGV("%s: notifyAudioSystem : %s", __func__, str.string());
         AudioSystem::setParameters(0, str);
     }
+
+    void AudioDaemon::notifyAudioSystemEventStatus(const char* event,
+                                            audio_event_status status) {
+
+        String8 str;
+        str += AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE;
+        str += "=";
+        str += event;
+
+        if (status == audio_event_on)
+            str += ",ON";
+        else
+            str += ",OFF";
+        ALOGD("%s: notifyAudioSystemEventStatus : %s", __func__, str.string());
+        AudioSystem::setParameters(0, str);
+    }
 }
diff --git a/audiod/AudioDaemon.h b/audiod/AudioDaemon.h
index 23d78dd..3359d72 100644
--- a/audiod/AudioDaemon.h
+++ b/audiod/AudioDaemon.h
@@ -52,6 +52,10 @@
     CPE_STATE
 };
 
+enum audio_event_status {audio_event_on, audio_event_off};
+
+#define AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE "ext_audio_device"
+
 class AudioDaemon:public Thread, public IBinder :: DeathRecipient
 {
     /*Overrides*/
@@ -64,9 +68,13 @@
     void notifyAudioSystem(int snd_card,
                            notify_status status,
                            notify_status_type type);
+    void notifyAudioSystemEventStatus(const char* event, audio_event_status status);
     int mUeventSock;
     bool getStateFDs(std::vector<std::pair<int,int> > &sndcardFdPair);
     void putStateFDs(std::vector<std::pair<int,int> > &sndcardFdPair);
+    bool getDeviceEventFDs();
+    void putDeviceEventFDs();
+    void checkEventState(int fd, int index);
 
 public:
     AudioDaemon();
@@ -74,6 +82,11 @@
 
 private:
     std::vector<std::pair<int,int> > mSndCardFd;
+
+    //file descriptors for audio device events and their statuses
+    std::vector<std::pair<String8, int> > mAudioEvents;
+    std::vector<std::pair<String8, int> > mAudioEventsStatus;
+
 };
 
 }