Rename module-policy to module-tizenaudio-policy 05/69905/2 accepted/tizen/ivi/20160520.002412 accepted/tizen/mobile/20160520.002438 accepted/tizen/tv/20160520.002352 accepted/tizen/wearable/20160520.002422 submit/tizen/20160519.072940
authorSangchul Lee <sc11.lee@samsung.com>
Tue, 17 May 2016 08:33:31 +0000 (17:33 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Thu, 19 May 2016 00:20:36 +0000 (09:20 +0900)
[Version] 5.0.51
[Profile] Common
[Issue Type] Rename

Change-Id: Ide2028491ac2b4fe0205f8f96fe3a3f4f8a05604
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
Makefile.am
configure.ac
packaging/pulseaudio-modules-tizen.spec
src/module-policy.c [deleted file]
src/module-tizenaudio-policy.c [new file with mode: 0644]

index 98321332cde2d1cd2ad29bbd7ddfea49e332d5a9..e4992bd5cada397c51002141705130a01ce90eb5 100644 (file)
@@ -33,16 +33,16 @@ pulsemodlibexec_LTLIBRARIES = \
                libhal-interface.la \
                module-tizenaudio-sink.la \
                module-tizenaudio-source.la \
-               module-sound-player.la \
-               module-policy.la
+               module-tizenaudio-policy.la \
+               module-sound-player.la
 
 # These are generated by an M4 script
 SYMDEF_FILES = \
                libhal-interface-symdef.h \
                module-tizenaudio-sink-symdef.h \
                module-tizenaudio-source-symdef.h \
-               module-sound-player-symdef.h \
-               module-policy-symdef.h
+               module-tizenaudio-policy-symdef.h \
+               module-sound-player-symdef.h
 
 if HAVE_DBUS
 
@@ -76,15 +76,15 @@ module_sound_player_la_LDFLAGS = $(MODULE_LDFLAGS) $(PACORE_LDFLAGS) $(PA_LDFLAG
 module_sound_player_la_LIBADD = $(MODULE_LIBADD) $(PACORE_LIBS) $(PA_LIBS) $(AM_LIBADD) $(DBUS_LIBS)
 module_sound_player_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(PACORE_CFLAGS) $(PA_CFLAGS)
 
-module_policy_la_SOURCES = \
-          src/module-policy.c   \
+module_tizenaudio_policy_la_SOURCES = \
+          src/module-tizenaudio-policy.c   \
           src/communicator.c   src/communicator.h   \
           src/stream-manager.c   src/stream-manager.h   src/stream-manager-priv.h   \
           src/stream-manager-volume.c   src/stream-manager-volume.h   src/stream-manager-volume-priv.h   \
           src/stream-manager-restriction.c   src/stream-manager-restriction-priv.h   \
           src/device-manager.c   src/device-manager.h \
           src/subscribe-observer.c   src/subscribe-observer.h
-module_policy_la_LDFLAGS = $(MODULE_LDFLAGS) $(PACORE_LDFLAGS) $(PA_LDFLAGS) -L$(pulsemodlibexecdir)
-module_policy_la_LIBADD = $(AM_LIBADD) $(PACORE_LIBS) $(PA_LIBS) $(DBUS_LIBS) $(VCONF_LIBS) $(INIPARSER_LIBS) $(LIBJSON_LIBS) libhal-interface.la
-module_policy_la_CFLAGS = $(AM_CFLAGS) $(PACORE_CFLAGS) $(PA_CFLAGS) $(DBUS_CFLAGS) $(VCONF_CFLAGS) $(INIPARSER_CFLAGS) $(LIBJSON_CFLAGS)
+module_tizenaudio_policy_la_LDFLAGS = $(MODULE_LDFLAGS) $(PACORE_LDFLAGS) $(PA_LDFLAGS) -L$(pulsemodlibexecdir)
+module_tizenaudio_policy_la_LIBADD = $(AM_LIBADD) $(PACORE_LIBS) $(PA_LIBS) $(DBUS_LIBS) $(VCONF_LIBS) $(INIPARSER_LIBS) $(LIBJSON_LIBS) libhal-interface.la
+module_tizenaudio_policy_la_CFLAGS = $(AM_CFLAGS) $(PACORE_CFLAGS) $(PA_CFLAGS) $(DBUS_CFLAGS) $(VCONF_CFLAGS) $(INIPARSER_CFLAGS) $(LIBJSON_CFLAGS)
 
index 0dc6f75e4756eadeb8ba64b7c9887baf568fb475..d6b0aa4e7f67be048ab255ca92297a435ef3a409 100644 (file)
@@ -23,7 +23,7 @@
 AC_PREREQ(2.63)
 
 AC_INIT([pulseaudio-module-tizen],[0.1])
-AC_CONFIG_SRCDIR([src/module-policy.c])
+AC_CONFIG_SRCDIR([src/module-tizenaudio-policy.c])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability subdir-objects silent-rules color-tests dist-xz tar-ustar])
index 94a97cd18d00fe26e94667f007a9257a28f40ed4..1136a679a9d6ad65d86ed50b396d99564f1865b1 100644 (file)
@@ -1,6 +1,6 @@
 Name:             pulseaudio-modules-tizen
 Summary:          Pulseaudio modules for Tizen
-Version:          5.0.50
+Version:          5.0.51
 Release:          0
 Group:            Multimedia/Audio
 License:          LGPL-2.1+
diff --git a/src/module-policy.c b/src/module-policy.c
deleted file mode 100644 (file)
index c6ca931..0000000
+++ /dev/null
@@ -1,1286 +0,0 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdbool.h>
-#include <strings.h>
-#include <vconf.h> // for mono
-#include <iniparser.h>
-#include <unistd.h>
-#include <pthread.h>
-
-#include <pulse/proplist.h>
-#include <pulse/timeval.h>
-#include <pulse/util.h>
-#include <pulse/rtclock.h>
-
-#include <pulsecore/core.h>
-#include <pulsecore/module.h>
-#include <pulsecore/modargs.h>
-#include <pulsecore/core-error.h>
-#include <pulsecore/core-rtclock.h>
-#include <pulsecore/core-scache.h>
-#include <pulsecore/core-subscribe.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/mutex.h>
-#include <pulsecore/log.h>
-#include <pulsecore/namereg.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/source-output.h>
-#include <pulsecore/pstream-util.h>
-#include <pulsecore/strbuf.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/sound-file.h>
-#include <pulsecore/play-memblockq.h>
-#include <pulsecore/shared.h>
-
-#include "module-policy-symdef.h"
-#include "communicator.h"
-#include "hal-interface.h"
-#include "stream-manager.h"
-#include "device-manager.h"
-
-PA_MODULE_AUTHOR("Seungbae Shin");
-PA_MODULE_DESCRIPTION("Media Policy module");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(true);
-PA_MODULE_USAGE(" ");
-
-static const char* const valid_modargs[] = {
-    NULL
-};
-
-struct userdata;
-
-/* Modules for dynamic loading */
-#define MODULE_COMBINE_SINK           "module-combine-sink"
-#define MODULE_NULL_SINK              "module-null-sink"
-#define MODULE_NULL_SOURCE            "module-null-source"
-#define MODULE_LOOPBACK               "module-loopback"
-
-/* Name of combine sink for external route type */
-#define SINK_NAME_COMBINED_EX         SINK_NAME_COMBINED"_ex"
-
-/* Default values */
-#define LOOPBACK_DEFAULT_LATENCY_MSEC    40
-#define LOOPBACK_DEFAULT_ADJUST_SEC      3
-
-/* Macros */
-#define CONVERT_TO_HAL_DIRECTION(stream_type) \
-    ((stream_type == STREAM_SINK_INPUT) ? DIRECTION_OUT : DIRECTION_IN)
-#define CONVERT_TO_DEVICE_DIRECTION(stream_type) \
-    ((stream_type == STREAM_SINK_INPUT) ? DM_DEVICE_DIRECTION_OUT : DM_DEVICE_DIRECTION_IN)
-#define IS_AVAILABLE_DIRECTION(stream_type, device_direction) \
-    ((stream_type == STREAM_SINK_INPUT) ? (device_direction & DM_DEVICE_DIRECTION_OUT) : (device_direction & DM_DEVICE_DIRECTION_IN))
-
-/* PCM Dump */
-#define PA_DUMP_INI_DEFAULT_PATH                SYSCONFDIR"/multimedia/mmfw_audio_pcm_dump.ini" /* SYSCONFDIR is defined at .spec */
-#define PA_DUMP_INI_TEMP_PATH                   "/opt/system/mmfw_audio_pcm_dump.ini"
-#define PA_DUMP_VCONF_KEY                       "memory/private/sound/pcm_dump"
-#define PA_DUMP_PLAYBACK_DECODER_OUT            0x00000001
-#define PA_DUMP_PLAYBACK_RESAMPLER_IN           0x00000008
-#define PA_DUMP_PLAYBACK_RESAMPLER_OUT          0x00000010
-#define PA_DUMP_CAPTURE_ENCODER_IN              0x80000000
-
-typedef enum _device_type {
-    DEVICE_BUILTIN_SPEAKER,
-    DEVICE_BUILTIN_RECEIVER,
-    DEVICE_BUILTIN_MIC,
-    DEVICE_AUDIO_JACK,
-    DEVICE_BT,
-    DEVICE_HDMI,
-    DEVICE_FORWARDING,
-    DEVICE_USB_AUDIO,
-    DEVICE_UNKNOWN,
-    DEVICE_MAX,
-} device_type_t;
-
-enum {
-    CACHED_DEVICE_DIRECTION_IN,
-    CACHED_DEVICE_DIRECTION_OUT,
-    CACHED_DEVICE_DIRECTION_MAX,
-};
-
-int cached_connected_devices[DEVICE_MAX][CACHED_DEVICE_DIRECTION_MAX];
-
-static device_type_t convert_device_type_str(const char *device)
-{
-    if (pa_streq(device, DEVICE_TYPE_SPEAKER))
-        return DEVICE_BUILTIN_SPEAKER;
-    else if (pa_streq(device, DEVICE_TYPE_RECEIVER))
-        return DEVICE_BUILTIN_RECEIVER;
-    else if (pa_streq(device, DEVICE_TYPE_MIC))
-        return DEVICE_BUILTIN_MIC;
-    else if (pa_streq(device, DEVICE_TYPE_AUDIO_JACK))
-        return DEVICE_AUDIO_JACK;
-    else if (pa_streq(device, DEVICE_TYPE_BT))
-        return DEVICE_BT;
-    else if (pa_streq(device, DEVICE_TYPE_HDMI))
-        return DEVICE_HDMI;
-    else if (pa_streq(device, DEVICE_TYPE_FORWARDING))
-        return DEVICE_FORWARDING;
-    else if (pa_streq(device, DEVICE_TYPE_USB_AUDIO))
-        return DEVICE_USB_AUDIO;
-
-    pa_log_warn("unknown device (%s)", device);
-    return DEVICE_UNKNOWN;
-}
-
-struct userdata {
-    pa_core *core;
-    pa_module *module;
-
-    struct {
-        pa_communicator *comm;
-        pa_hook_slot *comm_hook_select_proper_sink_or_source_slot;
-        pa_hook_slot *comm_hook_change_route_slot;
-        pa_hook_slot *comm_hook_device_connection_changed_slot;
-        pa_hook_slot *comm_hook_device_info_changed_slot;
-        pa_hook_slot *comm_hook_update_info_slot;
-    } communicator;
-
-    pa_hal_interface *hal_interface;
-    pa_stream_manager *stream_manager;
-    pa_device_manager *device_manager;
-
-    pa_module *module_combine_sink;
-    pa_module *module_combine_sink_for_ex;
-    pa_module *module_null_sink;
-    pa_module *module_null_source;
-    pa_module *module_loopback;
-    struct {
-        int32_t latency_msec;
-        int32_t adjust_sec;
-        pa_sink *sink;
-        pa_source *source;
-    } loopback_args;
-};
-
-static void __load_dump_config(struct userdata *u)
-{
-    dictionary * dict = NULL;
-    int vconf_dump = 0;
-
-    dict = iniparser_load(PA_DUMP_INI_DEFAULT_PATH);
-    if (!dict) {
-        pa_log_debug("%s load failed. Use temporary file", PA_DUMP_INI_DEFAULT_PATH);
-        dict = iniparser_load(PA_DUMP_INI_TEMP_PATH);
-        if (!dict) {
-            pa_log_warn("%s load failed", PA_DUMP_INI_TEMP_PATH);
-            return;
-        }
-    }
-
-    vconf_dump |= iniparser_getboolean(dict, "pcm_dump:decoder_out", 0) ? PA_DUMP_PLAYBACK_DECODER_OUT : 0;
-    vconf_dump |= iniparser_getboolean(dict, "pcm_dump:resampler_in", 0) ? PA_DUMP_PLAYBACK_RESAMPLER_IN : 0;
-    vconf_dump |= iniparser_getboolean(dict, "pcm_dump:resampler_out", 0) ? PA_DUMP_PLAYBACK_RESAMPLER_OUT : 0;
-    vconf_dump |= iniparser_getboolean(dict, "pcm_dump:encoder_in", 0) ? PA_DUMP_CAPTURE_ENCODER_IN : 0;
-    u->core->dump_sink = (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink", 0);
-    u->core->dump_sink_input = (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink_input", 0);
-    u->core->dump_source = (bool)iniparser_getboolean(dict, "pcm_dump:pa_source", 0);
-    u->core->dump_source_output = (bool)iniparser_getboolean(dict, "pcm_dump:pa_source_output", 0);
-
-    iniparser_freedict(dict);
-
-    if (vconf_set_int(PA_DUMP_VCONF_KEY, vconf_dump)) {
-        pa_log_warn("vconf_set_int %s=%x failed", PA_DUMP_VCONF_KEY, vconf_dump);
-    }
-}
-
-/* Set the proper sink(source) according to the data of the parameter.
- * - ROUTE_TYPE_AUTO(_ALL)
- *     1. Find the proper sink/source comparing between avail_devices
- *       and current connected devices.
- *     2. If not found, set it to null sink/source.
- * - ROUTE_TYPE_MANUAL(_EXT)
- *     1. Find the proper sink/source comparing between avail_devices
- *        and manual_devices that have been set by user.
- *     2. If not found, set it to null sink/source. */
-static pa_hook_result_t select_proper_sink_or_source_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_select *data, struct userdata *u) {
-    uint32_t idx = 0;
-    uint32_t m_idx = 0;
-    uint32_t conn_idx = 0;
-    uint32_t *device_id = NULL;
-    uint32_t dm_device_id = 0;
-    const char *device_type = NULL;
-    const char *dm_device_type = NULL;
-    const char *dm_device_subtype = NULL;
-    dm_device_direction_t dm_device_direction = DM_DEVICE_DIRECTION_NONE;
-    dm_device *device = NULL;
-    dm_device *latest_device = NULL;
-    pa_idxset *conn_devices = NULL;
-    pa_sink *sink = NULL;
-    pa_sink *null_sink = NULL;
-    pa_sink *combine_sink_arg1 = NULL;
-    pa_sink *combine_sink_arg2 = NULL;
-    pa_source *source = NULL;
-    pa_source *null_source = NULL;
-    char *args = NULL;
-    void *s = NULL;
-    uint32_t s_idx = 0;
-    bool is_found = 0;
-    pa_usec_t creation_time = 0;
-    pa_usec_t latest_creation_time = 0;
-
-    pa_assert(c);
-    pa_assert(data);
-    pa_assert(u);
-
-    pa_log_info("[SELECT] select_proper_sink_or_source_hook_cb is called. (%p), stream_type(%d), stream_role(%s), device_role(%s), route_type(%d)",
-                data, data->stream_type, data->stream_role, data->device_role, data->route_type);
-
-    null_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
-    null_source = (pa_source*)pa_namereg_get(u->core, SOURCE_NAME_NULL, PA_NAMEREG_SOURCE);
-    if (!null_sink || !null_source) {
-        pa_log_error("[SELECT] could not get null_sink(%p) or null_source(%p)", null_sink, null_source);
-        return PA_HOOK_OK;
-    }
-
-    /* check if the current occupying role is related to call.
-     * some targets use several pcm card as per their purpose.
-     * e.g) using a specific pcm card during voice call. */
-    if (data->occupying_role) {
-        if (pa_streq(data->occupying_role, STREAM_ROLE_CALL_VOICE)) {
-            data->device_role = DEVICE_ROLE_CALL_VOICE;
-            pa_log_info("[SELECT] current occupying stream role is [%s], set deivce role to [%s]", data->occupying_role, data->device_role);
-        }
-    }
-
-    if ((data->route_type <= STREAM_ROUTE_TYPE_AUTO_ALL) && data->idx_avail_devices) {
-        /* get current connected devices */
-        conn_devices = pa_device_manager_get_device_list(u->device_manager);
-        if (data->route_type == STREAM_ROUTE_TYPE_AUTO || data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) {
-            PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
-                pa_log_info("[SELECT][AUTO(_ALL)] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
-                if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
-                    continue;
-                PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
-                    dm_device_type = pa_device_manager_get_device_type(device);
-                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
-                    dm_device_direction = pa_device_manager_get_device_direction(device);
-                    dm_device_id = pa_device_manager_get_device_id(device);
-                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], id[%u]",
-                                 dm_device_type, dm_device_subtype, dm_device_direction, dm_device_id);
-                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
-                        is_found = true;
-                        pa_log_info("  ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
-                        if (data->stream_type == STREAM_SINK_INPUT) {
-                            if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL && u->module_combine_sink) {
-                                *(data->proper_sink) = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
-                                pa_log_debug("  -- found the combine-sink, set it to the sink");
-                            } else
-                                *(data->proper_sink) = pa_device_manager_get_sink(device, data->device_role);
-                        } else
-                            *(data->proper_source) = pa_device_manager_get_source(device, data->device_role);
-                        break;
-                    }
-                }
-                if (is_found && data->route_type == STREAM_ROUTE_TYPE_AUTO)
-                    break;
-            }
-        } else if (data->route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
-            PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
-                pa_log_info("[SELECT][AUTO_LAST_CONN] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
-                if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
-                    continue;
-                PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
-                    dm_device_type = pa_device_manager_get_device_type(device);
-                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
-                    dm_device_direction = pa_device_manager_get_device_direction(device);
-                    dm_device_id = pa_device_manager_get_device_id(device);
-                    creation_time = pa_device_manager_get_device_creation_time(device);
-                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], id[%u], creation_time[%llu]",
-                                 dm_device_type, dm_device_subtype, dm_device_direction, dm_device_id, creation_time);
-                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
-                        if (!latest_device || (latest_creation_time <= creation_time)) {
-                            latest_device = device;
-                            latest_creation_time = creation_time;
-                            pa_log_info("  ** updated the last connected device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
-                        }
-                    }
-                }
-            }
-            /* update active device info. */
-            if (latest_device) {
-                if (data->stream_type == STREAM_SINK_INPUT)
-                    *(data->proper_sink) = pa_device_manager_get_sink(latest_device, data->device_role);
-                else
-                    *(data->proper_source) = pa_device_manager_get_source(latest_device, data->device_role);
-
-                pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
-            }
-        }
-
-    } else if (data->route_type == STREAM_ROUTE_TYPE_MANUAL && data->idx_manual_devices && data->idx_avail_devices) {
-        PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
-            pa_log_info("[SELECT][MANUAL] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
-            if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
-                continue;
-            PA_IDXSET_FOREACH(device_id, data->idx_manual_devices, m_idx) {
-                if ((device = pa_device_manager_get_device_by_id(u->device_manager, *device_id))) {
-                    dm_device_type = pa_device_manager_get_device_type(device);
-                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
-                    dm_device_direction = pa_device_manager_get_device_direction(device);
-                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], device id[%u]",
-                            dm_device_type, dm_device_subtype, dm_device_direction, *device_id);
-                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
-                        pa_log_info("  ** found a matched device: type[%-16s], direction[0x%x]", device_type, dm_device_direction);
-                        if (data->stream_type == STREAM_SINK_INPUT) {
-                            if ((*(data->proper_sink)) == null_sink)
-                                pa_sink_input_move_to((pa_sink_input*)(data->stream), pa_device_manager_get_sink(device, data->device_role), false);
-                            else
-                                *(data->proper_sink) = pa_device_manager_get_sink(device, data->device_role);
-                        } else {
-                            if ((*(data->proper_source)) == null_source)
-                                pa_source_output_move_to((pa_source_output*)(data->stream), pa_device_manager_get_source(device, data->device_role), false);
-                            else
-                                *(data->proper_source) = pa_device_manager_get_source(device, data->device_role);
-                        }
-                    }
-                }
-            }
-        }
-
-    } else if (data->route_type == STREAM_ROUTE_TYPE_MANUAL_EXT && data->idx_manual_devices && data->idx_avail_devices) {
-        PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
-            pa_log_info("[SELECT][MANUAL_EXT] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
-            if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
-                continue;
-            PA_IDXSET_FOREACH(device_id, data->idx_manual_devices, m_idx) {
-                if ((device = pa_device_manager_get_device_by_id(u->device_manager, *device_id))) {
-                    dm_device_type = pa_device_manager_get_device_type(device);
-                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
-                    dm_device_direction = pa_device_manager_get_device_direction(device);
-                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], device id[%u]",
-                            dm_device_type, dm_device_subtype, dm_device_direction, *device_id);
-                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
-                        pa_log_info("  ** found a matched device: type[%-16s], direction[0x%x]", device_type, dm_device_direction);
-                        /* currently, we support two sinks for combining */
-                        if (data->stream_type == STREAM_SINK_INPUT) {
-                            if (!combine_sink_arg1) {
-                                if ((sink = combine_sink_arg1 = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL)))
-                                    pa_log_debug("  -- combine_sink_arg1[%s], combine_sink_arg2[%p]", sink->name, combine_sink_arg2);
-                                else
-                                    pa_log_warn("  -- could not get combine_sink_arg1");
-                            } else if (!combine_sink_arg2) {
-                                sink = combine_sink_arg2 = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL);
-                                if (sink && !pa_streq(sink->name, combine_sink_arg1->name)) {
-                                    pa_log_debug("  -- combine_sink_arg2[%s]", sink->name);
-                                    /* load combine sink */
-                                    if (!u->module_combine_sink_for_ex) {
-                                        args = pa_sprintf_malloc("sink_name=%s slaves=\"%s,%s\"", SINK_NAME_COMBINED_EX, combine_sink_arg1->name, combine_sink_arg2->name);
-                                        pa_log_info("  -- combined sink is not prepared, now load module[%s]", args);
-                                        u->module_combine_sink_for_ex = pa_module_load(u->core, MODULE_COMBINE_SINK, args);
-                                        pa_xfree(args);
-                                    }
-                                    sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED_EX, PA_NAMEREG_SINK);
-                                    PA_IDXSET_FOREACH(s, combine_sink_arg1->inputs, s_idx) {
-                                        if (s == data->stream) {
-                                            pa_sink_input_move_to(s, sink, false);
-                                            pa_log_debug("  -- *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, sink, sink->name);
-                                            break;
-                                        }
-                                    }
-                                } else if (!sink) {
-                                    pa_log_warn("  -- could not get combine_sink_arg2");
-                                }
-                            }
-                            if (data->origins_from_new_data)
-                                *(data->proper_sink) = sink;
-                            else {
-                                if (((pa_sink_input*)(data->stream))->sink != sink)
-                                    pa_sink_input_move_to(data->stream, sink, false);
-                                }
-
-                        } else if (data->stream_type == STREAM_SOURCE_OUTPUT) {
-                            if ((source = pa_device_manager_get_source(device, DEVICE_ROLE_NORMAL))) {
-                                if (data->origins_from_new_data)
-                                    *(data->proper_source) = source;
-                                else {
-                                    if (((pa_source_output*)(data->stream))->source != source)
-                                        pa_source_output_move_to(data->stream, source, false);
-                                }
-                            } else
-                                pa_log_warn("  -- could not get source");
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    if ((data->stream_type == STREAM_SINK_INPUT) ? !(*(data->proper_sink)) : !(*(data->proper_source))) {
-        pa_log_warn("[SELECT] could not find a proper sink/source, set it to null sink/source");
-        if (data->stream_type == STREAM_SINK_INPUT)
-            *(data->proper_sink) = null_sink;
-        else
-            *(data->proper_source) = null_source;
-    }
-
-    return PA_HOOK_OK;
-}
-
-/* The state of a device using internal audio codec is handled here.
- * Regarding the state of an external device, those is handled in device-manager.c */
-static void set_device_state_if_using_internal_codec(dm_device *device, stream_type_t stream_type, dm_device_state_t device_state) {
-    bool use_internal_codec = false;
-    dm_device_direction_t direction;
-
-    pa_assert(device);
-
-    direction = pa_device_manager_get_device_direction(device);
-    if (IS_AVAILABLE_DIRECTION(stream_type, direction))
-        if ((use_internal_codec = pa_device_manager_is_device_use_internal_codec(device, CONVERT_TO_DEVICE_DIRECTION(stream_type), DEVICE_ROLE_NORMAL)))
-            pa_device_manager_set_device_state(device, CONVERT_TO_DEVICE_DIRECTION(stream_type), device_state);
-
-    return;
-}
-
-/* Open/Close BT SCO if it is possible */
-static int update_bt_sco_state(pa_device_manager *dm, bool open) {
-    dm_device_bt_sco_status_t sco_status;
-
-    pa_assert(dm);
-
-    pa_device_manager_bt_sco_get_status(dm, &sco_status);
-
-    if (!open && (sco_status == DM_DEVICE_BT_SCO_STATUS_OPENED)) {
-        /* close BT SCO */
-        if (pa_device_manager_bt_sco_close(dm)) {
-            pa_log_error("BT SCO was opened, but failed to close SCO");
-            return -1;
-        } else
-            pa_log_debug("BT SCO is now closed");
-
-    } else if (open) {
-        if (sco_status == DM_DEVICE_BT_SCO_STATUS_DISCONNECTED) {
-            pa_log_error("BT SCO is not available for this BT device");
-            return -1;
-        } else if (sco_status == DM_DEVICE_BT_SCO_STATUS_CONNECTED) {
-            /* open BT SCO */
-            if (pa_device_manager_bt_sco_open(dm)) {
-                pa_log_error("failed to open BT SCO");
-                return -1;
-            } else
-                pa_log_debug("BT SCO is now opened");
-        }
-    }
-
-    return 0;
-}
-
-/* Load/Unload module-loopback */
-static void update_loopback_module(struct userdata *u, bool load) {
-    char *args = NULL;
-
-    pa_assert(u);
-
-    if (load && u->loopback_args.sink && u->loopback_args.source) {
-        if (!u->loopback_args.latency_msec || !u->loopback_args.latency_msec) {
-            u->loopback_args.latency_msec = LOOPBACK_DEFAULT_LATENCY_MSEC;
-            u->loopback_args.adjust_sec = LOOPBACK_DEFAULT_ADJUST_SEC;
-        }
-        args = pa_sprintf_malloc("sink=%s source=%s latency_msec=%d adjust_time=%d",
-                                 u->loopback_args.sink->name, u->loopback_args.source->name,
-                                 u->loopback_args.latency_msec, u->loopback_args.adjust_sec);
-        if (u->module_loopback)
-            pa_module_unload(u->core, u->module_loopback, true);
-
-        u->module_loopback = pa_module_load(u->core, MODULE_LOOPBACK, args);
-
-        pa_log_info("  -- load module-loopback with (%s)", args);
-        pa_xfree(args);
-
-    } else if (!load) {
-        if (u->module_loopback) {
-            pa_module_unload(u->core, u->module_loopback, true);
-            u->module_loopback = NULL;
-            u->loopback_args.sink = NULL;
-            u->loopback_args.source = NULL;
-            pa_log_info("  -- unload module-loopback");
-        }
-    } else {
-        pa_log_error("  -- failed to update loopback module");
-    }
-}
-
-/* Change the route setting according to the data from argument.
- * This function is called only when it needs to change routing path via HAL.
- * - stream is null
- *     1. It will be received when it is needed to terminate playback
- *       or capture routing path.
- *     2. Update the state of the device to be deactivated.
- *     3. Call HAL API to "reset" routing.
- * - ROUTE_TYPE_AUTO
- *     1. Find the proper sink/source comparing between avail_devices
- *       and current connected devices.
- *      : Need to check the priority of the device list by order of receipt.
- *     2. Update the state of devices.
- *     3. Call HAL API to apply the routing setting
- * - ROUTE_TYPE_AUTO_ALL
- *     1. Find the proper sink/source comparing between avail_devices
- *       and current connected devices.
- *      : Might use combine-sink according to the conditions.
- *     2. Update the state of devices.
- *     3. Call HAL API to apply the routing setting
- * - ROUTE_TYPE_MANUAL
- *     1. Find the proper sink/source comparing between avail_devices
- *        and manual_devices that have been set by user.
- *     2. Update the state of devices.
- *     3. Call HAL API to apply the routing setting. */
-static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_route *data, struct userdata *u) {
-    uint32_t i = 0;
-    uint32_t idx = 0;
-    uint32_t d_idx = 0;
-    uint32_t s_idx = 0;
-    hal_route_info route_info = {NULL, NULL, 0};
-    uint32_t conn_idx = 0;
-    uint32_t *device_id = NULL;
-    uint32_t dm_device_id = 0;
-    stream_route_type_t route_type;
-    const char *device_type = NULL;
-    dm_device *device = NULL;
-    dm_device *_device = NULL;
-    dm_device *latest_device = NULL;
-    const char *dm_device_type = NULL;
-    const char *dm_device_subtype = NULL;
-    dm_device_state_t dm_device_state = DM_DEVICE_STATE_DEACTIVATED;
-    dm_device_direction_t dm_device_direction = DM_DEVICE_DIRECTION_NONE;
-    io_direction_t hal_direction;
-    void *s = NULL;
-    pa_idxset *streams = NULL;
-    pa_sink *sink = NULL;
-    pa_sink *dst_sink = NULL;
-    pa_source *source = NULL;
-    pa_source *dst_source = NULL;
-    pa_idxset *conn_devices = NULL;
-    pa_sink *combine_sink_arg1 = NULL;
-    pa_sink *combine_sink_arg2 = NULL;
-    pa_sink *combine_sink = NULL;
-    pa_sink *null_sink = NULL;
-    char *args = NULL;
-    bool use_internal_codec = false;
-    pa_usec_t creation_time = 0;
-    pa_usec_t latest_creation_time = 0;
-
-    pa_assert(c);
-    pa_assert(data);
-    pa_assert(u);
-
-    pa_log_info("[ROUTE] route_change_hook_cb is called. (%p), stream_type(%d), stream_role(%s), route_type(%d)",
-                data, data->stream_type, data->stream_role, data->route_type);
-
-    route_info.role = data->stream_role;
-
-    if (data->stream == NULL) {
-        /* unload module-loopback */
-        if (u->module_loopback) {
-            if (data->stream_type == STREAM_SINK_INPUT && u->loopback_args.sink->use_internal_codec) {
-                update_loopback_module(u, false);
-            } else if (data->stream_type == STREAM_SOURCE_OUTPUT && u->loopback_args.source->use_internal_codec) {
-                update_loopback_module(u, false);
-            }
-        }
-        /* update BT SCO: close */
-        update_bt_sco_state(u->device_manager, false);
-
-        /* get current connected devices */
-        conn_devices = pa_device_manager_get_device_list(u->device_manager);
-        /* set device state to deactivate */
-        PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
-            dm_device_type = pa_device_manager_get_device_type(device);
-            dm_device_state = pa_device_manager_get_device_state(device, CONVERT_TO_DEVICE_DIRECTION(data->stream_type));
-            dm_device_direction = pa_device_manager_get_device_direction(device);
-            if (dm_device_state == DM_DEVICE_STATE_ACTIVATED && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
-                pa_log_info("[ROUTE][RESET] found a matched device and set state to DE-ACTIVATED: type[%s], direction[0x%x]",
-                            dm_device_type, dm_device_direction);
-                set_device_state_if_using_internal_codec(device, data->stream_type, DM_DEVICE_STATE_DEACTIVATED);
-            }
-        }
-        route_info.role = "reset";
-        route_info.num_of_devices = 1;
-        route_info.device_infos = pa_xmalloc0(sizeof(hal_device_info)*route_info.num_of_devices);
-        route_info.device_infos[0].direction = CONVERT_TO_HAL_DIRECTION(data->stream_type);
-
-        /* unload combine sink */
-        if (data->stream_type == STREAM_SINK_INPUT && u->module_combine_sink) {
-            pa_log_info("[ROUTE][RESET] unload module[%s]", SINK_NAME_COMBINED);
-            combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
-            null_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
-            if (!combine_sink || !null_sink)
-                pa_log_error("[ROUTE][RESET] could not get combine_sink(%p) or null_sink(%p)", combine_sink, null_sink);
-            else {
-                PA_IDXSET_FOREACH(s, combine_sink->inputs, s_idx) {
-                    pa_sink_input_move_to(s, null_sink, false);
-                    pa_log_debug("[ROUTE][RESET] *** sink-input(%p,%u) moves to sink(%p,%s)",
-                                 s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
-                }
-                pa_sink_suspend(combine_sink, true, PA_SUSPEND_USER);
-            }
-            pa_module_unload(u->core, u->module_combine_sink, true);
-            u->module_combine_sink = NULL;
-        }
-
-    } else if ((data->route_type <= STREAM_ROUTE_TYPE_AUTO_ALL) && data->idx_avail_devices) {
-        /* unload module-loopback */
-        if (u->module_loopback) {
-            if (data->stream_type == STREAM_SINK_INPUT && u->loopback_args.sink->use_internal_codec) {
-                update_loopback_module(u, false);
-            } else if (data->stream_type == STREAM_SOURCE_OUTPUT && u->loopback_args.source->use_internal_codec) {
-                update_loopback_module(u, false);
-            }
-        }
-        /* update BT SCO: close */
-        update_bt_sco_state(u->device_manager, false);
-
-        /* get current connected devices */
-        conn_devices = pa_device_manager_get_device_list(u->device_manager);
-        if (data->route_type == STREAM_ROUTE_TYPE_AUTO || data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) {
-            PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
-                pa_log_info("[ROUTE][AUTO(_ALL)] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, route_info.role, device_type);
-                if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
-                    continue;
-                PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
-                    dm_device_type = pa_device_manager_get_device_type(device);
-                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
-                    dm_device_direction = pa_device_manager_get_device_direction(device);
-                    dm_device_id = pa_device_manager_get_device_id(device);
-                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], id[%u]",
-                                 dm_device_type, dm_device_subtype, dm_device_direction, dm_device_id);
-                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
-                        pa_log_debug("  ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
-                        use_internal_codec = pa_device_manager_is_device_use_internal_codec(device,
-                                                                                            CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
-                                                                                            data->device_role);
-                        if (use_internal_codec) {
-                            hal_direction = CONVERT_TO_HAL_DIRECTION(data->stream_type);
-                            route_info.num_of_devices++;
-                            route_info.device_infos = pa_xrealloc(route_info.device_infos, sizeof(hal_device_info)*route_info.num_of_devices);
-                            route_info.device_infos[route_info.num_of_devices-1].type = dm_device_type;
-                            route_info.device_infos[route_info.num_of_devices-1].direction = hal_direction;
-                            route_info.device_infos[route_info.num_of_devices-1].id = dm_device_id;
-                            pa_log_info("  ** found a matched device and set state to ACTIVATED: type[%-16s], direction[0x%x], id[%u]",
-                                        route_info.device_infos[route_info.num_of_devices-1].type, hal_direction, dm_device_id);
-                            /* set device state to activated */
-                            set_device_state_if_using_internal_codec(device, data->stream_type, DM_DEVICE_STATE_ACTIVATED);
-                        } else
-                            pa_log_debug("  -- it does not use internal audio codec, skip it");
-                        break;
-                    }
-                }
-                if (device == NULL)
-                    continue;
-
-                if (data->route_type == STREAM_ROUTE_TYPE_AUTO) {
-                    /* check if this device uses internal codec */
-                    use_internal_codec = pa_device_manager_is_device_use_internal_codec(device,
-                                                                                        CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
-                                                                                        data->device_role);
-                    if (use_internal_codec) {
-                        /* set other device's state to be deactivated */
-                        PA_IDXSET_FOREACH(_device, conn_devices, conn_idx) {
-                            if (device == _device)
-                                continue;
-                            set_device_state_if_using_internal_codec(_device, data->stream_type, DM_DEVICE_STATE_DEACTIVATED);
-                        }
-                    }
-                    if (data->origins_from_new_data)
-                        pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
-                    else
-                        pa_proplist_sets(GET_STREAM_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
-
-                /* move sink-inputs/source-outputs if needed */
-                if (data->stream_type == STREAM_SINK_INPUT)
-                    sink = pa_device_manager_get_sink(device, data->device_role);
-
-                    /* unload combine sink */
-                    if (data->stream_type == STREAM_SINK_INPUT && u->module_combine_sink) {
-                        if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
-                            PA_IDXSET_FOREACH(s, combine_sink->inputs, s_idx) {
-                                pa_sink_input_move_to(s, sink, false);
-                                pa_log_debug("[ROUTE][AUTO] *** sink-input(%p,%u) moves to sink(%p,%s)",
-                                             s, ((pa_sink_input*)s)->index, sink, sink->name);
-                            }
-                            pa_sink_suspend(combine_sink, true, PA_SUSPEND_USER);
-                        } else
-                            pa_log_error("[ROUTE][AUTO] could not get combine_sink");
-
-                        pa_log_debug("[ROUTE][AUTO] unload module[%s]", SINK_NAME_COMBINED);
-                        pa_module_unload(u->core, u->module_combine_sink, true);
-                        u->module_combine_sink = NULL;
-                    }
-                    break;
-
-                } else if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) {
-                    /* find the proper sink/source */
-                    /* currently, we support two sinks for combining */
-                    if (data->stream_type == STREAM_SINK_INPUT && u->module_combine_sink) {
-                        sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
-                        pa_log_info("[ROUTE][AUTO_ALL] found the combine_sink already existed");
-                    } else if (data->stream_type == STREAM_SINK_INPUT && !combine_sink_arg1) {
-                        sink = combine_sink_arg1 = pa_device_manager_get_sink(device, data->device_role);
-                        pa_log_debug("[ROUTE][AUTO_ALL] combine_sink_arg1[%s], combine_sink_arg2[%p]", sink->name, combine_sink_arg2);
-                    } else if (data->stream_type == STREAM_SINK_INPUT && !combine_sink_arg2) {
-                        sink = combine_sink_arg2 = pa_device_manager_get_sink(device, data->device_role);
-                        if (sink && !pa_streq(sink->name, combine_sink_arg1->name)) {
-                            pa_log_debug("[ROUTE][AUTO_ALL] combine_sink_arg2[%s]", sink->name);
-                            /* load combine sink */
-                            if (!u->module_combine_sink) {
-                                args = pa_sprintf_malloc("sink_name=%s slaves=\"%s,%s\"", SINK_NAME_COMBINED, combine_sink_arg1->name, combine_sink_arg2->name);
-                                pa_log_info("[ROUTE][AUTO_ALL] combined sink is not prepared, now load module[%s]", args);
-                                u->module_combine_sink = pa_module_load(u->core, MODULE_COMBINE_SINK, args);
-                                pa_xfree(args);
-                            }
-                            if ((sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
-                                PA_IDXSET_FOREACH(s, combine_sink_arg1->inputs, s_idx) {
-                                    if (s == data->stream) {
-                                        pa_sink_input_move_to(s, sink, false);
-                                        pa_log_debug("[ROUTE][AUTO_ALL] *** sink-nput(%p,%u) moves to sink(%p,%s)",
-                                                     s, ((pa_sink_input*)s)->index, sink, sink->name);
-                                    }
-                                }
-                            } else
-                                pa_log_error("[ROUTE][AUTO_ALL] could not get combine_sink");
-                        }
-                    } else if (data->stream_type == STREAM_SOURCE_OUTPUT)
-                        source = pa_device_manager_get_source(device, data->device_role);
-
-                    if (data->origins_from_new_data) {
-                        if (data->stream_type == STREAM_SINK_INPUT)
-                            *(data->proper_sink) = sink;
-                        else
-                            *(data->proper_source) = source;
-                    } else {
-                        /* move sink-inputs/source-outputs if needed */
-                        if (data->idx_streams) {
-                            PA_IDXSET_FOREACH(s, data->idx_streams, s_idx) { /* data->idx_streams: null_sink */
-                                if (!pa_stream_manager_get_route_type(s, false, data->stream_type, &route_type) &&
-                                    (route_type == STREAM_ROUTE_TYPE_AUTO_ALL)) {
-                                    if ((data->stream_type == STREAM_SINK_INPUT) && (sink && (sink != ((pa_sink_input*)s)->sink))) {
-                                        pa_sink_input_move_to(s, sink, false);
-                                        pa_log_debug("[ROUTE][AUTO_ALL] *** sink-input(%p,%u) moves to sink(%p,%s)",
-                                                     s, ((pa_sink_input*)s)->index, sink, sink->name);
-                                    } else if ((data->stream_type == STREAM_SOURCE_OUTPUT) && (source && (source != ((pa_source_output*)s)->source))) {
-                                        pa_source_output_move_to(s, source, false);
-                                        pa_log_debug("[ROUTE][AUTO_ALL] *** source-output(%p,%u) moves to source(%p,%s)",
-                                                     s, ((pa_source_output*)s)->index, source, source->name);
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        } else if (data->route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
-            PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
-                pa_log_info("[ROUTE][AUTO_LAST_CONN] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
-                if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
-                    continue;
-                PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
-                    dm_device_type = pa_device_manager_get_device_type(device);
-                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
-                    dm_device_direction = pa_device_manager_get_device_direction(device);
-                    dm_device_id = pa_device_manager_get_device_id(device);
-                    creation_time = pa_device_manager_get_device_creation_time(device);
-                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], id[%u], creation_time[%llu]",
-                                 dm_device_type, dm_device_subtype, dm_device_direction, dm_device_id, creation_time);
-                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
-                        if (!latest_device || (latest_creation_time <= creation_time)) {
-                            latest_device = device;
-                            latest_creation_time = creation_time;
-                            pa_log_info("  ** updated the last connected device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
-                        }
-                    }
-                }
-            }
-            /* update activated device if it is found */
-            if (latest_device) {
-                dm_device_type = pa_device_manager_get_device_type(latest_device);
-                dm_device_id = pa_device_manager_get_device_id(latest_device);
-                use_internal_codec = pa_device_manager_is_device_use_internal_codec(latest_device,
-                                                                                    CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
-                                                                                    data->device_role);
-                if (use_internal_codec) {
-                    hal_direction = CONVERT_TO_HAL_DIRECTION(data->stream_type);
-                    route_info.num_of_devices++;
-                    route_info.device_infos = pa_xrealloc(route_info.device_infos, sizeof(hal_device_info)*route_info.num_of_devices);
-                    route_info.device_infos[route_info.num_of_devices-1].type = dm_device_type;
-                    route_info.device_infos[route_info.num_of_devices-1].direction = hal_direction;
-                    route_info.device_infos[route_info.num_of_devices-1].id = dm_device_id;
-                    pa_log_info("  ** found a matched device and set state to ACTIVATED: type[%-16s], direction[0x%x], id[%u]",
-                    route_info.device_infos[route_info.num_of_devices-1].type, hal_direction, dm_device_id);
-                    /* set device state to activated */
-                    set_device_state_if_using_internal_codec(latest_device, data->stream_type, DM_DEVICE_STATE_ACTIVATED);
-
-                    /* set other device's state to be deactivated */
-                    PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
-                        if (latest_device == device)
-                            continue;
-                        set_device_state_if_using_internal_codec(device, data->stream_type, DM_DEVICE_STATE_DEACTIVATED);
-                    }
-                } else
-                    pa_log_debug("  -- it does not use internal audio codec, skip it");
-
-                if (data->origins_from_new_data)
-                    pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
-                else
-                    pa_proplist_sets(GET_STREAM_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
-
-                /* unload combine sink */
-                if (data->stream_type == STREAM_SINK_INPUT && u->module_combine_sink) {
-                    if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
-                        PA_IDXSET_FOREACH(s, combine_sink->inputs, s_idx) {
-                            pa_sink_input_move_to(s, sink, false);
-                            pa_log_debug("[ROUTE][AUTO_LAST_CONN] *** sink-input(%p,%u) moves to sink(%p,%s)",
-                                         s, ((pa_sink_input*)s)->index, sink, sink->name);
-                        }
-                        pa_sink_suspend(combine_sink, true, PA_SUSPEND_USER);
-                    } else
-                        pa_log_error("[ROUTE][AUTO_LAST_CONN] could not get combine_sink");
-
-                    pa_log_info("[ROUTE][AUTO_LAST_CONN] unload module[%s]", SINK_NAME_COMBINED);
-                    pa_module_unload(u->core, u->module_combine_sink, true);
-                    u->module_combine_sink = NULL;
-                }
-            }
-        }
-
-        if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL && route_info.num_of_devices) {
-            /* set other device's state to be deactivated */
-            PA_IDXSET_FOREACH(_device, conn_devices, conn_idx) {
-                bool need_to_deactive = true;
-                dm_device_id = pa_device_manager_get_device_id(_device);
-                for (i = 0; i < route_info.num_of_devices; i++) {
-                    if (dm_device_id == route_info.device_infos[i].id) {
-                        need_to_deactive = false;
-                        break;
-                    }
-                }
-                if (need_to_deactive)
-                    set_device_state_if_using_internal_codec(_device, data->stream_type, DM_DEVICE_STATE_DEACTIVATED);
-            }
-        }
-
-    } else if (data->route_type == STREAM_ROUTE_TYPE_MANUAL && data->idx_manual_devices && data->idx_avail_devices) {
-        PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
-            pa_log_info("[ROUTE][MANUAL] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
-            if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
-                continue;
-            PA_IDXSET_FOREACH(device_id, data->idx_manual_devices, d_idx) {
-                pa_log_debug("  -- manual_device[%u] for this role[%-16s]: device_id(%u)", idx, data->stream_role, *device_id);
-                if ((device = pa_device_manager_get_device_by_id(u->device_manager, *device_id))) {
-                    dm_device_type = pa_device_manager_get_device_type(device);
-                    if (pa_streq(device_type, dm_device_type)) {
-                        dm_device_direction = pa_device_manager_get_device_direction(device);
-                        dm_device_subtype = pa_device_manager_get_device_subtype(device);
-                        pa_log_debug("  ** found a matched device: type[%-16s], subtype[%-5s], direction[0x%x]",
-                                     dm_device_type, dm_device_subtype, dm_device_direction);
-                        /* Check for BT SCO in case of call routing */
-                        if (pa_streq(route_info.role, STREAM_ROLE_CALL_VOICE) && pa_streq(dm_device_type, DEVICE_TYPE_BT)) {
-                            /* update BT SCO: open */
-                            if (update_bt_sco_state(u->device_manager, true)) {
-                                pa_log_error("  ** could not open BT SCO");
-                                continue;
-                            }
-                        } else {
-                            /* update BT SCO: close */
-                            update_bt_sco_state(u->device_manager, false);
-                        }
-                        /* Check for in/out devices in case of loopback */
-                        if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) {
-                            if (dm_device_direction & DM_DEVICE_DIRECTION_OUT)
-                                u->loopback_args.sink = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL);
-                            else if (dm_device_direction & DM_DEVICE_DIRECTION_IN)
-                                u->loopback_args.source = pa_device_manager_get_source(device, DEVICE_ROLE_NORMAL);
-                        }
-
-                        if (IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
-                            use_internal_codec = pa_device_manager_is_device_use_internal_codec(device,
-                                                                                                CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
-                                                                                                DEVICE_ROLE_NORMAL);
-                            if (use_internal_codec) {
-                                route_info.num_of_devices++;
-                                route_info.device_infos = pa_xrealloc(route_info.device_infos, sizeof(hal_device_info)*route_info.num_of_devices);
-                                route_info.device_infos[route_info.num_of_devices-1].type = dm_device_type;
-                                route_info.device_infos[route_info.num_of_devices-1].direction = CONVERT_TO_HAL_DIRECTION(data->stream_type);
-                                route_info.device_infos[route_info.num_of_devices-1].id = *device_id;
-                                pa_log_info("  ** found a matched device and set state to be ACTIVATED: type[%-16s], direction[0x%x], id[%u]",
-                                            route_info.device_infos[route_info.num_of_devices-1].type, dm_device_direction, *device_id);
-                                /* set device state to be activated */
-                                set_device_state_if_using_internal_codec(device, data->stream_type, DM_DEVICE_STATE_ACTIVATED);
-                            } else
-                                pa_log_debug("  -- it does not use internal audio codec, skip it");
-                        }
-                    }
-                }
-            }
-        }
-        if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) {
-            /* load module-loopback */
-            if (u->loopback_args.sink && u->loopback_args.source)
-                update_loopback_module(u, true);
-        }
-    }
-
-    /* move other streams that are belong to device of NORMAL role
-     * to a proper sink/source if needed */
-    if (device && data->stream && data->origins_from_new_data &&
-        data->route_type == STREAM_ROUTE_TYPE_MANUAL) {
-        if (pa_streq(data->stream_role, STREAM_ROLE_CALL_VOICE)) {
-            if (data->stream_type == STREAM_SINK_INPUT) {
-                if (!(sink = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL)) ||
-                    !(dst_sink = pa_device_manager_get_sink(device, DEVICE_ROLE_CALL_VOICE)) ||
-                    sink == dst_sink)
-                    pa_log_debug("[ROUTE][CALL-VOICE] no need to move streams, sink(%p), dst_sink(%p)", sink, dst_sink);
-                else
-                    streams = sink->inputs;
-            } else if (data->stream_type == STREAM_SOURCE_OUTPUT) {
-                if (!(source = pa_device_manager_get_source(device, DEVICE_ROLE_NORMAL)) ||
-                    !(dst_source = pa_device_manager_get_source(device, DEVICE_ROLE_CALL_VOICE)) ||
-                    source == dst_source)
-                    pa_log_debug("[ROUTE][CALL-VOICE] no need to move streams, source(%p), dst_source(%p)", source, dst_source);
-                else
-                    streams = source->outputs;
-            }
-            if (streams) {
-                PA_IDXSET_FOREACH(s, streams, idx) {
-                    if (data->stream_type == STREAM_SINK_INPUT) {
-                        pa_sink_input_move_to(s, dst_sink, false);
-                        pa_log_debug("[ROUTE][CALL-VOICE] *** sink-input(%p,%u) moves to sink(%p,%s)",
-                                     s, ((pa_sink_input*)s)->index, dst_sink, dst_sink->name);
-                    } else if (data->stream_type == STREAM_SOURCE_OUTPUT) {
-                        pa_source_output_move_to(s, dst_source, false);
-                        pa_log_debug("[ROUTE][CALL-VOICE] *** source-output(%p,%u) moves to source(%p,%s)",
-                                     s, ((pa_source_output*)s)->index, dst_source, dst_source->name);
-                    }
-                }
-                /* make sure the previous device is closed */
-                if (data->stream_type == STREAM_SINK_INPUT)
-                    pa_sink_suspend(sink, true, PA_SUSPEND_INTERNAL);
-                else if(data->stream_type == STREAM_SOURCE_OUTPUT)
-                    pa_source_suspend(source, true, PA_SUSPEND_INTERNAL);
-            }
-        }
-    }
-
-    if (route_info.device_infos) {
-        /* send information to HAL to update route */
-        if (pa_hal_interface_update_route(u->hal_interface, &route_info))
-            pa_log_error("[ROUTE] Failed to pa_hal_interface_update_route()");
-        pa_xfree(route_info.device_infos);
-    }
-
-    /* move streams to a proper sink/source if needed.
-     * some targets use several pcm card as per their purpose.
-     * e.g) using a specific pcm card during voice call.
-     * here is the code for roll-back. */
-    if (device && data->stream && !data->origins_from_new_data &&
-        data->route_type != STREAM_ROUTE_TYPE_MANUAL) {
-        if (data->stream_type == STREAM_SINK_INPUT)
-            sink = pa_device_manager_get_sink(device, data->device_role);
-        else if (data->stream_type == STREAM_SOURCE_OUTPUT)
-            source = pa_device_manager_get_source(device, data->device_role);
-        if (data->idx_streams) {
-            PA_IDXSET_FOREACH(s, data->idx_streams, idx) {
-                if (sink && sink != ((pa_sink_input*)s)->sink) {
-                    if (((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK)) &&
-                        ((pa_sink_input*)s)->sink == combine_sink))
-                        break;
-                    pa_sink_input_move_to(s, sink, false);
-                    pa_log_debug("[ROUTE][ROLLBACK] *** sink-input(%p,%u) moves to sink(%p,%s)",
-                                 s, ((pa_sink_input*)s)->index, sink, sink->name);
-                } else if (source && source != ((pa_source_output*)s)->source) {
-                    pa_source_output_move_to(s, source, false);
-                    pa_log_debug("[ROUTE][ROLLBACK] *** source-output(%p,%u) moves to source(%p,%s)",
-                                 s, ((pa_source_output*)s)->index, source, source->name);
-                }
-            }
-        }
-    }
-
-    return PA_HOOK_OK;
-}
-
-static pa_hook_result_t update_info_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_update_info *data, struct userdata *u) {
-    pa_assert(c);
-    pa_assert(data);
-    pa_assert(u);
-
-    if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) {
-        pa_log_info("[UPDATE] update_info_hook_cb is called. stream_role(%s) [name(%s)/value(%d)]",
-            data->stream_role, data->name, data->value);
-        if (pa_streq(data->name, MSG_FOR_LOOPBACK_ARG_LATENCY))
-            u->loopback_args.latency_msec = data->value;
-        else if (pa_streq(data->name, MSG_FOR_LOOPBACK_ARG_ADJUST_TIME))
-            u->loopback_args.adjust_sec = data->value;
-    }
-
-    return PA_HOOK_OK;
-}
-
-/* Update ref. count of each connected device */
-static void update_connected_devices(const char *device_type, dm_device_direction_t direction, bool is_connected) {
-    int32_t val = 0;
-    int* ptr_in = NULL;
-    int* ptr_out = NULL;
-
-    ptr_in = &cached_connected_devices[convert_device_type_str(device_type)][CACHED_DEVICE_DIRECTION_IN];
-    ptr_out = &cached_connected_devices[convert_device_type_str(device_type)][CACHED_DEVICE_DIRECTION_OUT];
-    val = (is_connected) ? 1 : -1;
-
-    if (direction & DM_DEVICE_DIRECTION_IN)
-        *ptr_in += val;
-    if (direction & DM_DEVICE_DIRECTION_OUT)
-        *ptr_out += val;
-
-    if (*ptr_in < 0)
-        *ptr_in = 0;
-    if (*ptr_out < 0)
-        *ptr_out = 0;
-
-    return;
-}
-
-static void dump_connected_devices()
-{
-#if 0
-    int32_t i = 0;
-
-    pa_log_debug("== dump cached current device ==");
-    for (i = 0; i < DEVICE_MAX-1; i++)
-        pa_log_debug("in: %d, out: %d", cached_connected_devices[i][CACHED_DEVICE_DIRECTION_IN], cached_connected_devices[i][CACHED_DEVICE_DIRECTION_OUT]);
-    pa_log_debug("================================");
-#endif
-    return;
-}
-
-/* Reorganize routing when a device has been connected or disconnected */
-static pa_hook_result_t device_connection_changed_hook_cb(pa_core *c, pa_device_manager_hook_data_for_conn_changed *conn, struct userdata *u) {
-    uint32_t idx = 0;
-    pa_sink_input *s = NULL;
-    dm_device_direction_t device_direction = DM_DEVICE_DIRECTION_OUT;
-    pa_sink *sink = NULL;
-    pa_sink *null_sink = NULL;
-    pa_sink *combine_sink = NULL;
-    bool use_internal_codec = false;
-    pa_idxset* conn_devices = NULL;
-    dm_device* device = NULL;
-
-    pa_assert(c);
-    pa_assert(conn);
-    pa_assert(u);
-
-    device_direction = pa_device_manager_get_device_direction(conn->device);
-
-    pa_log_info("[CONN] device_connection_changed_hook_cb is called. conn(%p), is_connected(%d), device(%p), direction(0x%x)",
-                conn, conn->is_connected, conn->device, device_direction);
-
-    update_connected_devices(pa_device_manager_get_device_type(conn->device), device_direction, conn->is_connected);
-    dump_connected_devices();
-
-    null_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
-    if (!null_sink) {
-        pa_log_error("[CONN] could not get null_sink(%p)", null_sink);
-        return PA_HOOK_OK;
-    }
-
-    use_internal_codec = pa_device_manager_is_device_use_internal_codec(conn->device, device_direction, DEVICE_ROLE_NORMAL);
-    /* update for unloading modules when external device is disconnected */
-    if (!use_internal_codec && !conn->is_connected) {
-        if (device_direction & DM_DEVICE_DIRECTION_OUT) {
-            if (u->module_combine_sink) {
-                /* unload combine sink */
-                if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
-                    conn_devices = pa_device_manager_get_device_list(u->device_manager);
-                    PA_IDXSET_FOREACH(device, conn_devices, idx) {
-                        device_direction = pa_device_manager_get_device_direction(device);
-                        if (device_direction == DM_DEVICE_DIRECTION_OUT) {
-                            if ((use_internal_codec = pa_device_manager_is_device_use_internal_codec(device, device_direction, DEVICE_ROLE_NORMAL))) {
-                                sink = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL);
-                                break;
-                            }
-                        }
-                    }
-                    if (combine_sink->inputs) {
-                        if (!sink)
-                            sink = null_sink;
-                        PA_IDXSET_FOREACH(s, combine_sink->inputs, idx) {
-                            /* re-routing this stream to the remaining device using internal codec */
-                            pa_sink_input_move_to(s, sink, false);
-                            pa_log_debug("[CONN] *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, sink, sink->name);
-                        }
-                    }
-                    pa_sink_suspend(combine_sink, true, PA_SUSPEND_USER);
-                    pa_module_unload(u->core, u->module_combine_sink, true);
-                    u->module_combine_sink = NULL;
-                } else
-                    pa_log_error("[CONN] could not get combine_sink");
-            }
-            if (u->module_combine_sink_for_ex) {
-                /* unload combine sink for external devices */
-                if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED_EX, PA_NAMEREG_SINK))) {
-                    if (combine_sink->inputs) {
-                        PA_IDXSET_FOREACH(s, combine_sink->inputs, idx) {
-                            pa_sink_input_move_to(s, null_sink, false);
-                            pa_log_debug("[CONN] *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
-                        }
-                    }
-                    pa_sink_suspend(combine_sink, true, PA_SUSPEND_USER);
-                    pa_module_unload(u->core, u->module_combine_sink_for_ex, true);
-                    u->module_combine_sink_for_ex = NULL;
-                } else
-                    pa_log_error("[CONN] could not get combine_sink_ex");
-            }
-            /* unload loopback module */
-            if (u->module_loopback)
-                if (u->loopback_args.sink == pa_device_manager_get_sink(conn->device, DEVICE_ROLE_NORMAL))
-                    update_loopback_module(u, false);
-        }
-    if (device_direction & DM_DEVICE_DIRECTION_IN) {
-        /* unload loopback module */
-        if (u->module_loopback)
-            if (u->loopback_args.source == pa_device_manager_get_source(conn->device, DEVICE_ROLE_NORMAL))
-                update_loopback_module(u, false);
-        }
-    }
-
-    return PA_HOOK_OK;
-}
-
-/* Update connected device list when a device's information has been changed */
-static pa_hook_result_t device_info_changed_hook_cb(pa_core *c, pa_device_manager_hook_data_for_info_changed *conn, struct userdata *u) {
-    dm_device_direction_t device_direction = DM_DEVICE_DIRECTION_OUT;
-    int* ptr_in = NULL;
-    int* ptr_out = NULL;
-
-    pa_assert(c);
-    pa_assert(conn);
-    pa_assert(u);
-
-    device_direction = pa_device_manager_get_device_direction(conn->device);
-
-    pa_log_info("[CONN] device_info_changed_hook_cb is called. conn(%p), changed_info(%d), device(%p), direction(0x%x)",
-                conn, conn->changed_info, conn->device, device_direction);
-
-    if (conn->changed_info == DM_DEVICE_CHANGED_INFO_IO_DIRECTION)
-        if (pa_streq(pa_device_manager_get_device_type(conn->device), DEVICE_TYPE_BT)) {
-            /* In case of the bluetooth, we do not maintain a ref. count of the bluetooth devices.
-             * Because bluetooth framework supports only one device simultaneously for audio profile. */
-            ptr_in = &cached_connected_devices[convert_device_type_str(DEVICE_TYPE_BT)][CACHED_DEVICE_DIRECTION_IN];
-            ptr_out = &cached_connected_devices[convert_device_type_str(DEVICE_TYPE_BT)][CACHED_DEVICE_DIRECTION_OUT];
-            *ptr_in = *ptr_out = 0;
-            if (device_direction & DM_DEVICE_DIRECTION_IN)
-                *ptr_in = 1;
-            if (device_direction & DM_DEVICE_DIRECTION_OUT)
-                *ptr_out = 1;
-            dump_connected_devices();
-        }
-
-    return PA_HOOK_OK;
-}
-
-int pa__init(pa_module *m)
-{
-    pa_modargs *ma = NULL;
-    struct userdata *u;
-    char *args = NULL;
-
-    pa_assert(m);
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log("Failed to parse module arguments");
-        goto fail;
-    }
-
-    m->userdata = u = pa_xnew0(struct userdata, 1);
-    u->core = m->core;
-    u->module = m;
-
-    u->hal_interface = pa_hal_interface_get(u->core);
-
-    if ((u->communicator.comm = pa_communicator_get(u->core))) {
-        u->communicator.comm_hook_select_proper_sink_or_source_slot = pa_hook_connect(
-                pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_SELECT_INIT_SINK_OR_SOURCE),
-                PA_HOOK_EARLY, (pa_hook_cb_t)select_proper_sink_or_source_hook_cb, u);
-        u->communicator.comm_hook_change_route_slot = pa_hook_connect(
-                pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_CHANGE_ROUTE),
-                PA_HOOK_EARLY, (pa_hook_cb_t)route_change_hook_cb, u);
-        u->communicator.comm_hook_device_connection_changed_slot = pa_hook_connect(
-                pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_DEVICE_CONNECTION_CHANGED),
-                PA_HOOK_EARLY, (pa_hook_cb_t)device_connection_changed_hook_cb, u);
-        u->communicator.comm_hook_device_info_changed_slot = pa_hook_connect(
-                pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_DEVICE_INFORMATION_CHANGED),
-                PA_HOOK_EARLY, (pa_hook_cb_t)device_info_changed_hook_cb, u);
-        u->communicator.comm_hook_update_info_slot = pa_hook_connect(
-                pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_UPDATE_INFORMATION),
-                PA_HOOK_EARLY, (pa_hook_cb_t)update_info_hook_cb, u);
-    }
-    u->device_manager = pa_device_manager_get(u->core);
-
-    u->stream_manager = pa_stream_manager_init(u->core);
-
-    /* load null sink/source */
-    args = pa_sprintf_malloc("sink_name=%s", SINK_NAME_NULL);
-    u->module_null_sink = pa_module_load(u->core, MODULE_NULL_SINK, args);
-    pa_xfree(args);
-    args = pa_sprintf_malloc("source_name=%s", SOURCE_NAME_NULL);
-    u->module_null_source = pa_module_load(u->core, MODULE_NULL_SOURCE, args);
-    pa_xfree(args);
-
-    __load_dump_config(u);
-
-    pa_log_info("policy module is loaded\n");
-
-    if (ma)
-        pa_modargs_free(ma);
-
-    return 0;
-
-fail:
-    pa__done(m);
-
-    return -1;
-}
-
-void pa__done(pa_module *m)
-{
-    struct userdata* u;
-
-    pa_assert(m);
-
-    if (!(u = m->userdata))
-        return;
-
-    pa_module_unload(u->core, u->module_null_sink, true);
-    u->module_null_sink = NULL;
-    pa_module_unload(u->core, u->module_null_source, true);
-    u->module_null_source = NULL;
-
-    if (u->device_manager)
-        pa_device_manager_unref(u->device_manager);
-
-    if (u->stream_manager)
-        pa_stream_manager_done(u->stream_manager);
-
-    if (u->communicator.comm) {
-        if (u->communicator.comm_hook_select_proper_sink_or_source_slot)
-            pa_hook_slot_free(u->communicator.comm_hook_select_proper_sink_or_source_slot);
-        if (u->communicator.comm_hook_change_route_slot)
-            pa_hook_slot_free(u->communicator.comm_hook_change_route_slot);
-        if (u->communicator.comm_hook_device_connection_changed_slot)
-            pa_hook_slot_free(u->communicator.comm_hook_device_connection_changed_slot);
-        if (u->communicator.comm_hook_device_info_changed_slot)
-            pa_hook_slot_free(u->communicator.comm_hook_device_info_changed_slot);
-        if (u->communicator.comm_hook_update_info_slot)
-            pa_hook_slot_free(u->communicator.comm_hook_update_info_slot);
-        pa_communicator_unref(u->communicator.comm);
-    }
-
-    if (u->hal_interface)
-        pa_hal_interface_unref(u->hal_interface);
-
-    pa_xfree(u);
-
-
-    pa_log_info("policy module is unloaded\n");
-}
diff --git a/src/module-tizenaudio-policy.c b/src/module-tizenaudio-policy.c
new file mode 100644 (file)
index 0000000..2508e3f
--- /dev/null
@@ -0,0 +1,1286 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <strings.h>
+#include <vconf.h> // for mono
+#include <iniparser.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <pulse/proplist.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/sound-file.h>
+#include <pulsecore/play-memblockq.h>
+#include <pulsecore/shared.h>
+
+#include "module-tizenaudio-policy-symdef.h"
+#include "communicator.h"
+#include "hal-interface.h"
+#include "stream-manager.h"
+#include "device-manager.h"
+
+PA_MODULE_AUTHOR("Seungbae Shin, Sangchul Lee, Jeongho Mok");
+PA_MODULE_DESCRIPTION("Tizen Audio Policy module");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(" ");
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct userdata;
+
+/* Modules for dynamic loading */
+#define MODULE_COMBINE_SINK           "module-combine-sink"
+#define MODULE_NULL_SINK              "module-null-sink"
+#define MODULE_NULL_SOURCE            "module-null-source"
+#define MODULE_LOOPBACK               "module-loopback"
+
+/* Name of combine sink for external route type */
+#define SINK_NAME_COMBINED_EX         SINK_NAME_COMBINED"_ex"
+
+/* Default values */
+#define LOOPBACK_DEFAULT_LATENCY_MSEC    40
+#define LOOPBACK_DEFAULT_ADJUST_SEC      3
+
+/* Macros */
+#define CONVERT_TO_HAL_DIRECTION(stream_type) \
+    ((stream_type == STREAM_SINK_INPUT) ? DIRECTION_OUT : DIRECTION_IN)
+#define CONVERT_TO_DEVICE_DIRECTION(stream_type) \
+    ((stream_type == STREAM_SINK_INPUT) ? DM_DEVICE_DIRECTION_OUT : DM_DEVICE_DIRECTION_IN)
+#define IS_AVAILABLE_DIRECTION(stream_type, device_direction) \
+    ((stream_type == STREAM_SINK_INPUT) ? (device_direction & DM_DEVICE_DIRECTION_OUT) : (device_direction & DM_DEVICE_DIRECTION_IN))
+
+/* PCM Dump */
+#define PA_DUMP_INI_DEFAULT_PATH                SYSCONFDIR"/multimedia/mmfw_audio_pcm_dump.ini" /* SYSCONFDIR is defined at .spec */
+#define PA_DUMP_INI_TEMP_PATH                   "/opt/system/mmfw_audio_pcm_dump.ini"
+#define PA_DUMP_VCONF_KEY                       "memory/private/sound/pcm_dump"
+#define PA_DUMP_PLAYBACK_DECODER_OUT            0x00000001
+#define PA_DUMP_PLAYBACK_RESAMPLER_IN           0x00000008
+#define PA_DUMP_PLAYBACK_RESAMPLER_OUT          0x00000010
+#define PA_DUMP_CAPTURE_ENCODER_IN              0x80000000
+
+typedef enum _device_type {
+    DEVICE_BUILTIN_SPEAKER,
+    DEVICE_BUILTIN_RECEIVER,
+    DEVICE_BUILTIN_MIC,
+    DEVICE_AUDIO_JACK,
+    DEVICE_BT,
+    DEVICE_HDMI,
+    DEVICE_FORWARDING,
+    DEVICE_USB_AUDIO,
+    DEVICE_UNKNOWN,
+    DEVICE_MAX,
+} device_type_t;
+
+enum {
+    CACHED_DEVICE_DIRECTION_IN,
+    CACHED_DEVICE_DIRECTION_OUT,
+    CACHED_DEVICE_DIRECTION_MAX,
+};
+
+int cached_connected_devices[DEVICE_MAX][CACHED_DEVICE_DIRECTION_MAX];
+
+static device_type_t convert_device_type_str(const char *device)
+{
+    if (pa_streq(device, DEVICE_TYPE_SPEAKER))
+        return DEVICE_BUILTIN_SPEAKER;
+    else if (pa_streq(device, DEVICE_TYPE_RECEIVER))
+        return DEVICE_BUILTIN_RECEIVER;
+    else if (pa_streq(device, DEVICE_TYPE_MIC))
+        return DEVICE_BUILTIN_MIC;
+    else if (pa_streq(device, DEVICE_TYPE_AUDIO_JACK))
+        return DEVICE_AUDIO_JACK;
+    else if (pa_streq(device, DEVICE_TYPE_BT))
+        return DEVICE_BT;
+    else if (pa_streq(device, DEVICE_TYPE_HDMI))
+        return DEVICE_HDMI;
+    else if (pa_streq(device, DEVICE_TYPE_FORWARDING))
+        return DEVICE_FORWARDING;
+    else if (pa_streq(device, DEVICE_TYPE_USB_AUDIO))
+        return DEVICE_USB_AUDIO;
+
+    pa_log_warn("unknown device (%s)", device);
+    return DEVICE_UNKNOWN;
+}
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    struct {
+        pa_communicator *comm;
+        pa_hook_slot *comm_hook_select_proper_sink_or_source_slot;
+        pa_hook_slot *comm_hook_change_route_slot;
+        pa_hook_slot *comm_hook_device_connection_changed_slot;
+        pa_hook_slot *comm_hook_device_info_changed_slot;
+        pa_hook_slot *comm_hook_update_info_slot;
+    } communicator;
+
+    pa_hal_interface *hal_interface;
+    pa_stream_manager *stream_manager;
+    pa_device_manager *device_manager;
+
+    pa_module *module_combine_sink;
+    pa_module *module_combine_sink_for_ex;
+    pa_module *module_null_sink;
+    pa_module *module_null_source;
+    pa_module *module_loopback;
+    struct {
+        int32_t latency_msec;
+        int32_t adjust_sec;
+        pa_sink *sink;
+        pa_source *source;
+    } loopback_args;
+};
+
+static void __load_dump_config(struct userdata *u)
+{
+    dictionary * dict = NULL;
+    int vconf_dump = 0;
+
+    dict = iniparser_load(PA_DUMP_INI_DEFAULT_PATH);
+    if (!dict) {
+        pa_log_debug("%s load failed. Use temporary file", PA_DUMP_INI_DEFAULT_PATH);
+        dict = iniparser_load(PA_DUMP_INI_TEMP_PATH);
+        if (!dict) {
+            pa_log_warn("%s load failed", PA_DUMP_INI_TEMP_PATH);
+            return;
+        }
+    }
+
+    vconf_dump |= iniparser_getboolean(dict, "pcm_dump:decoder_out", 0) ? PA_DUMP_PLAYBACK_DECODER_OUT : 0;
+    vconf_dump |= iniparser_getboolean(dict, "pcm_dump:resampler_in", 0) ? PA_DUMP_PLAYBACK_RESAMPLER_IN : 0;
+    vconf_dump |= iniparser_getboolean(dict, "pcm_dump:resampler_out", 0) ? PA_DUMP_PLAYBACK_RESAMPLER_OUT : 0;
+    vconf_dump |= iniparser_getboolean(dict, "pcm_dump:encoder_in", 0) ? PA_DUMP_CAPTURE_ENCODER_IN : 0;
+    u->core->dump_sink = (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink", 0);
+    u->core->dump_sink_input = (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink_input", 0);
+    u->core->dump_source = (bool)iniparser_getboolean(dict, "pcm_dump:pa_source", 0);
+    u->core->dump_source_output = (bool)iniparser_getboolean(dict, "pcm_dump:pa_source_output", 0);
+
+    iniparser_freedict(dict);
+
+    if (vconf_set_int(PA_DUMP_VCONF_KEY, vconf_dump)) {
+        pa_log_warn("vconf_set_int %s=%x failed", PA_DUMP_VCONF_KEY, vconf_dump);
+    }
+}
+
+/* Set the proper sink(source) according to the data of the parameter.
+ * - ROUTE_TYPE_AUTO(_ALL)
+ *     1. Find the proper sink/source comparing between avail_devices
+ *       and current connected devices.
+ *     2. If not found, set it to null sink/source.
+ * - ROUTE_TYPE_MANUAL(_EXT)
+ *     1. Find the proper sink/source comparing between avail_devices
+ *        and manual_devices that have been set by user.
+ *     2. If not found, set it to null sink/source. */
+static pa_hook_result_t select_proper_sink_or_source_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_select *data, struct userdata *u) {
+    uint32_t idx = 0;
+    uint32_t m_idx = 0;
+    uint32_t conn_idx = 0;
+    uint32_t *device_id = NULL;
+    uint32_t dm_device_id = 0;
+    const char *device_type = NULL;
+    const char *dm_device_type = NULL;
+    const char *dm_device_subtype = NULL;
+    dm_device_direction_t dm_device_direction = DM_DEVICE_DIRECTION_NONE;
+    dm_device *device = NULL;
+    dm_device *latest_device = NULL;
+    pa_idxset *conn_devices = NULL;
+    pa_sink *sink = NULL;
+    pa_sink *null_sink = NULL;
+    pa_sink *combine_sink_arg1 = NULL;
+    pa_sink *combine_sink_arg2 = NULL;
+    pa_source *source = NULL;
+    pa_source *null_source = NULL;
+    char *args = NULL;
+    void *s = NULL;
+    uint32_t s_idx = 0;
+    bool is_found = 0;
+    pa_usec_t creation_time = 0;
+    pa_usec_t latest_creation_time = 0;
+
+    pa_assert(c);
+    pa_assert(data);
+    pa_assert(u);
+
+    pa_log_info("[SELECT] select_proper_sink_or_source_hook_cb is called. (%p), stream_type(%d), stream_role(%s), device_role(%s), route_type(%d)",
+                data, data->stream_type, data->stream_role, data->device_role, data->route_type);
+
+    null_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
+    null_source = (pa_source*)pa_namereg_get(u->core, SOURCE_NAME_NULL, PA_NAMEREG_SOURCE);
+    if (!null_sink || !null_source) {
+        pa_log_error("[SELECT] could not get null_sink(%p) or null_source(%p)", null_sink, null_source);
+        return PA_HOOK_OK;
+    }
+
+    /* check if the current occupying role is related to call.
+     * some targets use several pcm card as per their purpose.
+     * e.g) using a specific pcm card during voice call. */
+    if (data->occupying_role) {
+        if (pa_streq(data->occupying_role, STREAM_ROLE_CALL_VOICE)) {
+            data->device_role = DEVICE_ROLE_CALL_VOICE;
+            pa_log_info("[SELECT] current occupying stream role is [%s], set deivce role to [%s]", data->occupying_role, data->device_role);
+        }
+    }
+
+    if ((data->route_type <= STREAM_ROUTE_TYPE_AUTO_ALL) && data->idx_avail_devices) {
+        /* get current connected devices */
+        conn_devices = pa_device_manager_get_device_list(u->device_manager);
+        if (data->route_type == STREAM_ROUTE_TYPE_AUTO || data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) {
+            PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
+                pa_log_info("[SELECT][AUTO(_ALL)] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
+                if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
+                    continue;
+                PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
+                    dm_device_type = pa_device_manager_get_device_type(device);
+                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
+                    dm_device_direction = pa_device_manager_get_device_direction(device);
+                    dm_device_id = pa_device_manager_get_device_id(device);
+                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], id[%u]",
+                                 dm_device_type, dm_device_subtype, dm_device_direction, dm_device_id);
+                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
+                        is_found = true;
+                        pa_log_info("  ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
+                        if (data->stream_type == STREAM_SINK_INPUT) {
+                            if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL && u->module_combine_sink) {
+                                *(data->proper_sink) = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
+                                pa_log_debug("  -- found the combine-sink, set it to the sink");
+                            } else
+                                *(data->proper_sink) = pa_device_manager_get_sink(device, data->device_role);
+                        } else
+                            *(data->proper_source) = pa_device_manager_get_source(device, data->device_role);
+                        break;
+                    }
+                }
+                if (is_found && data->route_type == STREAM_ROUTE_TYPE_AUTO)
+                    break;
+            }
+        } else if (data->route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
+            PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
+                pa_log_info("[SELECT][AUTO_LAST_CONN] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
+                if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
+                    continue;
+                PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
+                    dm_device_type = pa_device_manager_get_device_type(device);
+                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
+                    dm_device_direction = pa_device_manager_get_device_direction(device);
+                    dm_device_id = pa_device_manager_get_device_id(device);
+                    creation_time = pa_device_manager_get_device_creation_time(device);
+                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], id[%u], creation_time[%llu]",
+                                 dm_device_type, dm_device_subtype, dm_device_direction, dm_device_id, creation_time);
+                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
+                        if (!latest_device || (latest_creation_time <= creation_time)) {
+                            latest_device = device;
+                            latest_creation_time = creation_time;
+                            pa_log_info("  ** updated the last connected device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
+                        }
+                    }
+                }
+            }
+            /* update active device info. */
+            if (latest_device) {
+                if (data->stream_type == STREAM_SINK_INPUT)
+                    *(data->proper_sink) = pa_device_manager_get_sink(latest_device, data->device_role);
+                else
+                    *(data->proper_source) = pa_device_manager_get_source(latest_device, data->device_role);
+
+                pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
+            }
+        }
+
+    } else if (data->route_type == STREAM_ROUTE_TYPE_MANUAL && data->idx_manual_devices && data->idx_avail_devices) {
+        PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
+            pa_log_info("[SELECT][MANUAL] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
+            if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
+                continue;
+            PA_IDXSET_FOREACH(device_id, data->idx_manual_devices, m_idx) {
+                if ((device = pa_device_manager_get_device_by_id(u->device_manager, *device_id))) {
+                    dm_device_type = pa_device_manager_get_device_type(device);
+                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
+                    dm_device_direction = pa_device_manager_get_device_direction(device);
+                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], device id[%u]",
+                            dm_device_type, dm_device_subtype, dm_device_direction, *device_id);
+                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
+                        pa_log_info("  ** found a matched device: type[%-16s], direction[0x%x]", device_type, dm_device_direction);
+                        if (data->stream_type == STREAM_SINK_INPUT) {
+                            if ((*(data->proper_sink)) == null_sink)
+                                pa_sink_input_move_to((pa_sink_input*)(data->stream), pa_device_manager_get_sink(device, data->device_role), false);
+                            else
+                                *(data->proper_sink) = pa_device_manager_get_sink(device, data->device_role);
+                        } else {
+                            if ((*(data->proper_source)) == null_source)
+                                pa_source_output_move_to((pa_source_output*)(data->stream), pa_device_manager_get_source(device, data->device_role), false);
+                            else
+                                *(data->proper_source) = pa_device_manager_get_source(device, data->device_role);
+                        }
+                    }
+                }
+            }
+        }
+
+    } else if (data->route_type == STREAM_ROUTE_TYPE_MANUAL_EXT && data->idx_manual_devices && data->idx_avail_devices) {
+        PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
+            pa_log_info("[SELECT][MANUAL_EXT] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
+            if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
+                continue;
+            PA_IDXSET_FOREACH(device_id, data->idx_manual_devices, m_idx) {
+                if ((device = pa_device_manager_get_device_by_id(u->device_manager, *device_id))) {
+                    dm_device_type = pa_device_manager_get_device_type(device);
+                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
+                    dm_device_direction = pa_device_manager_get_device_direction(device);
+                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], device id[%u]",
+                            dm_device_type, dm_device_subtype, dm_device_direction, *device_id);
+                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
+                        pa_log_info("  ** found a matched device: type[%-16s], direction[0x%x]", device_type, dm_device_direction);
+                        /* currently, we support two sinks for combining */
+                        if (data->stream_type == STREAM_SINK_INPUT) {
+                            if (!combine_sink_arg1) {
+                                if ((sink = combine_sink_arg1 = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL)))
+                                    pa_log_debug("  -- combine_sink_arg1[%s], combine_sink_arg2[%p]", sink->name, combine_sink_arg2);
+                                else
+                                    pa_log_warn("  -- could not get combine_sink_arg1");
+                            } else if (!combine_sink_arg2) {
+                                sink = combine_sink_arg2 = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL);
+                                if (sink && !pa_streq(sink->name, combine_sink_arg1->name)) {
+                                    pa_log_debug("  -- combine_sink_arg2[%s]", sink->name);
+                                    /* load combine sink */
+                                    if (!u->module_combine_sink_for_ex) {
+                                        args = pa_sprintf_malloc("sink_name=%s slaves=\"%s,%s\"", SINK_NAME_COMBINED_EX, combine_sink_arg1->name, combine_sink_arg2->name);
+                                        pa_log_info("  -- combined sink is not prepared, now load module[%s]", args);
+                                        u->module_combine_sink_for_ex = pa_module_load(u->core, MODULE_COMBINE_SINK, args);
+                                        pa_xfree(args);
+                                    }
+                                    sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED_EX, PA_NAMEREG_SINK);
+                                    PA_IDXSET_FOREACH(s, combine_sink_arg1->inputs, s_idx) {
+                                        if (s == data->stream) {
+                                            pa_sink_input_move_to(s, sink, false);
+                                            pa_log_debug("  -- *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, sink, sink->name);
+                                            break;
+                                        }
+                                    }
+                                } else if (!sink) {
+                                    pa_log_warn("  -- could not get combine_sink_arg2");
+                                }
+                            }
+                            if (data->origins_from_new_data)
+                                *(data->proper_sink) = sink;
+                            else {
+                                if (((pa_sink_input*)(data->stream))->sink != sink)
+                                    pa_sink_input_move_to(data->stream, sink, false);
+                                }
+
+                        } else if (data->stream_type == STREAM_SOURCE_OUTPUT) {
+                            if ((source = pa_device_manager_get_source(device, DEVICE_ROLE_NORMAL))) {
+                                if (data->origins_from_new_data)
+                                    *(data->proper_source) = source;
+                                else {
+                                    if (((pa_source_output*)(data->stream))->source != source)
+                                        pa_source_output_move_to(data->stream, source, false);
+                                }
+                            } else
+                                pa_log_warn("  -- could not get source");
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    if ((data->stream_type == STREAM_SINK_INPUT) ? !(*(data->proper_sink)) : !(*(data->proper_source))) {
+        pa_log_warn("[SELECT] could not find a proper sink/source, set it to null sink/source");
+        if (data->stream_type == STREAM_SINK_INPUT)
+            *(data->proper_sink) = null_sink;
+        else
+            *(data->proper_source) = null_source;
+    }
+
+    return PA_HOOK_OK;
+}
+
+/* The state of a device using internal audio codec is handled here.
+ * Regarding the state of an external device, those is handled in device-manager.c */
+static void set_device_state_if_using_internal_codec(dm_device *device, stream_type_t stream_type, dm_device_state_t device_state) {
+    bool use_internal_codec = false;
+    dm_device_direction_t direction;
+
+    pa_assert(device);
+
+    direction = pa_device_manager_get_device_direction(device);
+    if (IS_AVAILABLE_DIRECTION(stream_type, direction))
+        if ((use_internal_codec = pa_device_manager_is_device_use_internal_codec(device, CONVERT_TO_DEVICE_DIRECTION(stream_type), DEVICE_ROLE_NORMAL)))
+            pa_device_manager_set_device_state(device, CONVERT_TO_DEVICE_DIRECTION(stream_type), device_state);
+
+    return;
+}
+
+/* Open/Close BT SCO if it is possible */
+static int update_bt_sco_state(pa_device_manager *dm, bool open) {
+    dm_device_bt_sco_status_t sco_status;
+
+    pa_assert(dm);
+
+    pa_device_manager_bt_sco_get_status(dm, &sco_status);
+
+    if (!open && (sco_status == DM_DEVICE_BT_SCO_STATUS_OPENED)) {
+        /* close BT SCO */
+        if (pa_device_manager_bt_sco_close(dm)) {
+            pa_log_error("BT SCO was opened, but failed to close SCO");
+            return -1;
+        } else
+            pa_log_debug("BT SCO is now closed");
+
+    } else if (open) {
+        if (sco_status == DM_DEVICE_BT_SCO_STATUS_DISCONNECTED) {
+            pa_log_error("BT SCO is not available for this BT device");
+            return -1;
+        } else if (sco_status == DM_DEVICE_BT_SCO_STATUS_CONNECTED) {
+            /* open BT SCO */
+            if (pa_device_manager_bt_sco_open(dm)) {
+                pa_log_error("failed to open BT SCO");
+                return -1;
+            } else
+                pa_log_debug("BT SCO is now opened");
+        }
+    }
+
+    return 0;
+}
+
+/* Load/Unload module-loopback */
+static void update_loopback_module(struct userdata *u, bool load) {
+    char *args = NULL;
+
+    pa_assert(u);
+
+    if (load && u->loopback_args.sink && u->loopback_args.source) {
+        if (!u->loopback_args.latency_msec || !u->loopback_args.latency_msec) {
+            u->loopback_args.latency_msec = LOOPBACK_DEFAULT_LATENCY_MSEC;
+            u->loopback_args.adjust_sec = LOOPBACK_DEFAULT_ADJUST_SEC;
+        }
+        args = pa_sprintf_malloc("sink=%s source=%s latency_msec=%d adjust_time=%d",
+                                 u->loopback_args.sink->name, u->loopback_args.source->name,
+                                 u->loopback_args.latency_msec, u->loopback_args.adjust_sec);
+        if (u->module_loopback)
+            pa_module_unload(u->core, u->module_loopback, true);
+
+        u->module_loopback = pa_module_load(u->core, MODULE_LOOPBACK, args);
+
+        pa_log_info("  -- load module-loopback with (%s)", args);
+        pa_xfree(args);
+
+    } else if (!load) {
+        if (u->module_loopback) {
+            pa_module_unload(u->core, u->module_loopback, true);
+            u->module_loopback = NULL;
+            u->loopback_args.sink = NULL;
+            u->loopback_args.source = NULL;
+            pa_log_info("  -- unload module-loopback");
+        }
+    } else {
+        pa_log_error("  -- failed to update loopback module");
+    }
+}
+
+/* Change the route setting according to the data from argument.
+ * This function is called only when it needs to change routing path via HAL.
+ * - stream is null
+ *     1. It will be received when it is needed to terminate playback
+ *       or capture routing path.
+ *     2. Update the state of the device to be deactivated.
+ *     3. Call HAL API to "reset" routing.
+ * - ROUTE_TYPE_AUTO
+ *     1. Find the proper sink/source comparing between avail_devices
+ *       and current connected devices.
+ *      : Need to check the priority of the device list by order of receipt.
+ *     2. Update the state of devices.
+ *     3. Call HAL API to apply the routing setting
+ * - ROUTE_TYPE_AUTO_ALL
+ *     1. Find the proper sink/source comparing between avail_devices
+ *       and current connected devices.
+ *      : Might use combine-sink according to the conditions.
+ *     2. Update the state of devices.
+ *     3. Call HAL API to apply the routing setting
+ * - ROUTE_TYPE_MANUAL
+ *     1. Find the proper sink/source comparing between avail_devices
+ *        and manual_devices that have been set by user.
+ *     2. Update the state of devices.
+ *     3. Call HAL API to apply the routing setting. */
+static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_route *data, struct userdata *u) {
+    uint32_t i = 0;
+    uint32_t idx = 0;
+    uint32_t d_idx = 0;
+    uint32_t s_idx = 0;
+    hal_route_info route_info = {NULL, NULL, 0};
+    uint32_t conn_idx = 0;
+    uint32_t *device_id = NULL;
+    uint32_t dm_device_id = 0;
+    stream_route_type_t route_type;
+    const char *device_type = NULL;
+    dm_device *device = NULL;
+    dm_device *_device = NULL;
+    dm_device *latest_device = NULL;
+    const char *dm_device_type = NULL;
+    const char *dm_device_subtype = NULL;
+    dm_device_state_t dm_device_state = DM_DEVICE_STATE_DEACTIVATED;
+    dm_device_direction_t dm_device_direction = DM_DEVICE_DIRECTION_NONE;
+    io_direction_t hal_direction;
+    void *s = NULL;
+    pa_idxset *streams = NULL;
+    pa_sink *sink = NULL;
+    pa_sink *dst_sink = NULL;
+    pa_source *source = NULL;
+    pa_source *dst_source = NULL;
+    pa_idxset *conn_devices = NULL;
+    pa_sink *combine_sink_arg1 = NULL;
+    pa_sink *combine_sink_arg2 = NULL;
+    pa_sink *combine_sink = NULL;
+    pa_sink *null_sink = NULL;
+    char *args = NULL;
+    bool use_internal_codec = false;
+    pa_usec_t creation_time = 0;
+    pa_usec_t latest_creation_time = 0;
+
+    pa_assert(c);
+    pa_assert(data);
+    pa_assert(u);
+
+    pa_log_info("[ROUTE] route_change_hook_cb is called. (%p), stream_type(%d), stream_role(%s), route_type(%d)",
+                data, data->stream_type, data->stream_role, data->route_type);
+
+    route_info.role = data->stream_role;
+
+    if (data->stream == NULL) {
+        /* unload module-loopback */
+        if (u->module_loopback) {
+            if (data->stream_type == STREAM_SINK_INPUT && u->loopback_args.sink->use_internal_codec) {
+                update_loopback_module(u, false);
+            } else if (data->stream_type == STREAM_SOURCE_OUTPUT && u->loopback_args.source->use_internal_codec) {
+                update_loopback_module(u, false);
+            }
+        }
+        /* update BT SCO: close */
+        update_bt_sco_state(u->device_manager, false);
+
+        /* get current connected devices */
+        conn_devices = pa_device_manager_get_device_list(u->device_manager);
+        /* set device state to deactivate */
+        PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
+            dm_device_type = pa_device_manager_get_device_type(device);
+            dm_device_state = pa_device_manager_get_device_state(device, CONVERT_TO_DEVICE_DIRECTION(data->stream_type));
+            dm_device_direction = pa_device_manager_get_device_direction(device);
+            if (dm_device_state == DM_DEVICE_STATE_ACTIVATED && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
+                pa_log_info("[ROUTE][RESET] found a matched device and set state to DE-ACTIVATED: type[%s], direction[0x%x]",
+                            dm_device_type, dm_device_direction);
+                set_device_state_if_using_internal_codec(device, data->stream_type, DM_DEVICE_STATE_DEACTIVATED);
+            }
+        }
+        route_info.role = "reset";
+        route_info.num_of_devices = 1;
+        route_info.device_infos = pa_xmalloc0(sizeof(hal_device_info)*route_info.num_of_devices);
+        route_info.device_infos[0].direction = CONVERT_TO_HAL_DIRECTION(data->stream_type);
+
+        /* unload combine sink */
+        if (data->stream_type == STREAM_SINK_INPUT && u->module_combine_sink) {
+            pa_log_info("[ROUTE][RESET] unload module[%s]", SINK_NAME_COMBINED);
+            combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
+            null_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
+            if (!combine_sink || !null_sink)
+                pa_log_error("[ROUTE][RESET] could not get combine_sink(%p) or null_sink(%p)", combine_sink, null_sink);
+            else {
+                PA_IDXSET_FOREACH(s, combine_sink->inputs, s_idx) {
+                    pa_sink_input_move_to(s, null_sink, false);
+                    pa_log_debug("[ROUTE][RESET] *** sink-input(%p,%u) moves to sink(%p,%s)",
+                                 s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
+                }
+                pa_sink_suspend(combine_sink, true, PA_SUSPEND_USER);
+            }
+            pa_module_unload(u->core, u->module_combine_sink, true);
+            u->module_combine_sink = NULL;
+        }
+
+    } else if ((data->route_type <= STREAM_ROUTE_TYPE_AUTO_ALL) && data->idx_avail_devices) {
+        /* unload module-loopback */
+        if (u->module_loopback) {
+            if (data->stream_type == STREAM_SINK_INPUT && u->loopback_args.sink->use_internal_codec) {
+                update_loopback_module(u, false);
+            } else if (data->stream_type == STREAM_SOURCE_OUTPUT && u->loopback_args.source->use_internal_codec) {
+                update_loopback_module(u, false);
+            }
+        }
+        /* update BT SCO: close */
+        update_bt_sco_state(u->device_manager, false);
+
+        /* get current connected devices */
+        conn_devices = pa_device_manager_get_device_list(u->device_manager);
+        if (data->route_type == STREAM_ROUTE_TYPE_AUTO || data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) {
+            PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
+                pa_log_info("[ROUTE][AUTO(_ALL)] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, route_info.role, device_type);
+                if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
+                    continue;
+                PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
+                    dm_device_type = pa_device_manager_get_device_type(device);
+                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
+                    dm_device_direction = pa_device_manager_get_device_direction(device);
+                    dm_device_id = pa_device_manager_get_device_id(device);
+                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], id[%u]",
+                                 dm_device_type, dm_device_subtype, dm_device_direction, dm_device_id);
+                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
+                        pa_log_debug("  ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
+                        use_internal_codec = pa_device_manager_is_device_use_internal_codec(device,
+                                                                                            CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
+                                                                                            data->device_role);
+                        if (use_internal_codec) {
+                            hal_direction = CONVERT_TO_HAL_DIRECTION(data->stream_type);
+                            route_info.num_of_devices++;
+                            route_info.device_infos = pa_xrealloc(route_info.device_infos, sizeof(hal_device_info)*route_info.num_of_devices);
+                            route_info.device_infos[route_info.num_of_devices-1].type = dm_device_type;
+                            route_info.device_infos[route_info.num_of_devices-1].direction = hal_direction;
+                            route_info.device_infos[route_info.num_of_devices-1].id = dm_device_id;
+                            pa_log_info("  ** found a matched device and set state to ACTIVATED: type[%-16s], direction[0x%x], id[%u]",
+                                        route_info.device_infos[route_info.num_of_devices-1].type, hal_direction, dm_device_id);
+                            /* set device state to activated */
+                            set_device_state_if_using_internal_codec(device, data->stream_type, DM_DEVICE_STATE_ACTIVATED);
+                        } else
+                            pa_log_debug("  -- it does not use internal audio codec, skip it");
+                        break;
+                    }
+                }
+                if (device == NULL)
+                    continue;
+
+                if (data->route_type == STREAM_ROUTE_TYPE_AUTO) {
+                    /* check if this device uses internal codec */
+                    use_internal_codec = pa_device_manager_is_device_use_internal_codec(device,
+                                                                                        CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
+                                                                                        data->device_role);
+                    if (use_internal_codec) {
+                        /* set other device's state to be deactivated */
+                        PA_IDXSET_FOREACH(_device, conn_devices, conn_idx) {
+                            if (device == _device)
+                                continue;
+                            set_device_state_if_using_internal_codec(_device, data->stream_type, DM_DEVICE_STATE_DEACTIVATED);
+                        }
+                    }
+                    if (data->origins_from_new_data)
+                        pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
+                    else
+                        pa_proplist_sets(GET_STREAM_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
+
+                /* move sink-inputs/source-outputs if needed */
+                if (data->stream_type == STREAM_SINK_INPUT)
+                    sink = pa_device_manager_get_sink(device, data->device_role);
+
+                    /* unload combine sink */
+                    if (data->stream_type == STREAM_SINK_INPUT && u->module_combine_sink) {
+                        if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
+                            PA_IDXSET_FOREACH(s, combine_sink->inputs, s_idx) {
+                                pa_sink_input_move_to(s, sink, false);
+                                pa_log_debug("[ROUTE][AUTO] *** sink-input(%p,%u) moves to sink(%p,%s)",
+                                             s, ((pa_sink_input*)s)->index, sink, sink->name);
+                            }
+                            pa_sink_suspend(combine_sink, true, PA_SUSPEND_USER);
+                        } else
+                            pa_log_error("[ROUTE][AUTO] could not get combine_sink");
+
+                        pa_log_debug("[ROUTE][AUTO] unload module[%s]", SINK_NAME_COMBINED);
+                        pa_module_unload(u->core, u->module_combine_sink, true);
+                        u->module_combine_sink = NULL;
+                    }
+                    break;
+
+                } else if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) {
+                    /* find the proper sink/source */
+                    /* currently, we support two sinks for combining */
+                    if (data->stream_type == STREAM_SINK_INPUT && u->module_combine_sink) {
+                        sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
+                        pa_log_info("[ROUTE][AUTO_ALL] found the combine_sink already existed");
+                    } else if (data->stream_type == STREAM_SINK_INPUT && !combine_sink_arg1) {
+                        sink = combine_sink_arg1 = pa_device_manager_get_sink(device, data->device_role);
+                        pa_log_debug("[ROUTE][AUTO_ALL] combine_sink_arg1[%s], combine_sink_arg2[%p]", sink->name, combine_sink_arg2);
+                    } else if (data->stream_type == STREAM_SINK_INPUT && !combine_sink_arg2) {
+                        sink = combine_sink_arg2 = pa_device_manager_get_sink(device, data->device_role);
+                        if (sink && !pa_streq(sink->name, combine_sink_arg1->name)) {
+                            pa_log_debug("[ROUTE][AUTO_ALL] combine_sink_arg2[%s]", sink->name);
+                            /* load combine sink */
+                            if (!u->module_combine_sink) {
+                                args = pa_sprintf_malloc("sink_name=%s slaves=\"%s,%s\"", SINK_NAME_COMBINED, combine_sink_arg1->name, combine_sink_arg2->name);
+                                pa_log_info("[ROUTE][AUTO_ALL] combined sink is not prepared, now load module[%s]", args);
+                                u->module_combine_sink = pa_module_load(u->core, MODULE_COMBINE_SINK, args);
+                                pa_xfree(args);
+                            }
+                            if ((sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
+                                PA_IDXSET_FOREACH(s, combine_sink_arg1->inputs, s_idx) {
+                                    if (s == data->stream) {
+                                        pa_sink_input_move_to(s, sink, false);
+                                        pa_log_debug("[ROUTE][AUTO_ALL] *** sink-nput(%p,%u) moves to sink(%p,%s)",
+                                                     s, ((pa_sink_input*)s)->index, sink, sink->name);
+                                    }
+                                }
+                            } else
+                                pa_log_error("[ROUTE][AUTO_ALL] could not get combine_sink");
+                        }
+                    } else if (data->stream_type == STREAM_SOURCE_OUTPUT)
+                        source = pa_device_manager_get_source(device, data->device_role);
+
+                    if (data->origins_from_new_data) {
+                        if (data->stream_type == STREAM_SINK_INPUT)
+                            *(data->proper_sink) = sink;
+                        else
+                            *(data->proper_source) = source;
+                    } else {
+                        /* move sink-inputs/source-outputs if needed */
+                        if (data->idx_streams) {
+                            PA_IDXSET_FOREACH(s, data->idx_streams, s_idx) { /* data->idx_streams: null_sink */
+                                if (!pa_stream_manager_get_route_type(s, false, data->stream_type, &route_type) &&
+                                    (route_type == STREAM_ROUTE_TYPE_AUTO_ALL)) {
+                                    if ((data->stream_type == STREAM_SINK_INPUT) && (sink && (sink != ((pa_sink_input*)s)->sink))) {
+                                        pa_sink_input_move_to(s, sink, false);
+                                        pa_log_debug("[ROUTE][AUTO_ALL] *** sink-input(%p,%u) moves to sink(%p,%s)",
+                                                     s, ((pa_sink_input*)s)->index, sink, sink->name);
+                                    } else if ((data->stream_type == STREAM_SOURCE_OUTPUT) && (source && (source != ((pa_source_output*)s)->source))) {
+                                        pa_source_output_move_to(s, source, false);
+                                        pa_log_debug("[ROUTE][AUTO_ALL] *** source-output(%p,%u) moves to source(%p,%s)",
+                                                     s, ((pa_source_output*)s)->index, source, source->name);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } else if (data->route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
+            PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
+                pa_log_info("[ROUTE][AUTO_LAST_CONN] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
+                if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
+                    continue;
+                PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
+                    dm_device_type = pa_device_manager_get_device_type(device);
+                    dm_device_subtype = pa_device_manager_get_device_subtype(device);
+                    dm_device_direction = pa_device_manager_get_device_direction(device);
+                    dm_device_id = pa_device_manager_get_device_id(device);
+                    creation_time = pa_device_manager_get_device_creation_time(device);
+                    pa_log_debug("  -- type[%-16s], subtype[%-5s], direction[0x%x], id[%u], creation_time[%llu]",
+                                 dm_device_type, dm_device_subtype, dm_device_direction, dm_device_id, creation_time);
+                    if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
+                        if (!latest_device || (latest_creation_time <= creation_time)) {
+                            latest_device = device;
+                            latest_creation_time = creation_time;
+                            pa_log_info("  ** updated the last connected device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
+                        }
+                    }
+                }
+            }
+            /* update activated device if it is found */
+            if (latest_device) {
+                dm_device_type = pa_device_manager_get_device_type(latest_device);
+                dm_device_id = pa_device_manager_get_device_id(latest_device);
+                use_internal_codec = pa_device_manager_is_device_use_internal_codec(latest_device,
+                                                                                    CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
+                                                                                    data->device_role);
+                if (use_internal_codec) {
+                    hal_direction = CONVERT_TO_HAL_DIRECTION(data->stream_type);
+                    route_info.num_of_devices++;
+                    route_info.device_infos = pa_xrealloc(route_info.device_infos, sizeof(hal_device_info)*route_info.num_of_devices);
+                    route_info.device_infos[route_info.num_of_devices-1].type = dm_device_type;
+                    route_info.device_infos[route_info.num_of_devices-1].direction = hal_direction;
+                    route_info.device_infos[route_info.num_of_devices-1].id = dm_device_id;
+                    pa_log_info("  ** found a matched device and set state to ACTIVATED: type[%-16s], direction[0x%x], id[%u]",
+                    route_info.device_infos[route_info.num_of_devices-1].type, hal_direction, dm_device_id);
+                    /* set device state to activated */
+                    set_device_state_if_using_internal_codec(latest_device, data->stream_type, DM_DEVICE_STATE_ACTIVATED);
+
+                    /* set other device's state to be deactivated */
+                    PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
+                        if (latest_device == device)
+                            continue;
+                        set_device_state_if_using_internal_codec(device, data->stream_type, DM_DEVICE_STATE_DEACTIVATED);
+                    }
+                } else
+                    pa_log_debug("  -- it does not use internal audio codec, skip it");
+
+                if (data->origins_from_new_data)
+                    pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
+                else
+                    pa_proplist_sets(GET_STREAM_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
+
+                /* unload combine sink */
+                if (data->stream_type == STREAM_SINK_INPUT && u->module_combine_sink) {
+                    if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
+                        PA_IDXSET_FOREACH(s, combine_sink->inputs, s_idx) {
+                            pa_sink_input_move_to(s, sink, false);
+                            pa_log_debug("[ROUTE][AUTO_LAST_CONN] *** sink-input(%p,%u) moves to sink(%p,%s)",
+                                         s, ((pa_sink_input*)s)->index, sink, sink->name);
+                        }
+                        pa_sink_suspend(combine_sink, true, PA_SUSPEND_USER);
+                    } else
+                        pa_log_error("[ROUTE][AUTO_LAST_CONN] could not get combine_sink");
+
+                    pa_log_info("[ROUTE][AUTO_LAST_CONN] unload module[%s]", SINK_NAME_COMBINED);
+                    pa_module_unload(u->core, u->module_combine_sink, true);
+                    u->module_combine_sink = NULL;
+                }
+            }
+        }
+
+        if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL && route_info.num_of_devices) {
+            /* set other device's state to be deactivated */
+            PA_IDXSET_FOREACH(_device, conn_devices, conn_idx) {
+                bool need_to_deactive = true;
+                dm_device_id = pa_device_manager_get_device_id(_device);
+                for (i = 0; i < route_info.num_of_devices; i++) {
+                    if (dm_device_id == route_info.device_infos[i].id) {
+                        need_to_deactive = false;
+                        break;
+                    }
+                }
+                if (need_to_deactive)
+                    set_device_state_if_using_internal_codec(_device, data->stream_type, DM_DEVICE_STATE_DEACTIVATED);
+            }
+        }
+
+    } else if (data->route_type == STREAM_ROUTE_TYPE_MANUAL && data->idx_manual_devices && data->idx_avail_devices) {
+        PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
+            pa_log_info("[ROUTE][MANUAL] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
+            if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(data->stream_type)-1] == 0)
+                continue;
+            PA_IDXSET_FOREACH(device_id, data->idx_manual_devices, d_idx) {
+                pa_log_debug("  -- manual_device[%u] for this role[%-16s]: device_id(%u)", idx, data->stream_role, *device_id);
+                if ((device = pa_device_manager_get_device_by_id(u->device_manager, *device_id))) {
+                    dm_device_type = pa_device_manager_get_device_type(device);
+                    if (pa_streq(device_type, dm_device_type)) {
+                        dm_device_direction = pa_device_manager_get_device_direction(device);
+                        dm_device_subtype = pa_device_manager_get_device_subtype(device);
+                        pa_log_debug("  ** found a matched device: type[%-16s], subtype[%-5s], direction[0x%x]",
+                                     dm_device_type, dm_device_subtype, dm_device_direction);
+                        /* Check for BT SCO in case of call routing */
+                        if (pa_streq(route_info.role, STREAM_ROLE_CALL_VOICE) && pa_streq(dm_device_type, DEVICE_TYPE_BT)) {
+                            /* update BT SCO: open */
+                            if (update_bt_sco_state(u->device_manager, true)) {
+                                pa_log_error("  ** could not open BT SCO");
+                                continue;
+                            }
+                        } else {
+                            /* update BT SCO: close */
+                            update_bt_sco_state(u->device_manager, false);
+                        }
+                        /* Check for in/out devices in case of loopback */
+                        if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) {
+                            if (dm_device_direction & DM_DEVICE_DIRECTION_OUT)
+                                u->loopback_args.sink = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL);
+                            else if (dm_device_direction & DM_DEVICE_DIRECTION_IN)
+                                u->loopback_args.source = pa_device_manager_get_source(device, DEVICE_ROLE_NORMAL);
+                        }
+
+                        if (IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
+                            use_internal_codec = pa_device_manager_is_device_use_internal_codec(device,
+                                                                                                CONVERT_TO_DEVICE_DIRECTION(data->stream_type),
+                                                                                                DEVICE_ROLE_NORMAL);
+                            if (use_internal_codec) {
+                                route_info.num_of_devices++;
+                                route_info.device_infos = pa_xrealloc(route_info.device_infos, sizeof(hal_device_info)*route_info.num_of_devices);
+                                route_info.device_infos[route_info.num_of_devices-1].type = dm_device_type;
+                                route_info.device_infos[route_info.num_of_devices-1].direction = CONVERT_TO_HAL_DIRECTION(data->stream_type);
+                                route_info.device_infos[route_info.num_of_devices-1].id = *device_id;
+                                pa_log_info("  ** found a matched device and set state to be ACTIVATED: type[%-16s], direction[0x%x], id[%u]",
+                                            route_info.device_infos[route_info.num_of_devices-1].type, dm_device_direction, *device_id);
+                                /* set device state to be activated */
+                                set_device_state_if_using_internal_codec(device, data->stream_type, DM_DEVICE_STATE_ACTIVATED);
+                            } else
+                                pa_log_debug("  -- it does not use internal audio codec, skip it");
+                        }
+                    }
+                }
+            }
+        }
+        if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) {
+            /* load module-loopback */
+            if (u->loopback_args.sink && u->loopback_args.source)
+                update_loopback_module(u, true);
+        }
+    }
+
+    /* move other streams that are belong to device of NORMAL role
+     * to a proper sink/source if needed */
+    if (device && data->stream && data->origins_from_new_data &&
+        data->route_type == STREAM_ROUTE_TYPE_MANUAL) {
+        if (pa_streq(data->stream_role, STREAM_ROLE_CALL_VOICE)) {
+            if (data->stream_type == STREAM_SINK_INPUT) {
+                if (!(sink = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL)) ||
+                    !(dst_sink = pa_device_manager_get_sink(device, DEVICE_ROLE_CALL_VOICE)) ||
+                    sink == dst_sink)
+                    pa_log_debug("[ROUTE][CALL-VOICE] no need to move streams, sink(%p), dst_sink(%p)", sink, dst_sink);
+                else
+                    streams = sink->inputs;
+            } else if (data->stream_type == STREAM_SOURCE_OUTPUT) {
+                if (!(source = pa_device_manager_get_source(device, DEVICE_ROLE_NORMAL)) ||
+                    !(dst_source = pa_device_manager_get_source(device, DEVICE_ROLE_CALL_VOICE)) ||
+                    source == dst_source)
+                    pa_log_debug("[ROUTE][CALL-VOICE] no need to move streams, source(%p), dst_source(%p)", source, dst_source);
+                else
+                    streams = source->outputs;
+            }
+            if (streams) {
+                PA_IDXSET_FOREACH(s, streams, idx) {
+                    if (data->stream_type == STREAM_SINK_INPUT) {
+                        pa_sink_input_move_to(s, dst_sink, false);
+                        pa_log_debug("[ROUTE][CALL-VOICE] *** sink-input(%p,%u) moves to sink(%p,%s)",
+                                     s, ((pa_sink_input*)s)->index, dst_sink, dst_sink->name);
+                    } else if (data->stream_type == STREAM_SOURCE_OUTPUT) {
+                        pa_source_output_move_to(s, dst_source, false);
+                        pa_log_debug("[ROUTE][CALL-VOICE] *** source-output(%p,%u) moves to source(%p,%s)",
+                                     s, ((pa_source_output*)s)->index, dst_source, dst_source->name);
+                    }
+                }
+                /* make sure the previous device is closed */
+                if (data->stream_type == STREAM_SINK_INPUT)
+                    pa_sink_suspend(sink, true, PA_SUSPEND_INTERNAL);
+                else if(data->stream_type == STREAM_SOURCE_OUTPUT)
+                    pa_source_suspend(source, true, PA_SUSPEND_INTERNAL);
+            }
+        }
+    }
+
+    if (route_info.device_infos) {
+        /* send information to HAL to update route */
+        if (pa_hal_interface_update_route(u->hal_interface, &route_info))
+            pa_log_error("[ROUTE] Failed to pa_hal_interface_update_route()");
+        pa_xfree(route_info.device_infos);
+    }
+
+    /* move streams to a proper sink/source if needed.
+     * some targets use several pcm card as per their purpose.
+     * e.g) using a specific pcm card during voice call.
+     * here is the code for roll-back. */
+    if (device && data->stream && !data->origins_from_new_data &&
+        data->route_type != STREAM_ROUTE_TYPE_MANUAL) {
+        if (data->stream_type == STREAM_SINK_INPUT)
+            sink = pa_device_manager_get_sink(device, data->device_role);
+        else if (data->stream_type == STREAM_SOURCE_OUTPUT)
+            source = pa_device_manager_get_source(device, data->device_role);
+        if (data->idx_streams) {
+            PA_IDXSET_FOREACH(s, data->idx_streams, idx) {
+                if (sink && sink != ((pa_sink_input*)s)->sink) {
+                    if (((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK)) &&
+                        ((pa_sink_input*)s)->sink == combine_sink))
+                        break;
+                    pa_sink_input_move_to(s, sink, false);
+                    pa_log_debug("[ROUTE][ROLLBACK] *** sink-input(%p,%u) moves to sink(%p,%s)",
+                                 s, ((pa_sink_input*)s)->index, sink, sink->name);
+                } else if (source && source != ((pa_source_output*)s)->source) {
+                    pa_source_output_move_to(s, source, false);
+                    pa_log_debug("[ROUTE][ROLLBACK] *** source-output(%p,%u) moves to source(%p,%s)",
+                                 s, ((pa_source_output*)s)->index, source, source->name);
+                }
+            }
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t update_info_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_update_info *data, struct userdata *u) {
+    pa_assert(c);
+    pa_assert(data);
+    pa_assert(u);
+
+    if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) {
+        pa_log_info("[UPDATE] update_info_hook_cb is called. stream_role(%s) [name(%s)/value(%d)]",
+            data->stream_role, data->name, data->value);
+        if (pa_streq(data->name, MSG_FOR_LOOPBACK_ARG_LATENCY))
+            u->loopback_args.latency_msec = data->value;
+        else if (pa_streq(data->name, MSG_FOR_LOOPBACK_ARG_ADJUST_TIME))
+            u->loopback_args.adjust_sec = data->value;
+    }
+
+    return PA_HOOK_OK;
+}
+
+/* Update ref. count of each connected device */
+static void update_connected_devices(const char *device_type, dm_device_direction_t direction, bool is_connected) {
+    int32_t val = 0;
+    int* ptr_in = NULL;
+    int* ptr_out = NULL;
+
+    ptr_in = &cached_connected_devices[convert_device_type_str(device_type)][CACHED_DEVICE_DIRECTION_IN];
+    ptr_out = &cached_connected_devices[convert_device_type_str(device_type)][CACHED_DEVICE_DIRECTION_OUT];
+    val = (is_connected) ? 1 : -1;
+
+    if (direction & DM_DEVICE_DIRECTION_IN)
+        *ptr_in += val;
+    if (direction & DM_DEVICE_DIRECTION_OUT)
+        *ptr_out += val;
+
+    if (*ptr_in < 0)
+        *ptr_in = 0;
+    if (*ptr_out < 0)
+        *ptr_out = 0;
+
+    return;
+}
+
+static void dump_connected_devices()
+{
+#if 0
+    int32_t i = 0;
+
+    pa_log_debug("== dump cached current device ==");
+    for (i = 0; i < DEVICE_MAX-1; i++)
+        pa_log_debug("in: %d, out: %d", cached_connected_devices[i][CACHED_DEVICE_DIRECTION_IN], cached_connected_devices[i][CACHED_DEVICE_DIRECTION_OUT]);
+    pa_log_debug("================================");
+#endif
+    return;
+}
+
+/* Reorganize routing when a device has been connected or disconnected */
+static pa_hook_result_t device_connection_changed_hook_cb(pa_core *c, pa_device_manager_hook_data_for_conn_changed *conn, struct userdata *u) {
+    uint32_t idx = 0;
+    pa_sink_input *s = NULL;
+    dm_device_direction_t device_direction = DM_DEVICE_DIRECTION_OUT;
+    pa_sink *sink = NULL;
+    pa_sink *null_sink = NULL;
+    pa_sink *combine_sink = NULL;
+    bool use_internal_codec = false;
+    pa_idxset* conn_devices = NULL;
+    dm_device* device = NULL;
+
+    pa_assert(c);
+    pa_assert(conn);
+    pa_assert(u);
+
+    device_direction = pa_device_manager_get_device_direction(conn->device);
+
+    pa_log_info("[CONN] device_connection_changed_hook_cb is called. conn(%p), is_connected(%d), device(%p), direction(0x%x)",
+                conn, conn->is_connected, conn->device, device_direction);
+
+    update_connected_devices(pa_device_manager_get_device_type(conn->device), device_direction, conn->is_connected);
+    dump_connected_devices();
+
+    null_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
+    if (!null_sink) {
+        pa_log_error("[CONN] could not get null_sink(%p)", null_sink);
+        return PA_HOOK_OK;
+    }
+
+    use_internal_codec = pa_device_manager_is_device_use_internal_codec(conn->device, device_direction, DEVICE_ROLE_NORMAL);
+    /* update for unloading modules when external device is disconnected */
+    if (!use_internal_codec && !conn->is_connected) {
+        if (device_direction & DM_DEVICE_DIRECTION_OUT) {
+            if (u->module_combine_sink) {
+                /* unload combine sink */
+                if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
+                    conn_devices = pa_device_manager_get_device_list(u->device_manager);
+                    PA_IDXSET_FOREACH(device, conn_devices, idx) {
+                        device_direction = pa_device_manager_get_device_direction(device);
+                        if (device_direction == DM_DEVICE_DIRECTION_OUT) {
+                            if ((use_internal_codec = pa_device_manager_is_device_use_internal_codec(device, device_direction, DEVICE_ROLE_NORMAL))) {
+                                sink = pa_device_manager_get_sink(device, DEVICE_ROLE_NORMAL);
+                                break;
+                            }
+                        }
+                    }
+                    if (combine_sink->inputs) {
+                        if (!sink)
+                            sink = null_sink;
+                        PA_IDXSET_FOREACH(s, combine_sink->inputs, idx) {
+                            /* re-routing this stream to the remaining device using internal codec */
+                            pa_sink_input_move_to(s, sink, false);
+                            pa_log_debug("[CONN] *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, sink, sink->name);
+                        }
+                    }
+                    pa_sink_suspend(combine_sink, true, PA_SUSPEND_USER);
+                    pa_module_unload(u->core, u->module_combine_sink, true);
+                    u->module_combine_sink = NULL;
+                } else
+                    pa_log_error("[CONN] could not get combine_sink");
+            }
+            if (u->module_combine_sink_for_ex) {
+                /* unload combine sink for external devices */
+                if ((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED_EX, PA_NAMEREG_SINK))) {
+                    if (combine_sink->inputs) {
+                        PA_IDXSET_FOREACH(s, combine_sink->inputs, idx) {
+                            pa_sink_input_move_to(s, null_sink, false);
+                            pa_log_debug("[CONN] *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, null_sink, null_sink->name);
+                        }
+                    }
+                    pa_sink_suspend(combine_sink, true, PA_SUSPEND_USER);
+                    pa_module_unload(u->core, u->module_combine_sink_for_ex, true);
+                    u->module_combine_sink_for_ex = NULL;
+                } else
+                    pa_log_error("[CONN] could not get combine_sink_ex");
+            }
+            /* unload loopback module */
+            if (u->module_loopback)
+                if (u->loopback_args.sink == pa_device_manager_get_sink(conn->device, DEVICE_ROLE_NORMAL))
+                    update_loopback_module(u, false);
+        }
+    if (device_direction & DM_DEVICE_DIRECTION_IN) {
+        /* unload loopback module */
+        if (u->module_loopback)
+            if (u->loopback_args.source == pa_device_manager_get_source(conn->device, DEVICE_ROLE_NORMAL))
+                update_loopback_module(u, false);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+/* Update connected device list when a device's information has been changed */
+static pa_hook_result_t device_info_changed_hook_cb(pa_core *c, pa_device_manager_hook_data_for_info_changed *conn, struct userdata *u) {
+    dm_device_direction_t device_direction = DM_DEVICE_DIRECTION_OUT;
+    int* ptr_in = NULL;
+    int* ptr_out = NULL;
+
+    pa_assert(c);
+    pa_assert(conn);
+    pa_assert(u);
+
+    device_direction = pa_device_manager_get_device_direction(conn->device);
+
+    pa_log_info("[CONN] device_info_changed_hook_cb is called. conn(%p), changed_info(%d), device(%p), direction(0x%x)",
+                conn, conn->changed_info, conn->device, device_direction);
+
+    if (conn->changed_info == DM_DEVICE_CHANGED_INFO_IO_DIRECTION)
+        if (pa_streq(pa_device_manager_get_device_type(conn->device), DEVICE_TYPE_BT)) {
+            /* In case of the bluetooth, we do not maintain a ref. count of the bluetooth devices.
+             * Because bluetooth framework supports only one device simultaneously for audio profile. */
+            ptr_in = &cached_connected_devices[convert_device_type_str(DEVICE_TYPE_BT)][CACHED_DEVICE_DIRECTION_IN];
+            ptr_out = &cached_connected_devices[convert_device_type_str(DEVICE_TYPE_BT)][CACHED_DEVICE_DIRECTION_OUT];
+            *ptr_in = *ptr_out = 0;
+            if (device_direction & DM_DEVICE_DIRECTION_IN)
+                *ptr_in = 1;
+            if (device_direction & DM_DEVICE_DIRECTION_OUT)
+                *ptr_out = 1;
+            dump_connected_devices();
+        }
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module *m)
+{
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    char *args = NULL;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+
+    u->hal_interface = pa_hal_interface_get(u->core);
+
+    if ((u->communicator.comm = pa_communicator_get(u->core))) {
+        u->communicator.comm_hook_select_proper_sink_or_source_slot = pa_hook_connect(
+                pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_SELECT_INIT_SINK_OR_SOURCE),
+                PA_HOOK_EARLY, (pa_hook_cb_t)select_proper_sink_or_source_hook_cb, u);
+        u->communicator.comm_hook_change_route_slot = pa_hook_connect(
+                pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_CHANGE_ROUTE),
+                PA_HOOK_EARLY, (pa_hook_cb_t)route_change_hook_cb, u);
+        u->communicator.comm_hook_device_connection_changed_slot = pa_hook_connect(
+                pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_DEVICE_CONNECTION_CHANGED),
+                PA_HOOK_EARLY, (pa_hook_cb_t)device_connection_changed_hook_cb, u);
+        u->communicator.comm_hook_device_info_changed_slot = pa_hook_connect(
+                pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_DEVICE_INFORMATION_CHANGED),
+                PA_HOOK_EARLY, (pa_hook_cb_t)device_info_changed_hook_cb, u);
+        u->communicator.comm_hook_update_info_slot = pa_hook_connect(
+                pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_UPDATE_INFORMATION),
+                PA_HOOK_EARLY, (pa_hook_cb_t)update_info_hook_cb, u);
+    }
+    u->device_manager = pa_device_manager_get(u->core);
+
+    u->stream_manager = pa_stream_manager_init(u->core);
+
+    /* load null sink/source */
+    args = pa_sprintf_malloc("sink_name=%s", SINK_NAME_NULL);
+    u->module_null_sink = pa_module_load(u->core, MODULE_NULL_SINK, args);
+    pa_xfree(args);
+    args = pa_sprintf_malloc("source_name=%s", SOURCE_NAME_NULL);
+    u->module_null_source = pa_module_load(u->core, MODULE_NULL_SOURCE, args);
+    pa_xfree(args);
+
+    __load_dump_config(u);
+
+    pa_log_info("Tizen Audio Policy module is loaded\n");
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module *m)
+{
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    pa_module_unload(u->core, u->module_null_sink, true);
+    u->module_null_sink = NULL;
+    pa_module_unload(u->core, u->module_null_source, true);
+    u->module_null_source = NULL;
+
+    if (u->device_manager)
+        pa_device_manager_unref(u->device_manager);
+
+    if (u->stream_manager)
+        pa_stream_manager_done(u->stream_manager);
+
+    if (u->communicator.comm) {
+        if (u->communicator.comm_hook_select_proper_sink_or_source_slot)
+            pa_hook_slot_free(u->communicator.comm_hook_select_proper_sink_or_source_slot);
+        if (u->communicator.comm_hook_change_route_slot)
+            pa_hook_slot_free(u->communicator.comm_hook_change_route_slot);
+        if (u->communicator.comm_hook_device_connection_changed_slot)
+            pa_hook_slot_free(u->communicator.comm_hook_device_connection_changed_slot);
+        if (u->communicator.comm_hook_device_info_changed_slot)
+            pa_hook_slot_free(u->communicator.comm_hook_device_info_changed_slot);
+        if (u->communicator.comm_hook_update_info_slot)
+            pa_hook_slot_free(u->communicator.comm_hook_update_info_slot);
+        pa_communicator_unref(u->communicator.comm);
+    }
+
+    if (u->hal_interface)
+        pa_hal_interface_unref(u->hal_interface);
+
+    pa_xfree(u);
+
+
+    pa_log_info("Tizen Audio Policy module is unloaded\n");
+}