hal: add support for external speaker driver library

Change-Id: I599c96793097ab6202412cbd55c8e9165c226eed

Bug:  17319721
diff --git a/hal/Android.mk b/hal/Android.mk
index a1ff948..18b8bf4 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -22,6 +22,7 @@
 	audio_hw.c \
 	voice.c \
 	platform_info.c \
+	audio_extn/ext_speaker.c \
 	$(AUDIO_PLATFORM)/platform.c
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index efa63aa..6009366 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -19,6 +19,11 @@
 
 #include <cutils/str_parms.h>
 
+void *audio_extn_extspk_init(struct audio_device *adev);
+void audio_extn_extspk_deinit(void *extn);
+void audio_extn_extspk_update(void* extn);
+void audio_extn_extspk_set_mode(void* extn, audio_mode_t mode);
+
 #ifndef HFP_ENABLED
 #define audio_extn_hfp_is_active(adev)                  (0)
 #define audio_extn_hfp_get_usecase()                    (-1)
diff --git a/hal/audio_extn/ext_speaker.c b/hal/audio_extn/ext_speaker.c
new file mode 100644
index 0000000..611e916
--- /dev/null
+++ b/hal/audio_extn/ext_speaker.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ext_speaker"
+/*#define LOG_NDEBUG 0*/
+#include <cutils/log.h>
+
+#include <audio_hw.h>
+#include <dlfcn.h>
+
+#ifdef __LP64__
+        #define LIB_SPEAKER_BUNDLE "/system/lib64/soundfx/libspeakerbundle.so"
+#else
+        #define LIB_SPEAKER_BUNDLE "/system/lib/soundfx/libspeakerbundle.so"
+#endif
+
+typedef void (*set_mode_t)(int);
+typedef void (*set_speaker_on_t)(bool);
+typedef void (*set_earpiece_on_t)(bool);
+typedef void (*set_voice_vol_t)(int);
+
+struct speaker_data {
+    struct audio_device *adev;
+    void *speaker_bundle;
+    set_mode_t set_mode;
+    set_speaker_on_t set_speaker_on;
+    set_earpiece_on_t set_earpiece_on;
+    set_voice_vol_t set_voice_vol;
+};
+
+static struct speaker_data* open_speaker_bundle()
+{
+    struct speaker_data *sd = calloc(1, sizeof(struct speaker_data));
+
+    sd->speaker_bundle = dlopen(LIB_SPEAKER_BUNDLE, RTLD_NOW);
+    if (sd->speaker_bundle == NULL) {
+        ALOGE("%s: DLOPEN failed for %s", __func__, LIB_SPEAKER_BUNDLE);
+        goto error;
+    } else {
+        ALOGV("%s: DLOPEN successful for %s", __func__, LIB_SPEAKER_BUNDLE);
+
+        sd->set_mode = (set_mode_t)dlsym(sd->speaker_bundle,
+                                             "set_mode");
+        if (sd->set_mode == NULL) {
+            ALOGE("%s: dlsym error %s for set_mode", __func__,
+                  dlerror());
+            goto error;
+        }
+        sd->set_speaker_on = (set_speaker_on_t)dlsym(sd->speaker_bundle,
+                                             "set_speaker_on");
+        if (sd->set_speaker_on == NULL) {
+            ALOGE("%s: dlsym error %s for set_speaker_on", __func__,
+                  dlerror());
+            goto error;
+        }
+        sd->set_earpiece_on = (set_earpiece_on_t)dlsym(sd->speaker_bundle,
+                                             "set_earpiece_on");
+        if (sd->set_earpiece_on == NULL) {
+            ALOGE("%s: dlsym error %s for set_earpiece_on", __func__,
+                  dlerror());
+            goto error;
+        }
+        sd->set_voice_vol = (set_voice_vol_t)dlsym(sd->speaker_bundle,
+                                             "set_voice_volume");
+        if (sd->set_voice_vol == NULL) {
+            ALOGE("%s: dlsym error %s for set_voice_volume",
+                  __func__, dlerror());
+            goto error;
+        }
+    }
+    return sd;
+
+error:
+    free(sd);
+    return 0;
+}
+
+static void close_speaker_bundle(struct speaker_data *sd)
+{
+    if (sd != NULL) {
+        dlclose(sd->speaker_bundle);
+        free(sd);
+        sd = NULL;
+    }
+}
+
+void *audio_extn_extspk_init(struct audio_device *adev)
+{
+    struct speaker_data *data = open_speaker_bundle();
+
+    if (data)
+        data->adev = adev;
+
+    return data;
+}
+
+void audio_extn_extspk_deinit(void *extn)
+{
+    struct speaker_data *data = (struct speaker_data*)extn;
+    close_speaker_bundle(data);
+}
+
+void audio_extn_extspk_update(void* extn)
+{
+    struct speaker_data *data = (struct speaker_data*)extn;
+
+    if (data) {
+        bool speaker_on = false;
+        bool earpiece_on = false;
+        struct listnode *node;
+        struct audio_usecase *usecase;
+        list_for_each(node, &data->adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            if (usecase->devices & AUDIO_DEVICE_OUT_EARPIECE) {
+                if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+                    earpiece_on = true;
+                }
+            }
+            if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+                if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+                    speaker_on = true;
+                }
+            }
+        }
+        data->set_earpiece_on(earpiece_on);
+        data->set_speaker_on(speaker_on);
+    }
+}
+
+void audio_extn_extspk_set_mode(void* extn, audio_mode_t mode)
+{
+    struct speaker_data *data = (struct speaker_data*)extn;
+
+    if (data)
+        data->set_mode(mode);
+}
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 1fee1d3..9908b81 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1005,6 +1005,8 @@
     list_remove(&uc_info->list);
     free(uc_info);
 
+    audio_extn_extspk_update(adev->extspk);
+
     /* Must be called after removing the usecase from list */
     if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
         check_and_set_hdmi_channels(adev, DEFAULT_HDMI_OUT_CHANNELS);
@@ -1045,6 +1047,8 @@
 
     select_devices(adev, out->usecase);
 
+    audio_extn_extspk_update(adev->extspk);
+
     ALOGV("%s: Opening PCM device card_id(%d) device_id(%d) format(%#x)",
           __func__, adev->snd_card, out->pcm_device_id, out->config.format);
     if (out->usecase != USECASE_AUDIO_PLAYBACK_OFFLOAD) {
@@ -1353,6 +1357,9 @@
 
         pthread_mutex_unlock(&adev->lock);
         pthread_mutex_unlock(&out->lock);
+
+        /*handles device and call state changes*/
+        audio_extn_extspk_update(adev->extspk);
     }
 
     if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
@@ -2280,6 +2287,9 @@
         adev->mode = mode;
     }
     pthread_mutex_unlock(&adev->lock);
+
+    audio_extn_extspk_set_mode(adev->extspk, mode);
+
     return 0;
 }
 
@@ -2566,6 +2576,7 @@
     audio_route_free(adev->audio_route);
     free(adev->snd_dev_ref_cnt);
     platform_deinit(adev->platform);
+    audio_extn_extspk_deinit(adev->extspk);
     for (i = 0; i < ARRAY_SIZE(adev->use_case_table); ++i) {
         pcm_params_free(adev->use_case_table[i]);
     }
@@ -2649,6 +2660,8 @@
         return -EINVAL;
     }
 
+    adev->extspk = audio_extn_extspk_init(adev);
+
     if (access(VISUALIZER_LIBRARY_PATH, R_OK) == 0) {
         adev->visualizer_lib = dlopen(VISUALIZER_LIBRARY_PATH, RTLD_NOW);
         if (adev->visualizer_lib == NULL) {
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 251a055..c45cb74 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -214,6 +214,7 @@
 
     int snd_card;
     void *platform;
+    void *extspk;
 
     void *visualizer_lib;
     int (*visualizer_start_output)(audio_io_handle_t, int);