Merge "hal: Enable support for Listen voice wakeup feature"
diff --git a/hal/Android.mk b/hal/Android.mk
index 6a8da10..44e6862 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -84,6 +84,12 @@
 	$(LOCAL_PATH)/$(AUDIO_PLATFORM) \
 	$(LOCAL_PATH)/audio_extn
 
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_LISTEN)),true)
+    LOCAL_CFLAGS += -DAUDIO_LISTEN_ENABLED
+    LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/audio-listen
+    LOCAL_SRC_FILES += audio_extn/listen.c
+endif
+
 LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM)
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 2389f00..98765c4 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -196,6 +196,7 @@
    audio_extn_set_anc_parameters(parms);
    audio_extn_set_afe_proxy_parameters(parms);
    audio_extn_fm_set_parameters(adev, parms);
+   audio_extn_listen_set_parameters(adev, parms);
 }
 
 void audio_extn_get_parameters(const struct audio_device *adev,
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 3676c08..71b4863 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -93,4 +93,28 @@
                              char *device_name);
 #endif
 
+#ifndef AUDIO_LISTEN_ENABLED
+
+#define audio_extn_listen_init(adev)                            (0)
+#define audio_extn_listen_deinit(adev)                          (0)
+#define audio_extn_listen_update_status(uc_info, event)         (0)
+#define audio_extn_listen_set_parameters(adev, parms)           (0)
+
+#else
+
+enum listen_event_type {
+    LISTEN_EVENT_AUDIO_CAPTURE_INACTIVE,
+    LISTEN_EVENT_AUDIO_CAPTURE_ACTIVE,
+};
+typedef enum listen_event_type listen_event_type_t;
+
+int audio_extn_listen_init(struct audio_device *adev);
+void audio_extn_listen_deinit(struct audio_device *adev);
+void audio_extn_listen_update_status(struct audio_usecase *uc_info,
+        listen_event_type_t event);
+void audio_extn_listen_set_parameters(struct audio_device *adev,
+                                        struct str_parms *parms);
+
+#endif /* AUDIO_LISTEN_ENABLED */
+
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/listen.c b/hal/audio_extn/listen.c
new file mode 100644
index 0000000..14a427b
--- /dev/null
+++ b/hal/audio_extn/listen.c
@@ -0,0 +1,185 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#define LOG_TAG "listen_hal_loader"
+#define LOG_NDEBUG 0
+/* #define LOG_NDDEBUG 0 */
+#include <stdbool.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <cutils/log.h>
+#ifdef AUDIO_LISTEN_ENABLED
+#include <listen_types.h>
+#endif
+#include "audio_hw.h"
+#include "audio_extn.h"
+#include "platform.h"
+#include "platform_api.h"
+
+
+#ifdef AUDIO_LISTEN_ENABLED
+
+#define LIB_LISTEN_LOADER "/vendor/lib/liblistenhardware.so"
+
+#define LISTEN_LOAD_SYMBOLS(dev, func_p, func_type, symbol) \
+{\
+    dev->func_p = (func_type)dlsym(dev->lib_handle,#symbol);\
+    if (dev->func_p == NULL) {\
+            ALOGE("%s: dlsym error %s for %s",\
+                    __func__, dlerror(), #symbol);\
+            free(dev);\
+            dev = NULL;\
+            return -EINVAL;\
+    }\
+}
+
+
+typedef int (*create_listen_hw_t)();
+typedef void (*destroy_listen_hw_t)();
+
+typedef int (*open_listen_session_t)(struct audio_hw_device *,
+                                    struct listen_session**);
+
+typedef int (*close_listen_session_t)(struct audio_hw_device *dev,
+                                    struct listen_session* handle);
+
+typedef int (*set_mad_observer_t)(struct audio_hw_device *dev,
+                                      listen_callback_t cb_func);
+
+typedef int (*listen_set_parameters_t)(struct audio_hw_device *dev,
+                                const char *kv_pairs);
+typedef char* (*get_parameters_t)(const struct audio_hw_device *dev,
+                                    const char *keys);
+typedef void (*listen_notify_event_t)(event_type_t event_type);
+
+struct listen_audio_device {
+    void *lib_handle;
+    struct audio_device *adev;
+
+    create_listen_hw_t create_listen_hw;
+    destroy_listen_hw_t destroy_listen_hw;
+    open_listen_session_t open_listen_session;
+    close_listen_session_t close_listen_session;
+    set_mad_observer_t set_mad_observer;
+    listen_set_parameters_t listen_set_parameters;
+    get_parameters_t get_parameters;
+    listen_notify_event_t notify_event;
+};
+
+static struct listen_audio_device *listen_dev;
+
+void audio_extn_listen_update_status(struct audio_usecase *uc_info,
+                                    listen_event_type_t event)
+{
+    if (listen_dev) {
+        ALOGI("%s(): current active device =  %s. Event = %u", __func__,
+                platform_get_snd_device_name(uc_info->in_snd_device),
+                event);
+        if (uc_info->in_snd_device != SND_DEVICE_IN_CAPTURE_FM)
+            listen_dev->notify_event(event);
+    }
+}
+
+void audio_extn_listen_set_parameters(struct audio_device *adev,
+                               struct str_parms *parms)
+{
+    return;
+}
+
+int audio_extn_listen_init(struct audio_device *adev)
+{
+    int ret;
+    void *lib_handle;
+
+    ALOGI("%s: Enter", __func__);
+
+    lib_handle = dlopen(LIB_LISTEN_LOADER, RTLD_NOW);
+
+    if (lib_handle == NULL) {
+        ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, LIB_LISTEN_LOADER,
+                dlerror());
+        return -EINVAL;
+    } else {
+        ALOGI("%s: DLOPEN successful for %s", __func__, LIB_LISTEN_LOADER);
+
+        listen_dev = (struct listen_audio_device*)
+            calloc(1, sizeof(struct listen_audio_device));
+
+        listen_dev->lib_handle = lib_handle;
+        listen_dev->adev = adev;
+
+        LISTEN_LOAD_SYMBOLS(listen_dev, create_listen_hw,
+                create_listen_hw_t, create_listen_hw);
+
+        LISTEN_LOAD_SYMBOLS(listen_dev, destroy_listen_hw,
+                destroy_listen_hw_t, destroy_listen_hw);
+
+        LISTEN_LOAD_SYMBOLS(listen_dev, open_listen_session,
+                open_listen_session_t, open_listen_session);
+
+        adev->device.open_listen_session = listen_dev->open_listen_session;
+
+        LISTEN_LOAD_SYMBOLS(listen_dev, close_listen_session,
+                close_listen_session_t, close_listen_session);
+
+        adev->device.close_listen_session = listen_dev->close_listen_session;
+
+        LISTEN_LOAD_SYMBOLS(listen_dev, set_mad_observer,
+                set_mad_observer_t, set_mad_observer);
+
+        adev->device.set_mad_observer = listen_dev->set_mad_observer;
+
+        LISTEN_LOAD_SYMBOLS(listen_dev, listen_set_parameters,
+                listen_set_parameters_t, listen_hw_set_parameters);
+
+        adev->device.listen_set_parameters = listen_dev->listen_set_parameters;
+
+        LISTEN_LOAD_SYMBOLS(listen_dev, get_parameters,
+                get_parameters_t, listen_hw_get_parameters);
+
+        LISTEN_LOAD_SYMBOLS(listen_dev, notify_event,
+                listen_notify_event_t, listen_hw_notify_event);
+
+        listen_dev->create_listen_hw();
+    }
+    return 0;
+}
+
+void audio_extn_listen_deinit(struct audio_device *adev)
+{
+    ALOGI("%s: Enter", __func__);
+
+    if (listen_dev && (listen_dev->adev == adev) && listen_dev->lib_handle) {
+        listen_dev->destroy_listen_hw();
+        dlclose(listen_dev->lib_handle);
+        free(listen_dev);
+        listen_dev = NULL;
+    }
+}
+
+#endif /* AUDIO_LISTEN_ENABLED */
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index b53892f..59e7c86 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -135,7 +135,8 @@
 
 static struct audio_device *adev = NULL;
 static pthread_mutex_t adev_init_lock;
-static bool is_adev_initialised = false;
+static unsigned int audio_device_ref_count;
+
 static int set_voice_volume_l(struct audio_device *adev, float volume);
 
 static bool is_supported_format(audio_format_t format)
@@ -694,6 +695,9 @@
     /* 2. Disable the tx device */
     disable_snd_device(adev, uc_info->in_snd_device, true);
 
+    audio_extn_listen_update_status(uc_info,
+            LISTEN_EVENT_AUDIO_CAPTURE_INACTIVE);
+
     list_remove(&uc_info->list);
     free(uc_info);
 
@@ -738,6 +742,9 @@
     list_add_tail(&adev->usecase_list, &uc_info->list);
     select_devices(adev, in->usecase);
 
+    audio_extn_listen_update_status(uc_info,
+            LISTEN_EVENT_AUDIO_CAPTURE_ACTIVE);
+
     ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d",
           __func__, SOUND_CARD, in->pcm_device_id, in->config.channels);
     in->pcm = pcm_open(SOUND_CARD, in->pcm_device_id,
@@ -2289,10 +2296,21 @@
 static int adev_close(hw_device_t *device)
 {
     struct audio_device *adev = (struct audio_device *)device;
-    audio_route_free(adev->audio_route);
-    free(adev->snd_dev_ref_cnt);
-    platform_deinit(adev->platform);
-    free(device);
+
+    if (!adev)
+        return 0;
+
+    pthread_mutex_lock(&adev_init_lock);
+
+    if ((--audio_device_ref_count) == 0) {
+        audio_route_free(adev->audio_route);
+        free(adev->snd_dev_ref_cnt);
+        platform_deinit(adev->platform);
+        audio_extn_listen_deinit(adev);
+        free(device);
+        adev = NULL;
+    }
+    pthread_mutex_unlock(&adev_init_lock);
     return 0;
 }
 
@@ -2305,8 +2323,9 @@
     if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL;
 
     pthread_mutex_lock(&adev_init_lock);
-    if (is_adev_initialised == true){
+    if (audio_device_ref_count != 0){
             *device = &adev->device.common;
+            audio_device_ref_count++;
             ALOGD("%s: returning existing instance of adev", __func__);
             ALOGD("%s: exit", __func__);
             pthread_mutex_unlock(&adev_init_lock);
@@ -2374,11 +2393,11 @@
                                                         "visualizer_hal_stop_output");
         }
     }
+    audio_extn_listen_init(adev);
 
     *device = &adev->device.common;
 
-    /* update init flag*/
-    is_adev_initialised = true;
+    audio_device_ref_count++;
     pthread_mutex_unlock(&adev_init_lock);
 
     ALOGV("%s: exit", __func__);
diff --git a/hal/voice.c b/hal/voice.c
index 39e2dd9..907ebc9 100644
--- a/hal/voice.c
+++ b/hal/voice.c
@@ -31,6 +31,7 @@
 #include "voice_extn/voice_extn.h"
 #include "platform.h"
 #include "platform_api.h"
+#include "audio_extn.h"
 
 struct pcm_config pcm_config_voice_call = {
     .channels = 1,
@@ -106,6 +107,9 @@
     disable_snd_device(adev, uc_info->out_snd_device, false);
     disable_snd_device(adev, uc_info->in_snd_device, true);
 
+    audio_extn_listen_update_status(uc_info,
+            LISTEN_EVENT_AUDIO_CAPTURE_INACTIVE);
+
     list_remove(&uc_info->list);
     free(uc_info);
 
@@ -136,6 +140,9 @@
 
     select_devices(adev, usecase_id);
 
+    audio_extn_listen_update_status(uc_info,
+            LISTEN_EVENT_AUDIO_CAPTURE_ACTIVE);
+
     pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
     pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);