[pulseaudio] 01/04: Add support for Android 5.x
David Henningsson
diwic-guest at moszumanska.debian.org
Mon Feb 1 14:50:13 UTC 2016
This is an automated email from the git hooks/post-receive script.
diwic-guest pushed a commit to branch ubuntu
in repository pulseaudio.
commit c34522b59aed3395b59ce96686b09ccc0f88ecf4
Author: Simon Fels <simon.fels at canonical.com>
Date: Mon Feb 1 12:21:24 2016 +0100
Add support for Android 5.x
---
debian/changelog | 18 +
debian/control | 5 +-
...with-upstream-for-Android-5-support-and-b.patch | 5247 ++++++++++++++++++++
.../0601-droid-alternative-hw-module-id.patch | 34 +
.../0602-droid-inputstream-config-parameters.pach | 75 +
...0603-droid-port-priority-and-availability.patch | 103 +
debian/patches/series | 6 +
debian/pulseaudio-module-droid.install | 13 +-
8 files changed, 5494 insertions(+), 7 deletions(-)
diff --git a/debian/changelog b/debian/changelog
index a416e85..8f43b7b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,21 @@
+pulseaudio (1:7.1-1ubuntu5) xenial; urgency=medium
+
+ * debian/pulseaudio-module-droid.install:
+ - Correctly install all droid modules which have changed names
+ after we're now supporting multiple Android versions.
+
+ -- Simon Fels <simon.fels at canonical.com> Thu, 28 Jan 2016 11:19:33 +0100
+
+pulseaudio (1:7.1-1ubuntu4) xenial; urgency=medium
+
+ * debian/patches/0600-droid-sync-with-upstream-for-Android-5-support-and-b.patches
+ debian/patches/0601-droid-alternative-hw-module-id.patch
+ debian/patcheshes/0602-droid-inputstream-config-parameters.pach
+ debian/patches/0603-0603droid-port-priority-and-availability.patch:
+ - Import patches from vivid for Android 5.x support
+
+ -- Simon Fels <simon.fels at canonical.com> Thu, 28 Jan 2016 10:19:54 +0100
+
pulseaudio (1:7.1-1ubuntu3) xenial; urgency=medium
* trust-store: Update translation string
diff --git a/debian/control b/debian/control
index b14027d..04dcd5f 100644
--- a/debian/control
+++ b/debian/control
@@ -8,13 +8,16 @@ Uploaders: Sjoerd Simons <sjoerd at debian.org>,
Felipe Sateler <fsateler at debian.org>
# The following packages can be omitted for bootstrapping (DEB_BUILD_PROFILES=stage1):
# libbluetooth-dev
-Build-Depends: debhelper (>= 9),
+Build-Depends: android-headers-19 (>= 23),
+ android-headers-22 (>= 23),
+ debhelper (>= 9),
cdbs (>= 0.4.93),
check,
dh-autoreconf,
dh-exec,
intltool,
libapparmor-dev [linux-any],
+ libandroid-properties-dev [armhf i386 amd64],
libasound2-dev (>= 1.0.24) [linux-any],
libasyncns-dev,
libatomic-ops-dev,
diff --git a/debian/patches/0600-droid-sync-with-upstream-for-Android-5-support-and-b.patch b/debian/patches/0600-droid-sync-with-upstream-for-Android-5-support-and-b.patch
new file mode 100644
index 0000000..50bfdbc
--- /dev/null
+++ b/debian/patches/0600-droid-sync-with-upstream-for-Android-5-support-and-b.patch
@@ -0,0 +1,5247 @@
+From f821497cf8d1031c66ae3a50759045023b43a615 Mon Sep 17 00:00:00 2001
+From: Simon Fels <simon.fels at canonical.com>
+Date: Thu, 14 Jan 2016 09:49:37 +0100
+Subject: [PATCH] droid: sync with upstream for Android 5 support and building
+ for multiple Android versions
+
+---
+ configure.ac | 20 +
+ src/Makefile.am | 136 +++-
+ src/modules/droid/droid-sink.c | 391 +++++----
+ src/modules/droid/droid-source.c | 307 +++----
+ src/modules/droid/droid-source.h | 3 +
+ src/modules/droid/droid-util-42.h | 5 +-
+ src/modules/droid/droid-util-44.h | 358 +++++++++
+ src/modules/droid/droid-util-51.h | 412 ++++++++++
+ src/modules/droid/droid-util.c | 1248 +++++++++++++++++++++++------
+ src/modules/droid/droid-util.h | 117 ++-
+ src/modules/droid/module-droid-card.c | 585 +++++++++++---
+ src/modules/droid/module-droid-discover.c | 95 +++
+ src/modules/droid/module-droid-sink.c | 27 +-
+ src/modules/droid/module-droid-source.c | 12 +-
+ src/modules/module-device-restore.c | 1 +
+ 15 files changed, 2925 insertions(+), 792 deletions(-)
+ create mode 100644 src/modules/droid/droid-util-44.h
+ create mode 100644 src/modules/droid/droid-util-51.h
+ create mode 100644 src/modules/droid/module-droid-discover.c
+
+Index: overlay/configure.ac
+===================================================================
+--- overlay.orig/configure.ac
++++ overlay/configure.ac
+@@ -822,6 +822,23 @@ AS_IF([test "x$enable_android_hal" = "xy
+ AM_CONDITIONAL([HAVE_ANDROID], [test "x$HAVE_ANDROID" = "x1"])
+ AS_IF([test "x$HAVE_ANDROID" = "x1"], AC_DEFINE([HAVE_ANDROID], 1, [Have Android Audio HAL?]))
+
++AS_IF([test "x$HAVE_ANDROID" = "x1"],
++ [PKG_CHECK_MODULES(LIBANDROID_PROPERTIES, [libandroid-properties], HAVE_ANDROID_PROPERTIES=1, HAVE_ANDROID_PROPERTIES=0)],
++ HAVE_ANDROID_PROPERTIES=0)
++AS_IF([test "x$HAVE_ANDROID" = "x1"],
++ [AS_IF([test "x$HAVE_ANDROID_PROPERTIES" = "x0"],[AC_MSG_ERROR([*** libandroid-properties not found])])],
++ [])
++
++AS_IF([test "x$HAVE_ANDROID" = "x1"],
++ [PKG_CHECK_MODULES(ANDROID_HEADERS_19, [android-headers-19], HAVE_ANDROID_HEADERS_19=1, HAVE_ANDROID_HEADERS_19=0)],
++ HAVE_ANDROID_HEADERS_19=0)
++AM_CONDITIONAL([HAVE_ANDROID_HEADERS_19], [test "x$HAVE_ANDROID_HEADERS_19" = "x1"])
++
++AS_IF([test "x$HAVE_ANDROID" = "x1"],
++ [PKG_CHECK_MODULES(ANDROID_HEADERS_22, [android-headers-22], HAVE_ANDROID_HEADERS_22=1, HAVE_ANDROID_HEADERS_22=0)],
++ HAVE_ANDROID_HEADERS_22=0)
++AM_CONDITIONAL([HAVE_ANDROID_HEADERS_22], [test "x$HAVE_ANDROID_HEADERS_22" = "x1"])
++
+ #### EsounD support (optional) ####
+
+ AC_ARG_ENABLE([esound],
+@@ -1527,6 +1544,8 @@ AS_IF([test "x$HAVE_OSS_OUTPUT" = "x1"],
+ AS_IF([test "x$HAVE_OSS_WRAPPER" = "x1"], ENABLE_OSS_WRAPPER=yes, ENABLE_OSS_WRAPPER=no)
+ AS_IF([test "x$HAVE_ALSA" = "x1"], ENABLE_ALSA=yes, ENABLE_ALSA=no)
+ AS_IF([test "x$HAVE_ANDROID" = "x1"], ENABLE_ANDROID=yes, ENABLE_ANDROID=no)
++AS_IF([test "x$HAVE_ANDROID_HEADERS_19" = "x1"], ENABLE_ANDROID_19=yes, ENABLE_ANDROID_19=no)
++AS_IF([test "x$HAVE_ANDROID_HEADERS_22" = "x1"], ENABLE_ANDROID_22=yes, ENABLE_ANDROID_22=no)
+ AS_IF([test "x$HAVE_COREAUDIO" = "x1"], ENABLE_COREAUDIO=yes, ENABLE_COREAUDIO=no)
+ AS_IF([test "x$HAVE_SOLARIS" = "x1"], ENABLE_SOLARIS=yes, ENABLE_SOLARIS=no)
+ AS_IF([test "x$HAVE_WAVEOUT" = "x1"], ENABLE_WAVEOUT=yes, ENABLE_WAVEOUT=no)
+@@ -1589,6 +1608,8 @@ echo "
+ Enable EsounD: ${ENABLE_ESOUND}
+ Enable Alsa: ${ENABLE_ALSA}
+ Enable Android Audio HAL: ${ENABLE_ANDROID}
++ API Level 19: ${ENABLE_ANDROID_19}
++ API Level 22: ${ENABLE_ANDROID_22}
+ Enable CoreAudio: ${ENABLE_COREAUDIO}
+ Enable Solaris: ${ENABLE_SOLARIS}
+ Enable WaveOut: ${ENABLE_WAVEOUT}
+Index: overlay/src/Makefile.am
+===================================================================
+--- overlay.orig/src/Makefile.am
++++ overlay/src/Makefile.am
+@@ -1272,12 +1272,25 @@ modlibexec_LTLIBRARIES += \
+
+ if HAVE_ANDROID
+ modlibexec_LTLIBRARIES += \
+- libdroid-util.la \
+- libdroid-sink.la \
+- libdroid-source.la \
+- module-droid-sink.la \
+- module-droid-source.la \
+- module-droid-card.la
++ module-droid-discover.la
++if HAVE_ANDROID_HEADERS_19
++modlibexec_LTLIBRARIES += \
++ libdroid-util-19.la \
++ libdroid-sink-19.la \
++ libdroid-source-19.la \
++ module-droid-sink-19.la \
++ module-droid-source-19.la \
++ module-droid-card-19.la
++endif
++if HAVE_ANDROID_HEADERS_22
++modlibexec_LTLIBRARIES += \
++ libdroid-util-22.la \
++ libdroid-sink-22.la \
++ libdroid-source-22.la \
++ module-droid-sink-22.la \
++ module-droid-source-22.la \
++ module-droid-card-22.la
++endif
+ endif
+
+ dist_alsaprofilesets_DATA = \
+@@ -1552,9 +1565,19 @@ SYMDEF_FILES = \
+
+ if HAVE_ANDROID
+ SYMDEF_FILES += \
+- module-droid-sink-symdef.h \
+- module-droid-source-symdef.h \
+- module-droid-card-symdef.h
++ module-droid-discover-symdef.h
++if HAVE_ANDROID_HEADERS_19
++SYMDEF_FILES += \
++ module-droid-sink-19-symdef.h \
++ module-droid-source-19-symdef.h \
++ module-droid-card-19-symdef.h
++endif
++if HAVE_ANDROID_HEADERS_22
++SYMDEF_FILES += \
++ module-droid-sink-22-symdef.h \
++ module-droid-source-22-symdef.h \
++ module-droid-card-22-symdef.h
++endif
+ endif
+
+ if HAVE_ESOUND
+@@ -1867,42 +1890,86 @@ libalsa_util_la_CFLAGS += $(DBUS_CFLAGS)
+ endif
+
+ if HAVE_ANDROID
+-libdroid_util_la_SOURCES = modules/droid/droid-util.c modules/droid/droid-util.h
+-libdroid_util_la_LDFLAGS = -avoid-version
+-libdroid_util_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS)
+-libdroid_util_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS)
+-
+-libdroid_sink_la_SOURCES = modules/droid/droid-sink.c modules/droid/droid-sink.h
+-libdroid_sink_la_LDFLAGS = -avoid-version
+-libdroid_sink_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la
+-libdroid_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS)
+-
+-libdroid_source_la_SOURCES = modules/droid/droid-source.c modules/droid/droid-source.h
+-libdroid_source_la_LDFLAGS = -avoid-version
+-libdroid_source_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la
+-libdroid_source_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS)
+-
+-module_droid_sink_la_SOURCES = modules/droid/module-droid-sink.c
+-module_droid_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+-module_droid_sink_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la libdroid-sink.la
+-module_droid_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS)
+-
+-module_droid_source_la_SOURCES = modules/droid/module-droid-source.c
+-module_droid_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+-module_droid_source_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la libdroid-source.la
+-module_droid_source_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS)
+-
+-module_droid_card_la_SOURCES = modules/droid/module-droid-card.c
+-module_droid_card_la_LDFLAGS = $(MODULE_LDFLAGS)
+-module_droid_card_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la libdroid-sink.la libdroid-source.la
+-module_droid_card_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS)
++module_droid_discover_la_SOURCES = modules/droid/module-droid-discover.c
++module_droid_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
++module_droid_discover_la_LIBADD = $(MODULE_LIBADD) $(LIBANDROID_PROPERTIES_LIBS)
++module_droid_discover_la_CFLAGS = $(AM_CFLAGS) $(LIBANDROID_PROPERTIES_CFLAGS)
++
++if HAVE_ANDROID_HEADERS_19
++libdroid_util_19_la_SOURCES = modules/droid/droid-util.c modules/droid/droid-util.h
++libdroid_util_19_la_LDFLAGS = -avoid-version
++libdroid_util_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS)
++libdroid_util_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS)
++
++libdroid_sink_19_la_SOURCES = modules/droid/droid-sink.c modules/droid/droid-sink.h
++libdroid_sink_19_la_LDFLAGS = -avoid-version
++libdroid_sink_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-19.la
++libdroid_sink_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS)
++
++libdroid_source_19_la_SOURCES = modules/droid/droid-source.c modules/droid/droid-source.h
++libdroid_source_19_la_LDFLAGS = -avoid-version
++libdroid_source_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-19.la
++libdroid_source_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS)
++
++module_droid_sink_19_la_SOURCES = modules/droid/module-droid-sink.c
++module_droid_sink_19_la_LDFLAGS = $(MODULE_LDFLAGS)
++module_droid_sink_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-19.la libdroid-sink-19.la
++module_droid_sink_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS)
++
++module_droid_source_19_la_SOURCES = modules/droid/module-droid-source.c
++module_droid_source_19_la_LDFLAGS = $(MODULE_LDFLAGS)
++module_droid_source_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-19.la libdroid-source-19.la
++module_droid_source_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS)
++
++module_droid_card_19_la_SOURCES = modules/droid/module-droid-card.c
++module_droid_card_19_la_LDFLAGS = $(MODULE_LDFLAGS)
++module_droid_card_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-19.la libdroid-sink-19.la libdroid-source-19.la
++module_droid_card_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS)
+
+ if HAVE_UDEV
+-module_droid_card_la_SOURCES += modules/droid/droid-extcon.c modules/droid/droid-extcon.h
+-module_droid_card_la_LIBADD += $(UDEV_LIBS)
+-module_droid_card_la_CFLAGS += $(UDEV_CFLAGS)
+-endif
++module_droid_card_19_la_SOURCES += modules/droid/droid-extcon.c modules/droid/droid-extcon.h
++module_droid_card_19_la_LIBADD += $(UDEV_LIBS)
++module_droid_card_19_la_CFLAGS += $(UDEV_CFLAGS)
++endif
++endif # HAVE_ANDROID_HEADERS_19
++if HAVE_ANDROID_HEADERS_22
++libdroid_util_22_la_SOURCES = modules/droid/droid-util.c modules/droid/droid-util.h
++libdroid_util_22_la_LDFLAGS = -avoid-version
++libdroid_util_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS)
++libdroid_util_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS)
++
++libdroid_sink_22_la_SOURCES = modules/droid/droid-sink.c modules/droid/droid-sink.h
++libdroid_sink_22_la_LDFLAGS = -avoid-version
++libdroid_sink_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-22.la
++libdroid_sink_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS)
++
++libdroid_source_22_la_SOURCES = modules/droid/droid-source.c modules/droid/droid-source.h
++libdroid_source_22_la_LDFLAGS = -avoid-version
++libdroid_source_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-22.la
++libdroid_source_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS)
++
++module_droid_sink_22_la_SOURCES = modules/droid/module-droid-sink.c
++module_droid_sink_22_la_LDFLAGS = $(MODULE_LDFLAGS)
++module_droid_sink_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-22.la libdroid-sink-22.la
++module_droid_sink_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS)
++
++module_droid_source_22_la_SOURCES = modules/droid/module-droid-source.c
++module_droid_source_22_la_LDFLAGS = $(MODULE_LDFLAGS)
++module_droid_source_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-22.la libdroid-source-22.la
++module_droid_source_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS)
++
++module_droid_card_22_la_SOURCES = modules/droid/module-droid-card.c
++module_droid_card_22_la_LDFLAGS = $(MODULE_LDFLAGS)
++module_droid_card_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-22.la libdroid-sink-22.la libdroid-source-22.la
++module_droid_card_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS)
++
++if HAVE_UDEV
++module_droid_card_22_la_SOURCES += modules/droid/droid-extcon.c modules/droid/droid-extcon.h
++module_droid_card_22_la_LIBADD += $(UDEV_LIBS)
++module_droid_card_22_la_CFLAGS += $(UDEV_CFLAGS)
+ endif
++endif # HAVE_ANDROID_HEADERS_22
++endif # HAVE_ANDROID
+
+ module_alsa_sink_la_SOURCES = modules/alsa/module-alsa-sink.c
+ module_alsa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+Index: overlay/src/modules/droid/droid-sink.c
+===================================================================
+--- overlay.orig/src/modules/droid/droid-sink.c
++++ overlay/src/modules/droid/droid-sink.c
+@@ -1,6 +1,5 @@
+ /*
+ * Copyright (C) 2013 Jolla Ltd.
+- * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Contact: Juho Hämäläinen <juho.hamalainen at tieto.com>
+ *
+@@ -76,13 +75,14 @@ struct userdata {
+
+ pa_memblockq *memblockq;
+ pa_memchunk silence;
+- size_t buffer_count;
+ size_t buffer_size;
+- pa_usec_t buffer_latency;
+- pa_usec_t timestamp;
++ pa_usec_t buffer_time;
++ pa_usec_t write_time;
++ pa_usec_t write_threshold;
+
+ audio_devices_t primary_devices;
+ audio_devices_t extra_devices;
++ pa_hashmap *extra_devices_map;
+
+ bool use_hw_volume;
+ bool use_voice_volume;
+@@ -101,16 +101,15 @@ struct userdata {
+
+ pa_droid_card_data *card_data;
+ pa_droid_hw_module *hw_module;
+- struct audio_stream_out *stream_out;
+-
+- char *sco_fake_sink_name;
+- struct pa_sink *sco_fake_sink;
++ pa_droid_stream *stream;
+ };
+
+ enum {
+ SINK_MESSAGE_DO_ROUTING = PA_SINK_MESSAGE_MAX,
+ };
+
++#define PULSEAUDIO_VERSION_MAJOR 6
++
+ #define DEFAULT_MODULE_ID "primary"
+
+ /* sink properties */
+@@ -138,6 +137,7 @@ typedef struct droid_parameter_mapping {
+ #define DEFAULT_SCO_FAKE_SINK "sink.fake.sco"
+ #define HSP_PREVENT_SUSPEND_STR "bluetooth.hsp.prevent.suspend.transport"
+
++static void parameter_free(droid_parameter_mapping *m);
+ static void userdata_free(struct userdata *u);
+ static void set_voice_volume_from_input(struct userdata *u, pa_sink_input *i);
+ static struct pa_sink *pa_sco_fake_sink_discover(pa_core *core, const char *sink_name);
+@@ -149,67 +149,77 @@ static void set_primary_devices(struct u
+ u->primary_devices = devices;
+ }
+
+-static void add_extra_devices(struct userdata *u, audio_devices_t devices) {
+- pa_assert(u);
+- pa_assert(devices);
++static bool add_extra_devices(struct userdata *u, audio_devices_t devices) {
++ void *value;
++ uint32_t count;
++ bool need_update = false;
+
+- u->extra_devices |= devices;
+-}
+-
+-static void remove_extra_devices(struct userdata *u, audio_devices_t devices) {
+ pa_assert(u);
++ pa_assert(u->extra_devices_map);
+ pa_assert(devices);
+
+- u->extra_devices &= ~devices;
+-}
++ if ((value = pa_hashmap_get(u->extra_devices_map, PA_UINT_TO_PTR(devices)))) {
++ count = PA_PTR_TO_UINT(value);
++ count++;
++ pa_hashmap_remove(u->extra_devices_map, PA_UINT_TO_PTR(devices));
++ pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(count));
+
+-static void parameter_free(droid_parameter_mapping *m) {
+- pa_assert(m);
++ /* added extra device already exists in hashmap, so no need to update route. */
++ need_update = false;
++ } else {
++ pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(1));
++ u->extra_devices |= devices;
++ need_update = true;
++ }
+
+- pa_xfree(m->key);
+- pa_xfree(m->value);
+- pa_xfree(m);
++ return need_update;
+ }
+
+-static void set_fake_sco_sink_transport_property(struct userdata *u, const char *value) {
+- pa_proplist *pl;
++static bool remove_extra_devices(struct userdata *u, audio_devices_t devices) {
++ void *value;
++ uint32_t count;
++ bool need_update = false;
+
+ pa_assert(u);
+- pa_assert(value);
+- pa_assert(u->sco_fake_sink);
++ pa_assert(u->extra_devices_map);
++ pa_assert(devices);
+
+- pl = pa_proplist_new();
+- pa_proplist_sets(pl, HSP_PREVENT_SUSPEND_STR, value);
+- pa_sink_update_proplist(u->sco_fake_sink, PA_UPDATE_REPLACE, pl);
+- pa_proplist_free(pl);
++ if ((value = pa_hashmap_get(u->extra_devices_map, PA_UINT_TO_PTR(devices)))) {
++ pa_hashmap_remove(u->extra_devices_map, PA_UINT_TO_PTR(devices));
++ count = PA_PTR_TO_UINT(value);
++ count--;
++ if (count == 0) {
++ u->extra_devices &= ~devices;
++ need_update = true;
++ } else {
++ /* added extra devices still exists in hashmap, so no need to update route. */
++ pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(count));
++ need_update = false;
++ }
++ }
++
++ return need_update;
+ }
+
+ /* Called from main context during voice calls, and from IO context during media operation. */
+-static bool do_routing(struct userdata *u) {
++static void do_routing(struct userdata *u) {
+ audio_devices_t routing;
+- char tmp[32];
+
+ pa_assert(u);
+- pa_assert(u->stream_out);
++ pa_assert(u->stream);
+
+ routing = u->primary_devices | u->extra_devices;
+
+- pa_snprintf(tmp, sizeof(tmp), "%s=%u;", AUDIO_PARAMETER_STREAM_ROUTING, routing);
+- pa_log_debug("Routing: set_parameters(): %s (%#010x)", tmp, routing);
+- pa_droid_hw_module_lock(u->hw_module);
+- u->stream_out->common.set_parameters(&u->stream_out->common, tmp);
+- pa_droid_hw_module_unlock(u->hw_module);
+-
+- return true;
++ pa_droid_stream_set_output_route(u->stream, routing);
+ }
+
+ static bool parse_device_list(const char *str, audio_devices_t *dst) {
+- char *dev;
+- const char *state = NULL;
+-
+ pa_assert(str);
+ pa_assert(dst);
+
++ char *dev;
++ const char *state = NULL;
++
+ *dst = 0;
+
+ while ((dev = pa_split(str, "|", &state))) {
+@@ -235,15 +245,18 @@ static int thread_write_silence(struct u
+
+ /* Drop our rendered audio and write silence to HAL. */
+ pa_memblockq_drop(u->memblockq, u->buffer_size);
++ u->write_time = pa_rtclock_now();
+
+ /* We should be able to write everything in one go as long as memblock size
+ * is multiples of buffer_size. Even if we don't write whole buffer size
+ * here it's okay, as long as mute time isn't configured too strictly. */
+
+ p = pa_memblock_acquire(u->silence.memblock);
+- wrote = u->stream_out->write(u->stream_out, (const uint8_t*) p + u->silence.index, u->silence.length);
++ wrote = u->stream->out->write(u->stream->out, (const uint8_t*) p + u->silence.index, u->silence.length);
+ pa_memblock_release(u->silence.memblock);
+
++ u->write_time = pa_rtclock_now() - u->write_time;
++
+ if (wrote < 0)
+ return -1;
+
+@@ -260,14 +273,18 @@ static int thread_write(struct userdata
+ /* We should be able to write everything in one go as long as memblock size
+ * is multiples of buffer_size. */
+
++ u->write_time = pa_rtclock_now();
++
+ for (;;) {
+ p = pa_memblock_acquire(c.memblock);
+- wrote = u->stream_out->write(u->stream_out, (const uint8_t*) p + c.index, c.length);
++ wrote = u->stream->out->write(u->stream->out, (const uint8_t*) p + c.index, c.length);
+ pa_memblock_release(c.memblock);
+
+ if (wrote < 0) {
+ pa_memblockq_drop(u->memblockq, c.length);
+ pa_memblock_unref(c.memblock);
++ u->write_time = 0;
++ pa_log("failed to write stream (%d)", wrote);
+ return -1;
+ }
+
+@@ -283,6 +300,8 @@ static int thread_write(struct userdata
+ break;
+ }
+
++ u->write_time = pa_rtclock_now() - u->write_time;
++
+ return 0;
+ }
+ static void thread_render(struct userdata *u) {
+@@ -290,7 +309,7 @@ static void thread_render(struct userdat
+ size_t missing;
+
+ length = pa_memblockq_get_length(u->memblockq);
+- missing = u->buffer_size * u->buffer_count - length;
++ missing = u->buffer_size - length;
+
+ if (missing > 0) {
+ pa_memchunk c;
+@@ -352,22 +371,18 @@ static void thread_func(void *userdata)
+
+ pa_thread_mq_install(&u->thread_mq);
+
+- u->timestamp = 0;
+-
+ for (;;) {
+ int ret;
+
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+
+- u->timestamp = pa_rtclock_now();
+-
+ if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+ process_rewind(u);
+- else
+- thread_render(u);
+
+ if (pa_rtpoll_timer_elapsed(u->rtpoll)) {
+- pa_usec_t now, sleept;
++ pa_usec_t sleept = 0;
++
++ thread_render(u);
+
+ if (u->routing_counter == u->mute_routing_after) {
+ do_routing(u);
+@@ -378,12 +393,8 @@ static void thread_func(void *userdata)
+ } else
+ thread_write(u);
+
+- now = pa_rtclock_now();
+-
+- if (now - u->timestamp > u->buffer_latency / 2)
+- sleept = 0;
+- else
+- sleept = u->buffer_latency / 2 - (now - u->timestamp) ;
++ if (u->write_time > u->write_threshold)
++ sleept = u->buffer_time;
+
+ pa_rtpoll_set_timer_relative(u->rtpoll, sleept);
+ }
+@@ -391,7 +402,11 @@ static void thread_func(void *userdata)
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+ /* Sleep */
++#if (PULSEAUDIO_VERSION_MAJOR == 5)
++ if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
++#elif (PULSEAUDIO_VERSION_MAJOR == 6)
+ if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
++#endif
+ goto fail;
+
+ if (ret == 0)
+@@ -415,9 +430,9 @@ static int suspend(struct userdata *u) {
+
+ pa_assert(u);
+ pa_assert(u->sink);
+- pa_assert(u->stream_out);
++ pa_assert(u->stream->out);
+
+- ret = u->stream_out->common.standby(&u->stream_out->common);
++ ret = u->stream->out->common.standby(&u->stream->out->common);
+
+ if (ret == 0) {
+ pa_sink_set_max_request_within_thread(u->sink, 0);
+@@ -462,8 +477,8 @@ static int sink_process_msg(pa_msgobject
+ pa_usec_t r = 0;
+
+ /* HAL reports milliseconds */
+- if (u->stream_out)
+- r = u->stream_out->get_latency(u->stream_out) * PA_USEC_PER_MSEC * u->buffer_count;
++ if (u->stream->out)
++ r = u->stream->out->get_latency(u->stream->out) * PA_USEC_PER_MSEC;
+
+ *((pa_usec_t*) data) = r;
+
+@@ -487,7 +502,6 @@ static int sink_process_msg(pa_msgobject
+ /* Fall through */
+ case PA_SINK_RUNNING: {
+ int r;
+- u->timestamp = 0;
+
+ if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+ if ((r = unsuspend(u)) < 0)
+@@ -498,8 +512,13 @@ static int sink_process_msg(pa_msgobject
+ break;
+ }
+
++ case PA_SINK_UNLINKED: {
++ /* Suspending since some implementations do not want to free running stream. */
++ suspend(u);
++ break;
++ }
++
+ /* not needed */
+- case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
+ ;
+@@ -514,7 +533,6 @@ static int sink_process_msg(pa_msgobject
+ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
+ struct userdata *u = s->userdata;
+ pa_droid_port_data *data;
+- const char *sco_transport_enabled;
+
+ pa_assert(u);
+ pa_assert(p);
+@@ -532,24 +550,9 @@ static int sink_set_port_cb(pa_sink *s,
+ pa_log_debug("Sink set port %u", data->device);
+
+ set_primary_devices(u, data->device);
+-
+- /* See if the sco fake sink element is available (only when needed) */
+- if ((u->sco_fake_sink == NULL) && (data->device & AUDIO_DEVICE_OUT_ALL_SCO))
+- u->sco_fake_sink = pa_sco_fake_sink_discover(u->core, u->sco_fake_sink_name);
+-
+- /* Update the bluetooth hsp transport property before we do the routing */
+- if (u->sco_fake_sink) {
+- sco_transport_enabled = pa_proplist_gets(u->sco_fake_sink->proplist, HSP_PREVENT_SUSPEND_STR);
+- if (sco_transport_enabled && pa_streq(sco_transport_enabled, "true")) {
+- if (data->device & ~AUDIO_DEVICE_OUT_ALL_SCO)
+- set_fake_sco_sink_transport_property(u, "false");
+- } else if (data->device & AUDIO_DEVICE_OUT_ALL_SCO)
+- set_fake_sco_sink_transport_property(u, "true");
+- }
+-
+ /* If we are in voice call, sink is usually in suspended state and routing change can be applied immediately.
+- * When in media use cases, do the routing change in IO thread. */
+- if (u->use_voice_volume)
++ * When in media use cases, do the routing change in IO thread if we are currently in RUNNING or IDLE state. */
++ if (u->use_voice_volume || !PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)))
+ do_routing(u);
+ else {
+ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL);
+@@ -569,7 +572,7 @@ static void sink_set_volume_cb(pa_sink *
+ float val = pa_sw_volume_to_linear(r.values[0]);
+ pa_log_debug("Set hw volume %f", val);
+ pa_droid_hw_module_lock(u->hw_module);
+- if (u->stream_out->set_volume(u->stream_out, val, val) < 0)
++ if (u->stream->out->set_volume(u->stream->out, val, val) < 0)
+ pa_log_warn("Failed to set hw volume.");
+ pa_droid_hw_module_unlock(u->hw_module);
+ } else if (r.channels == 2) {
+@@ -578,7 +581,7 @@ static void sink_set_volume_cb(pa_sink *
+ val[i] = pa_sw_volume_to_linear(r.values[i]);
+ pa_log_debug("Set hw volume %f : %f", val[0], val[1]);
+ pa_droid_hw_module_lock(u->hw_module);
+- if (u->stream_out->set_volume(u->stream_out, val[0], val[1]) < 0)
++ if (u->stream->out->set_volume(u->stream->out, val[0], val[1]) < 0)
+ pa_log_warn("Failed to set hw volume.");
+ pa_droid_hw_module_unlock(u->hw_module);
+ }
+@@ -635,10 +638,9 @@ static void update_volumes(struct userda
+
+ /* set_volume returns 0 if hw volume control is implemented, < 0 otherwise. */
+ pa_droid_hw_module_lock(u->hw_module);
+- if (u->stream_out->set_volume) {
++ if (u->stream->out->set_volume) {
+ pa_log_debug("Probe hw volume support for %s", u->sink->name);
+- pa_log_debug("Stream out volume set to 1.0f, 1.0f");
+- ret = u->stream_out->set_volume(u->stream_out, 1.0f, 1.0f);
++ ret = u->stream->out->set_volume(u->stream->out, 1.0f, 1.0f);
+ }
+ pa_droid_hw_module_unlock(u->hw_module);
+
+@@ -808,6 +810,7 @@ void pa_droid_sink_set_voice_control(pa_
+
+ if (u->use_voice_volume) {
+ pa_log_debug("Using voice volume control for %s", u->sink->name);
++ pa_sink_set_set_volume_callback(u->sink, NULL);
+
+ if (u->voice_virtual_stream)
+ create_voice_virtual_stream(u);
+@@ -818,6 +821,7 @@ void pa_droid_sink_set_voice_control(pa_
+
+ /* First disable module-device-restore, as we don't want to save the voice volume
+ * as the default sink volume when restoring to the default mode */
++#define MODULE_DEVICE_RESTORE_SKIP_PROPERTY "module-device-restore.skip"
+ pa_proplist_sets(u->sink->proplist, MODULE_DEVICE_RESTORE_SKIP_PROPERTY, "true");
+
+ /* Then map normal sink volume changes to voice call volume changes */
+@@ -888,9 +892,9 @@ static pa_hook_result_t sink_input_put_h
+
+ pa_log_debug("Add extra route %s (%u).", dev_str, devices);
+
+- add_extra_devices(u, devices);
+- /* post routing change */
+- pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL);
++ /* if this device was not routed to previously post routing change */
++ if (add_extra_devices(u, devices))
++ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL);
+ }
+ }
+
+@@ -919,9 +923,9 @@ static pa_hook_result_t sink_input_unlin
+
+ pa_log_debug("Remove extra route %s (%u).", dev_str, devices);
+
+- remove_extra_devices(u, devices);
+- /* post routing change */
+- pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL);
++ /* if this device no longer exists in extra devices map post routing change */
++ if (remove_extra_devices(u, devices))
++ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL);
+ }
+ }
+
+@@ -973,10 +977,8 @@ static pa_hook_result_t sink_proplist_ch
+ if (changed) {
+ pa_assert(parameter);
+ tmp = pa_sprintf_malloc("%s=%s;", parameter->key, parameter->value);
+- pa_log_debug("sink proplist changed: set_parameters(): %s", tmp);
+- pa_droid_hw_module_lock(u->hw_module);
+- u->stream_out->common.set_parameters(&u->stream_out->common, tmp);
+- pa_droid_hw_module_unlock(u->hw_module);
++ pa_log_debug("set_parameters(): %s", tmp);
++ pa_droid_stream_set_parameters(u->stream, tmp);
+ pa_xfree(tmp);
+ }
+ }
+@@ -984,25 +986,6 @@ static pa_hook_result_t sink_proplist_ch
+ return PA_HOOK_OK;
+ }
+
+-static struct pa_sink *pa_sco_fake_sink_discover(pa_core *core, const char *sink_name) {
+- struct pa_sink *sink;
+- pa_idxset *idxset;
+- void *state = NULL;
+-
+- pa_assert(core);
+- pa_assert(sink_name);
+- pa_assert_se((idxset = core->sinks));
+-
+- while ((sink = pa_idxset_iterate(idxset, &state, NULL)) != NULL) {
+- if (pa_streq(sink_name, sink->name)) {
+- pa_log_debug("Found fake SCO sink '%s'", sink_name);
+- return sink;
+- }
+- }
+-
+- return NULL;
+-}
+-
+ pa_sink *pa_droid_sink_new(pa_module *m,
+ pa_modargs *ma,
+ const char *driver,
+@@ -1019,23 +1002,20 @@ pa_sink *pa_droid_sink_new(pa_module *m,
+ pa_sink_new_data data;
+ const char *module_id = NULL;
+ const char *tmp;
+- /* char *list = NULL; */
++ char *list = NULL;
+ uint32_t alternate_sample_rate;
+- uint32_t sample_rate;
++ const char *format;
+ audio_devices_t dev_out;
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+ bool namereg_fail = false;
+- uint32_t total_latency;
++ pa_usec_t latency;
+ pa_droid_config_audio *config = NULL; /* Only used when sink is created without card */
+ int32_t mute_routing_before = 0;
+ int32_t mute_routing_after = 0;
+ uint32_t sink_buffer = 0;
+ int ret;
+
+- audio_format_t hal_audio_format = 0;
+- audio_channel_mask_t hal_channel_mask = 0;
+-
+ pa_assert(m);
+ pa_assert(ma);
+ pa_assert(driver);
+@@ -1054,8 +1034,36 @@ pa_sink *pa_droid_sink_new(pa_module *m,
+ sample_spec = m->core->default_sample_spec;
+ channel_map = m->core->default_channel_map;
+
++ /* First parse both sample spec and channel map, then see if sink_* override some
++ * of the values. */
+ if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) {
+- pa_log("Failed to parse sample specification and channel map.");
++ pa_log("Failed to parse sink sample specification and channel map.");
++ goto fail;
++ }
++
++ if (pa_modargs_get_value(ma, "sink_channel_map", NULL)) {
++ if (pa_modargs_get_channel_map(ma, "sink_channel_map", &channel_map) < 0) {
++ pa_log("Failed to parse sink channel map.");
++ goto fail;
++ }
++
++ sample_spec.channels = channel_map.channels;
++ }
++
++ if ((format = pa_modargs_get_value(ma, "sink_format", NULL))) {
++ if ((sample_spec.format = pa_parse_sample_format(format)) < 0) {
++ pa_log("Failed to parse sink format.");
++ goto fail;
++ }
++ }
++
++ if (pa_modargs_get_value_u32(ma, "sink_rate", &sample_spec.rate) < 0) {
++ pa_log("Failed to parse sink samplerate");
++ goto fail;
++ }
++
++ if (!pa_sample_spec_valid(&sample_spec)) {
++ pa_log("Sample spec is not valid.");
+ goto fail;
+ }
+
+@@ -1097,12 +1105,13 @@ pa_sink *pa_droid_sink_new(pa_module *m,
+ u->deferred_volume = deferred_volume;
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+- u->parameters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) parameter_free);
++ u->parameters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
++ NULL, (pa_free_cb_t) parameter_free);
+ u->voice_volume_call_mode = voice_volume_call_mode;
+ u->voice_virtual_stream = voice_virtual_stream;
+ u->voice_property_key = pa_xstrdup(pa_modargs_get_value(ma, "voice_property_key", DEFAULT_VOICE_CONTROL_PROPERTY_KEY));
+ u->voice_property_value = pa_xstrdup(pa_modargs_get_value(ma, "voice_property_value", DEFAULT_VOICE_CONTROL_PROPERTY_VALUE));
+- u->sco_fake_sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sco_fake_sink", DEFAULT_SCO_FAKE_SINK));
++ u->extra_devices_map = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+ if (card_data) {
+ u->card_data = card_data;
+@@ -1110,40 +1119,26 @@ pa_sink *pa_droid_sink_new(pa_module *m,
+ pa_assert_se((u->hw_module = pa_droid_hw_module_get(u->core, NULL, card_data->module_id)));
+ } else {
+ /* Sink wasn't created from inside card module, so we'll need to open
+- * hw module ourselves.
+- * TODO some way to share hw module between other sinks/sources since
+- * opening same module from different places likely isn't a good thing. */
+-
+- if (!(config = pa_droid_config_load(ma)))
+- goto fail;
+-
+- /* Ownership of config transfers to hw_module if opening of hw module succeeds. */
+- if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id)))
+- goto fail;
+- }
+-
+- if (!pa_convert_format(sample_spec.format, CONV_FROM_PA, &hal_audio_format)) {
+- pa_log("Sample spec format %u not supported.", sample_spec.format);
+- goto fail;
+- }
+-
+- for (int i = 0; i < channel_map.channels; i++) {
+- audio_channel_mask_t c;
+- if (!pa_convert_output_channel(channel_map.map[i], CONV_FROM_PA, &c)) {
+- pa_log("Failed to convert channel map.");
+- goto fail;
++ * hw module ourself.
++ *
++ * First let's find out if hw module has already been opened, or if we need to
++ * do it ourself.
++ */
++ if (!(u->hw_module = pa_droid_hw_module_get(u->core, NULL, module_id))) {
++
++ /* No hw module object in shared object db, let's open the module now. */
++
++ if (!(config = pa_droid_config_load(ma)))
++ goto fail;
++
++ /* Ownership of config transfers to hw_module if opening of hw module succeeds. */
++ if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id)))
++ goto fail;
+ }
+- hal_channel_mask |= c;
+ }
+
+- struct audio_config config_out = {
+- .sample_rate = sample_spec.rate,
+- .channel_mask = hal_channel_mask,
+- .format = hal_audio_format
+- };
+-
+ /* Default routing */
+- dev_out = AUDIO_DEVICE_OUT_DEFAULT;
++ dev_out = u->hw_module->config->global_config.default_output_device;
+
+ if ((tmp = pa_modargs_get_value(ma, "output_devices", NULL))) {
+ audio_devices_t tmp_dev;
+@@ -1157,26 +1152,14 @@ pa_sink *pa_droid_sink_new(pa_module *m,
+ if (am)
+ flags = am->output->flags;
+
+- pa_droid_hw_module_lock(u->hw_module);
+- ret = u->hw_module->device->open_output_stream(u->hw_module->device,
+- u->hw_module->stream_out_id++,
+- dev_out,
+- flags,
+- &config_out,
+- &u->stream_out);
+- pa_droid_hw_module_unlock(u->hw_module);
++ u->stream = pa_droid_open_output_stream(u->hw_module, &sample_spec, &channel_map, flags, dev_out);
+
+- if (!u->stream_out) {
+- pa_log("Failed to open output stream. (errno %d)", ret);
++ if (!u->stream) {
++ pa_log("Failed to open output stream.");
+ goto fail;
+ }
+
+- if ((sample_rate = u->stream_out->common.get_sample_rate(&u->stream_out->common)) != sample_spec.rate) {
+- pa_log_warn("Requested sample rate %u but got %u instead.", sample_spec.rate, sample_rate);
+- sample_spec.rate = sample_rate;
+- }
+-
+- u->buffer_size = u->stream_out->common.get_buffer_size(&u->stream_out->common);
++ u->buffer_size = u->stream->out->common.get_buffer_size(&u->stream->out->common);
+ if (sink_buffer) {
+ if (sink_buffer < u->buffer_size)
+ pa_log_warn("Requested buffer size %u less than HAL reported buffer size (%u).", sink_buffer, u->buffer_size);
+@@ -1190,40 +1173,32 @@ pa_sink *pa_droid_sink_new(pa_module *m,
+ }
+ }
+
+- u->buffer_latency = pa_bytes_to_usec(u->buffer_size, &sample_spec);
+- /* Disable internal rewinding for now. */
+- u->buffer_count = 1;
+-
+- pa_log_info("Created Android stream with device: %u flags: %u sample rate: %u channel mask: %u format: %u buffer size: %u",
+- dev_out,
+- flags,
+- sample_rate,
+- config_out.channel_mask,
+- config_out.format,
+- u->buffer_size);
+-
++ u->buffer_time = pa_bytes_to_usec(u->buffer_size, &u->stream->sample_spec);
+
++ u->write_threshold = u->buffer_time - u->buffer_time / 6;
+ u->mute_routing_before = mute_routing_before / u->buffer_size;
+ u->mute_routing_after = mute_routing_after / u->buffer_size;
+ if (u->mute_routing_before == 0 && mute_routing_before)
+- u->mute_routing_before = u->buffer_size;
++ u->mute_routing_before = 1;
+ if (u->mute_routing_after == 0 && mute_routing_after)
+- u->mute_routing_after = u->buffer_size;
++ u->mute_routing_after = 1;
+ if (u->mute_routing_before || u->mute_routing_after)
+ pa_log_debug("Mute playback when routing is changing, %u before and %u after.",
+ u->mute_routing_before * u->buffer_size,
+ u->mute_routing_after * u->buffer_size);
+- pa_silence_memchunk_get(&u->core->silence_cache, u->core->mempool, &u->silence, &sample_spec, u->buffer_size);
+- u->memblockq = pa_memblockq_new("droid-sink", 0, u->buffer_size * u->buffer_count, u->buffer_size * u->buffer_count, &sample_spec, 1, 0, 0, &u->silence);
++ pa_silence_memchunk_get(&u->core->silence_cache, u->core->mempool, &u->silence, &u->stream->sample_spec, u->buffer_size);
++ u->memblockq = pa_memblockq_new("droid-sink", 0, u->buffer_size, u->buffer_size, &u->stream->sample_spec, 1, 0, 0, &u->silence);
+
+ pa_sink_new_data_init(&data);
+ data.driver = driver;
+ data.module = m;
+ data.card = card;
+
+- set_sink_name(ma, &data, module_id);
++ if (am)
++ set_sink_name(ma, &data, am->output->name);
++ else
++ set_sink_name(ma, &data, module_id);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "internal");
+
+ /* We need to give pa_modargs_get_value_boolean() a pointer to a local
+ * variable instead of using &data.namereg_fail directly, because
+@@ -1237,8 +1212,8 @@ pa_sink *pa_droid_sink_new(pa_module *m,
+ }
+ data.namereg_fail = namereg_fail;
+
+- pa_sink_new_data_set_sample_spec(&data, &sample_spec);
+- pa_sink_new_data_set_channel_map(&data, &channel_map);
++ pa_sink_new_data_set_sample_spec(&data, &u->stream->sample_spec);
++ pa_sink_new_data_set_channel_map(&data, &u->stream->channel_map);
+ pa_sink_new_data_set_alternate_sample_rate(&data, alternate_sample_rate);
+
+ /*
+@@ -1282,9 +1257,12 @@ pa_sink *pa_droid_sink_new(pa_module *m,
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+ /* Rewind internal memblockq */
+- pa_sink_set_max_rewind(u->sink, u->buffer_size * (u->buffer_count - 1));
++ pa_sink_set_max_rewind(u->sink, 0);
+
+- thread_name = pa_sprintf_malloc("droid-sink-%s", module_id);
++ if (am)
++ thread_name = pa_sprintf_malloc("droid-sink-%s", am->output->name);
++ else
++ thread_name = pa_sprintf_malloc("droid-sink-%s", module_id);
+ if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+@@ -1292,11 +1270,11 @@ pa_sink *pa_droid_sink_new(pa_module *m,
+ pa_xfree(thread_name);
+ thread_name = NULL;
+
+- /* Latency consists of HAL latency + our memblockq latency */
+- total_latency = u->stream_out->get_latency(u->stream_out) + (uint32_t) pa_bytes_to_usec(u->buffer_size * u->buffer_count, &sample_spec);
+- pa_sink_set_fixed_latency(u->sink, total_latency);
+- pa_log_debug("Set fixed latency %lu usec", (unsigned long) pa_bytes_to_usec(total_latency, &sample_spec));
+- pa_sink_set_max_request(u->sink, u->buffer_size * u->buffer_count);
++ /* HAL latencies are in milliseconds. */
++ latency = u->stream->out->get_latency(u->stream->out) * PA_USEC_PER_MSEC;
++ pa_sink_set_fixed_latency(u->sink, latency);
++ pa_log_debug("Set fixed latency %llu usec", latency);
++ pa_sink_set_max_request(u->sink, u->buffer_size);
+
+ if (u->sink->active_port)
+ sink_set_port_cb(u->sink, u->sink->active_port);
+@@ -1337,6 +1315,14 @@ void pa_droid_sink_free(pa_sink *s) {
+ userdata_free(u);
+ }
+
++static void parameter_free(droid_parameter_mapping *m) {
++ pa_assert(m);
++
++ pa_xfree(m->key);
++ pa_xfree(m->value);
++ pa_xfree(m);
++}
++
+ static void userdata_free(struct userdata *u) {
+
+ if (u->sink)
+@@ -1367,11 +1353,8 @@ static void userdata_free(struct userdat
+ if (u->parameters)
+ pa_hashmap_free(u->parameters);
+
+- if (u->hw_module && u->stream_out) {
+- pa_droid_hw_module_lock(u->hw_module);
+- u->hw_module->device->close_output_stream(u->hw_module->device, u->stream_out);
+- pa_droid_hw_module_unlock(u->hw_module);
+- }
++ if (u->stream)
++ pa_droid_stream_unref(u->stream);
+
+ if (u->memblockq)
+ pa_memblockq_free(u->memblockq);
+@@ -1382,13 +1365,13 @@ static void userdata_free(struct userdat
+ if (u->hw_module)
+ pa_droid_hw_module_unref(u->hw_module);
+
+- if (u->sco_fake_sink_name)
+- pa_xfree(u->sco_fake_sink_name);
+-
+ if (u->voice_property_key)
+ pa_xfree(u->voice_property_key);
+ if (u->voice_property_value)
+ pa_xfree(u->voice_property_value);
+
++ if (u->extra_devices_map)
++ pa_hashmap_free(u->extra_devices_map);
++
+ pa_xfree(u);
+ }
+Index: overlay/src/modules/droid/droid-source.c
+===================================================================
+--- overlay.orig/src/modules/droid/droid-source.c
++++ overlay/src/modules/droid/droid-source.c
+@@ -66,7 +66,6 @@ struct userdata {
+
+ pa_memchunk memchunk;
+ audio_devices_t primary_devices;
+- audio_devices_t enabled_devices;
+ bool routing_changes_enabled;
+
+ size_t buffer_size;
+@@ -74,58 +73,65 @@ struct userdata {
+
+ pa_droid_card_data *card_data;
+ pa_droid_hw_module *hw_module;
+- audio_stream_in_t *stream;
++ pa_droid_stream *stream;
+ };
+
++#define PULSEAUDIO_VERSION_MAJOR 6
++
+ #define DEFAULT_MODULE_ID "primary"
+
++#define DROID_AUDIO_SOURCE "droid.audio_source"
++#define DROID_AUDIO_SOURCE_UNDEFINED "undefined"
++
+ static void userdata_free(struct userdata *u);
+
+-static bool do_routing(struct userdata *u, audio_devices_t devices) {
+- char tmp[32];
+- char *devlist;
++static int do_routing(struct userdata *u, audio_devices_t devices, bool force) {
++ int ret;
++ pa_proplist *p;
++ const char *source_str;
++ audio_devices_t old_device;
++ audio_source_t source;
+
+ pa_assert(u);
+ pa_assert(u->stream);
+
+- if (!u->routing_changes_enabled) {
++ if (!force && !u->routing_changes_enabled) {
+ pa_log_debug("Skipping routing change.");
+- return false;
++ return 0;
+ }
+
+ if (u->primary_devices == devices)
+ pa_log_debug("Refresh active device routing.");
+
+- u->enabled_devices &= ~u->primary_devices;
++ old_device = u->primary_devices;
+ u->primary_devices = devices;
+- u->enabled_devices |= u->primary_devices;
+
+- devlist = pa_list_string_input_device(devices);
+- pa_assert(devlist);
+-#ifdef DROID_DEVICE_I9305
+- pa_snprintf(tmp, sizeof(tmp), "%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, devices & ~AUDIO_DEVICE_BIT_IN);
+-#else
+- pa_snprintf(tmp, sizeof(tmp), "%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, devices);
+-#endif
+- pa_log_debug("set_parameters(): %s (%s : %#010x)", tmp, devlist, devices);
+- pa_xfree(devlist);
+-#ifdef DROID_DEVICE_MAKO
+-#warning Using mako set_parameters hack.
+- u->card_data->set_parameters(u->card_data, tmp);
+-#else
+- u->stream->common.set_parameters(&u->stream->common, tmp);
+-#endif
++ ret = pa_droid_stream_set_input_route(u->stream, devices, &source);
+
+- return true;
++ if (ret < 0)
++ u->primary_devices = old_device;
++ else {
++ if (source != (uint32_t) -1)
++ pa_assert_se(pa_droid_audio_source_name(source, &source_str));
++ else
++ source_str = DROID_AUDIO_SOURCE_UNDEFINED;
++
++ p = pa_proplist_new();
++ pa_proplist_sets(p, DROID_AUDIO_SOURCE, source_str);
++ pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, p);
++ pa_proplist_free(p);
++ }
++
++ return ret;
+ }
+
+ static bool parse_device_list(const char *str, audio_devices_t *dst) {
+- char *dev;
+- const char *state = NULL;
+-
+ pa_assert(str);
+ pa_assert(dst);
+
++ char *dev;
++ const char *state = NULL;
++
+ *dst = 0;
+
+ while ((dev = pa_split(str, "|", &state))) {
+@@ -153,7 +159,7 @@ static int thread_read(struct userdata *
+ chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) u->buffer_size);
+
+ p = pa_memblock_acquire(chunk.memblock);
+- readd = u->stream->read(u->stream, (uint8_t*) p, pa_memblock_get_length(chunk.memblock));
++ readd = u->stream->in->read(u->stream->in, (uint8_t*) p, pa_memblock_get_length(chunk.memblock));
+ pa_memblock_release(chunk.memblock);
+
+ if (readd < 0) {
+@@ -179,6 +185,7 @@ static void thread_func(void *userdata)
+ struct userdata *u = userdata;
+
+ pa_assert(u);
++ pa_assert(u->stream);
+
+ pa_log_debug("Thread starting up.");
+
+@@ -189,6 +196,8 @@ static void thread_func(void *userdata)
+
+ u->timestamp = pa_rtclock_now();
+
++ u->stream->in->common.standby(&u->stream->in->common);
++
+ for (;;) {
+ int ret;
+
+@@ -200,7 +209,11 @@ static void thread_func(void *userdata)
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+ /* Sleep */
++#if (PULSEAUDIO_VERSION_MAJOR == 5)
++ if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
++#elif (PULSEAUDIO_VERSION_MAJOR == 6)
+ if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
++#endif
+ goto fail;
+
+ if (ret == 0)
+@@ -225,7 +238,7 @@ static int suspend(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->stream);
+
+- ret = u->stream->common.standby(&u->stream->common);
++ ret = u->stream->in->common.standby(&u->stream->in->common);
+
+ if (ret == 0)
+ pa_log_info("Device suspended.");
+@@ -259,8 +272,13 @@ static int source_process_msg(pa_msgobje
+ break;
+ }
+
++ case PA_SOURCE_UNLINKED: {
++ /* Suspending since some implementations do not want to free running stream. */
++ suspend(u);
++ break;
++ }
++
+ /* not needed */
+- case PA_SOURCE_UNLINKED:
+ case PA_SOURCE_INIT:
+ case PA_SOURCE_INVALID_STATE:
+ ;
+@@ -272,7 +290,7 @@ static int source_process_msg(pa_msgobje
+ return pa_source_process_msg(o, code, data, offset, chunk);
+ }
+
+-static int source_set_port_cb(pa_source *s, pa_device_port *p) {
++static int droid_source_set_port(pa_source *s, pa_device_port *p, bool force) {
+ struct userdata *u = s->userdata;
+ pa_droid_port_data *data;
+
+@@ -291,11 +309,34 @@ static int source_set_port_cb(pa_source
+
+ pa_log_debug("Source set port %u", data->device);
+
+- do_routing(u, data->device);
++ return do_routing(u, data->device, force);
++}
+
+- return 0;
++int pa_droid_source_set_port(pa_source *s, pa_device_port *p) {
++ return droid_source_set_port(s, p, true);
+ }
+
++static int source_set_port_cb(pa_source *s, pa_device_port *p) {
++ return droid_source_set_port(s, p, false);
++}
++
++static void source_set_voicecall_source_port(struct userdata *u) {
++ pa_device_port *port;
++ pa_droid_port_data *data;
++ void *state;
++
++ pa_assert(u);
++ pa_assert(u->source);
++
++ PA_HASHMAP_FOREACH(port, u->source->ports, state) {
++ data = PA_DEVICE_PORT_DATA(port);
++
++ if (data->device & AUDIO_DEVICE_IN_VOICE_CALL) {
++ pa_source_set_port(u->source, port->name, false);
++ break;
++ }
++ }
++}
+
+ static void source_set_name(pa_modargs *ma, pa_source_new_data *data, const char *module_id) {
+ const char *tmp;
+@@ -318,8 +359,13 @@ static void source_set_name(pa_modargs *
+ }
+ }
+
++#if (PULSEAUDIO_VERSION_MAJOR == 5)
+ static void source_get_mute_cb(pa_source *s) {
++#elif (PULSEAUDIO_VERSION_MAJOR == 6)
++static int source_get_mute_cb(pa_source *s, bool *muted) {
++#endif
+ struct userdata *u = s->userdata;
++ int ret = 0;
+ bool b;
+
+ pa_assert(u);
+@@ -328,12 +374,19 @@ static void source_get_mute_cb(pa_source
+ pa_droid_hw_module_lock(u->hw_module);
+ if (u->hw_module->device->get_mic_mute(u->hw_module->device, &b) < 0) {
+ pa_log("Failed to get mute state.");
+- pa_droid_hw_module_unlock(u->hw_module);
+- return;
++ ret = -1;
+ }
+ pa_droid_hw_module_unlock(u->hw_module);
+
+- s->muted = b;
++#if (PULSEAUDIO_VERSION_MAJOR == 5)
++ if (ret == 0)
++ s->muted = b;
++#elif (PULSEAUDIO_VERSION_MAJOR == 6)
++ if (ret == 0)
++ *muted = b;
++
++ return ret;
++#endif
+ }
+
+ static void source_set_mute_cb(pa_source *s) {
+@@ -377,6 +430,7 @@ void pa_droid_source_set_routing(pa_sour
+ pa_source *pa_droid_source_new(pa_module *m,
+ pa_modargs *ma,
+ const char *driver,
++ audio_devices_t device,
+ pa_droid_card_data *card_data,
+ pa_droid_mapping *am,
+ pa_card *card) {
+@@ -385,27 +439,24 @@ pa_source *pa_droid_source_new(pa_module
+ char *thread_name = NULL;
+ pa_source_new_data data;
+ const char *module_id = NULL;
+- /* const char *tmp; */
++ const char *tmp;
+ uint32_t sample_rate;
+ uint32_t alternate_sample_rate;
+ audio_devices_t dev_in;
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
++ const char *format;
+ bool namereg_fail = false;
+ pa_droid_config_audio *config = NULL; /* Only used when source is created without card */
+ uint32_t source_buffer = 0;
+- char audio_source[32];
+- int ret;
+-
+- audio_format_t hal_audio_format = 0;
+- audio_channel_mask_t hal_channel_mask = 0;
++ bool voicecall_source = false;
+
+ pa_assert(m);
+ pa_assert(ma);
+ pa_assert(driver);
+
+ /* When running under card use hw module name for source by default. */
+- if (card && ma)
++ if (am)
+ module_id = am->input->module->name;
+ else
+ module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID);
+@@ -413,8 +464,41 @@ pa_source *pa_droid_source_new(pa_module
+ sample_spec = m->core->default_sample_spec;
+ channel_map = m->core->default_channel_map;
+
++ if (device & AUDIO_DEVICE_IN_VOICE_CALL) {
++ pa_log_info("Enabling voice call record source. Most module arguments are overridden.");
++ voicecall_source = true;
++ }
++
++ /* First parse both sample spec and channel map, then see if source_* override some
++ * of the values. */
+ if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) {
+- pa_log("Failed to parse sample specification and channel map.");
++ pa_log("Failed to parse source sample specification and channel map.");
++ goto fail;
++ }
++
++ if (pa_modargs_get_value(ma, "source_channel_map", NULL)) {
++ if (pa_modargs_get_channel_map(ma, "source_channel_map", &channel_map) < 0) {
++ pa_log("Failed to parse source channel map.");
++ goto fail;
++ }
++
++ sample_spec.channels = channel_map.channels;
++ }
++
++ if ((format = pa_modargs_get_value(ma, "source_format", NULL))) {
++ if ((sample_spec.format = pa_parse_sample_format(format)) < 0) {
++ pa_log("Failed to parse source format.");
++ goto fail;
++ }
++ }
++
++ if (pa_modargs_get_value_u32(ma, "source_rate", &sample_spec.rate) < 0) {
++ pa_log("Failed to parse source_rate.");
++ goto fail;
++ }
++
++ if (!pa_sample_spec_valid(&sample_spec)) {
++ pa_log("Sample spec is not valid.");
+ goto fail;
+ }
+
+@@ -436,8 +520,8 @@ pa_source *pa_droid_source_new(pa_module
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+- /* Enabled routing changes by default. */
+- u->routing_changes_enabled = true;
++ /* Enabled routing changes by default, except for voicecall source. */
++ u->routing_changes_enabled = voicecall_source ? false : true;
+
+ if (card_data) {
+ pa_assert(card);
+@@ -446,83 +530,44 @@ pa_source *pa_droid_source_new(pa_module
+ } else {
+ /* Stand-alone source */
+
+- if (!(config = pa_droid_config_load(ma)))
+- goto fail;
+-
+- /* Ownership of config transfers to hw_module if opening of hw module succeeds. */
+- if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id)))
+- goto fail;
++ if (!(u->hw_module = pa_droid_hw_module_get(u->core, NULL, module_id))) {
++ if (!(config = pa_droid_config_load(ma)))
++ goto fail;
++
++ /* Ownership of config transfers to hw_module if opening of hw module succeeds. */
++ if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id)))
++ goto fail;
++ }
+ }
+
+- if (!pa_convert_format(sample_spec.format, CONV_FROM_PA, &hal_audio_format)) {
+- pa_log("Sample spec format %u not supported.", sample_spec.format);
+- goto fail;
+- }
++ /* Default routing */
++ if (device)
++ dev_in = device;
++ else {
++ /* FIXME So while setting routing through stream with HALv2 API fails, creation of stream
++ * requires HALv2 style device to work properly. So until that oddity is resolved we always
++ * set AUDIO_DEVICE_IN_BUILTIN_MIC as initial device here. */
++ pa_log_info("FIXME: Setting AUDIO_DEVICE_IN_BUILTIN_MIC as initial device.");
++ pa_assert_se(pa_string_convert_input_device_str_to_num("AUDIO_DEVICE_IN_BUILTIN_MIC", &dev_in));
+
+- for (int i = 0; i < channel_map.channels; i++) {
+- audio_channel_mask_t c;
+- if (!pa_convert_input_channel(channel_map.map[i], CONV_FROM_PA, &c)) {
+- pa_log("Failed to convert channel map.");
+- goto fail;
+- }
+- hal_channel_mask |= c;
+- }
++ if ((tmp = pa_modargs_get_value(ma, "input_devices", NULL))) {
++ audio_devices_t tmp_dev;
+
+- struct audio_config config_in = {
+- .sample_rate = sample_spec.rate,
+- .channel_mask = hal_channel_mask,
+- .format = hal_audio_format
+- };
++ if (parse_device_list(tmp, &tmp_dev) && tmp_dev)
++ dev_in = tmp_dev;
+
+- /* Default routing */
+- /* FIXME So while setting routing through stream with HALv2 API fails, creation of stream
+- * requires HALv2 style device to work properly. So until that oddity is resolved we always
+- * set AUDIO_DEVICE_IN_BUILTIN_MIC as initial device here. */
+-#if 0
+- pa_assert_se(pa_string_convert_input_device_str_to_num("AUDIO_DEVICE_IN_BUILTIN_MIC", &dev_in));
+-
+- if ((tmp = pa_modargs_get_value(ma, "input_devices", NULL))) {
+- audio_devices_t tmp_dev;
+-
+- if (parse_device_list(tmp, &tmp_dev) && tmp_dev)
+- dev_in = tmp_dev;
+-
+- pa_log_debug("Set initial devices %s", tmp);
+- }
+-#else
+- pa_log_info("FIXME: Setting AUDIO_DEVICE_IN_BUILTIN_MIC as initial device.");
+- dev_in = AUDIO_DEVICE_IN_BUILTIN_MIC;
+-#endif
+- pa_droid_hw_module_lock(u->hw_module);
+- ret = u->hw_module->device->open_input_stream(u->hw_module->device,
+- u->hw_module->stream_in_id,
+- dev_in,
+- &config_in,
+- &u->stream);
+- /* On some devices the first call will fail if the config parameters are
+- * not supported, but it'll automatically set the right ones, expecting
+- * the caller to call it again, so let's try at least one more time */
+- if (!u->stream)
+- ret = u->hw_module->device->open_input_stream(u->hw_module->device,
+- u->hw_module->stream_in_id,
+- dev_in,
+- &config_in,
+- &u->stream);
++ pa_log_debug("Set initial devices %s", tmp);
++ }
++ }
+
+- u->hw_module->stream_in_id++;
+- pa_droid_hw_module_unlock(u->hw_module);
++ u->stream = pa_droid_open_input_stream(u->hw_module, &sample_spec, &channel_map, dev_in);
+
+- if (ret < 0) {
++ if (!u->stream) {
+ pa_log("Failed to open input stream.");
+ goto fail;
+ }
+
+- if ((sample_rate = u->stream->common.get_sample_rate(&u->stream->common)) != sample_spec.rate) {
+- pa_log_warn("Requested sample rate %u but got %u instead.", sample_spec.rate, sample_rate);
+- sample_spec.rate = sample_rate;
+- }
+-
+- u->buffer_size = u->stream->common.get_buffer_size(&u->stream->common);
++ u->buffer_size = u->stream->in->common.get_buffer_size(&u->stream->in->common);
+ if (source_buffer) {
+ if (source_buffer < u->buffer_size)
+ pa_log_warn("Requested buffer size %u less than HAL reported buffer size (%u).", source_buffer, u->buffer_size);
+@@ -536,18 +581,6 @@ pa_source *pa_droid_source_new(pa_module
+ }
+ }
+
+- pa_log_info("Created Android stream with device: %u sample rate: %u channel mask: %u format: %u buffer size: %u",
+- dev_in,
+- sample_rate,
+- config_in.channel_mask,
+- config_in.format,
+- u->buffer_size);
+-
+- /* Setting audio source to MIC by default */
+- pa_snprintf(audio_source, sizeof(audio_source), "%s=%u", AUDIO_PARAMETER_STREAM_INPUT_SOURCE, AUDIO_SOURCE_MIC);
+- u->stream->common.set_parameters(&u->stream->common, audio_source);
+- pa_log_debug("Setting audio source to AUDIO_SOURCE_MIC by default");
+-
+ pa_source_new_data_init(&data);
+ data.driver = driver;
+ data.module = m;
+@@ -567,11 +600,11 @@ pa_source *pa_droid_source_new(pa_module
+ }
+ data.namereg_fail = namereg_fail;
+
+- pa_source_new_data_set_sample_spec(&data, &sample_spec);
+- pa_source_new_data_set_channel_map(&data, &channel_map);
++ pa_source_new_data_set_sample_spec(&data, &u->stream->sample_spec);
++ pa_source_new_data_set_channel_map(&data, &u->stream->channel_map);
+ pa_source_new_data_set_alternate_sample_rate(&data, alternate_sample_rate);
+
+- if (am)
++ if (am && card)
+ pa_droid_add_ports(data.ports, am, card);
+
+ u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE);
+@@ -604,12 +637,15 @@ pa_source *pa_droid_source_new(pa_module
+ pa_xfree(thread_name);
+ thread_name = NULL;
+
+- pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &sample_spec));
+- pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, &sample_spec));
++ pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &u->stream->sample_spec));
++ pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, &u->stream->sample_spec));
+
+- if (u->source->active_port)
++ if (!voicecall_source && u->source->active_port)
+ source_set_port_cb(u->source, u->source->active_port);
+
++ if (voicecall_source)
++ source_set_voicecall_source_port(u);
++
+ pa_source_put(u->source);
+
+ return u->source;
+@@ -653,11 +689,8 @@ static void userdata_free(struct userdat
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+
+- if (u->hw_module && u->stream) {
+- pa_droid_hw_module_lock(u->hw_module);
+- u->hw_module->device->close_input_stream(u->hw_module->device, u->stream);
+- pa_droid_hw_module_unlock(u->hw_module);
+- }
++ if (u->stream)
++ pa_droid_stream_unref(u->stream);
+
+ // Stand alone source
+ if (u->hw_module)
+Index: overlay/src/modules/droid/droid-source.h
+===================================================================
+--- overlay.orig/src/modules/droid/droid-source.h
++++ overlay/src/modules/droid/droid-source.h
+@@ -43,14 +43,17 @@
+
+ #include "droid-util.h"
+
++/* If device is non-zero, it will override whatever is set in modargs for input device. */
+ pa_source *pa_droid_source_new(pa_module *m,
+ pa_modargs *ma,
+ const char *driver,
++ audio_devices_t device,
+ pa_droid_card_data *card_data,
+ pa_droid_mapping *am,
+ pa_card *card);
+ void pa_droid_source_free(pa_source *s);
+
+ void pa_droid_source_set_routing(pa_source *s, bool enabled);
++int pa_droid_source_set_port(pa_source *s, pa_device_port *p);
+
+ #endif
+Index: overlay/src/modules/droid/droid-util-42.h
+===================================================================
+--- overlay.orig/src/modules/droid/droid-util-42.h
++++ overlay/src/modules/droid/droid-util-42.h
+@@ -22,7 +22,10 @@
+ #ifndef _ANDROID_UTIL_V42_H_
+ #define _ANDROID_UTIL_V42_H_
+
+-#define HAL_V2
++#define DROID_HAL 2
++
++#include <hardware/audio.h>
++#include <hardware_legacy/audio_policy_conf.h>
+
+ // PulseAudio value - Android value
+
+Index: overlay/src/modules/droid/droid-util-44.h
+===================================================================
+--- /dev/null
++++ overlay/src/modules/droid/droid-util-44.h
+@@ -0,0 +1,349 @@
++/*
++ * Copyright (C) 2013 Jolla Ltd.
++ *
++ * Contact: Juho Hämäläinen <juho.hamalainen at tieto.com>
++ *
++ * These PulseAudio Modules are free software; you can redistribute
++ * it and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation
++ * version 2.1 of the License.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
++ * USA.
++ */
++
++#ifndef _ANDROID_UTIL_V44_H_
++#define _ANDROID_UTIL_V44_H_
++
++#define DROID_HAL 2
++
++// Android v4.4 has SPEAKER_DRC_ENABLED_TAG, so might the future versions
++#define DROID_HAVE_DRC
++
++#include <hardware/audio.h>
++#include <hardware_legacy/audio_policy_conf.h>
++
++// PulseAudio value - Android value
++
++uint32_t conversion_table_output_channel[][2] = {
++ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_OUT_MONO },
++ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_OUT_FRONT_LEFT },
++ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_OUT_FRONT_RIGHT},
++ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_OUT_FRONT_CENTER },
++ { PA_CHANNEL_POSITION_SUBWOOFER, AUDIO_CHANNEL_OUT_LOW_FREQUENCY },
++ { PA_CHANNEL_POSITION_REAR_LEFT, AUDIO_CHANNEL_OUT_BACK_LEFT },
++ { PA_CHANNEL_POSITION_REAR_RIGHT, AUDIO_CHANNEL_OUT_BACK_RIGHT },
++ { PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER },
++ { PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER },
++ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_OUT_BACK_CENTER },
++ { PA_CHANNEL_POSITION_SIDE_LEFT, AUDIO_CHANNEL_OUT_SIDE_LEFT },
++ { PA_CHANNEL_POSITION_SIDE_RIGHT, AUDIO_CHANNEL_OUT_SIDE_RIGHT },
++ { PA_CHANNEL_POSITION_TOP_CENTER, AUDIO_CHANNEL_OUT_TOP_CENTER },
++ { PA_CHANNEL_POSITION_TOP_FRONT_LEFT, AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT },
++ { PA_CHANNEL_POSITION_TOP_FRONT_CENTER, AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER },
++ { PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT },
++ { PA_CHANNEL_POSITION_TOP_REAR_LEFT, AUDIO_CHANNEL_OUT_TOP_BACK_LEFT },
++ { PA_CHANNEL_POSITION_TOP_REAR_CENTER, AUDIO_CHANNEL_OUT_TOP_BACK_CENTER },
++ { PA_CHANNEL_POSITION_TOP_REAR_RIGHT, AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT }
++};
++
++uint32_t conversion_table_input_channel[][2] = {
++ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_MONO },
++ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_IN_LEFT },
++ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_IN_RIGHT},
++ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_IN_FRONT },
++ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_IN_BACK },
++ /* Following are missing suitable counterparts on PulseAudio side. */
++ { AUDIO_CHANNEL_IN_LEFT_PROCESSED, AUDIO_CHANNEL_IN_LEFT_PROCESSED },
++ { AUDIO_CHANNEL_IN_RIGHT_PROCESSED, AUDIO_CHANNEL_IN_RIGHT_PROCESSED },
++ { AUDIO_CHANNEL_IN_FRONT_PROCESSED, AUDIO_CHANNEL_IN_FRONT_PROCESSED },
++ { AUDIO_CHANNEL_IN_BACK_PROCESSED, AUDIO_CHANNEL_IN_BACK_PROCESSED },
++ { AUDIO_CHANNEL_IN_PRESSURE, AUDIO_CHANNEL_IN_PRESSURE },
++ { AUDIO_CHANNEL_IN_X_AXIS, AUDIO_CHANNEL_IN_X_AXIS },
++ { AUDIO_CHANNEL_IN_Y_AXIS, AUDIO_CHANNEL_IN_Y_AXIS },
++ { AUDIO_CHANNEL_IN_Z_AXIS, AUDIO_CHANNEL_IN_Z_AXIS },
++ { AUDIO_CHANNEL_IN_VOICE_UPLINK, AUDIO_CHANNEL_IN_VOICE_UPLINK },
++ { AUDIO_CHANNEL_IN_VOICE_DNLINK, AUDIO_CHANNEL_IN_VOICE_DNLINK }
++};
++
++uint32_t conversion_table_format[][2] = {
++ { PA_SAMPLE_U8, AUDIO_FORMAT_PCM_8_BIT },
++ { PA_SAMPLE_S16LE, AUDIO_FORMAT_PCM_16_BIT },
++ { PA_SAMPLE_S32LE, AUDIO_FORMAT_PCM_32_BIT },
++ { PA_SAMPLE_S24LE, AUDIO_FORMAT_PCM_8_24_BIT }
++};
++
++uint32_t conversion_table_default_audio_source[][2] = {
++#ifdef DROID_DEVICE_HAMMERHEAD
++ { AUDIO_DEVICE_IN_COMMUNICATION, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_AMBIENT, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_BUILTIN_MIC, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_AUX_DIGITAL, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_VOICE_CALL, AUDIO_SOURCE_VOICE_CALL },
++ { AUDIO_DEVICE_IN_BACK_MIC, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_SOURCE_REMOTE_SUBMIX },
++ { AUDIO_DEVICE_IN_ANC_HEADSET, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_FM_RX, AUDIO_SOURCE_FM_RX },
++ { AUDIO_DEVICE_IN_FM_RX_A2DP, AUDIO_SOURCE_FM_RX_A2DP },
++#endif
++ { AUDIO_DEVICE_IN_ALL, AUDIO_SOURCE_DEFAULT }
++};
++
++struct string_conversion {
++ uint32_t value;
++ const char *str;
++};
++
++#if defined(STRING_ENTRY)
++#error STRING_ENTRY already defined somewhere, fix this lib.
++#endif
++#define STRING_ENTRY(str) { str, #str }
++/* Output devices */
++struct string_conversion string_conversion_table_output_device[] = {
++ STRING_ENTRY(AUDIO_DEVICE_OUT_EARPIECE),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_SPEAKER),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADPHONE),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_AUX_DIGITAL),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_ACCESSORY),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_DEVICE),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_REMOTE_SUBMIX),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_DEFAULT),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_A2DP),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_SCO),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_USB),
++#ifdef QCOM_HARDWARE
++ STRING_ENTRY(AUDIO_DEVICE_OUT_FM),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_FM_TX),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ANC_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ANC_HEADPHONE),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_PROXY),
++#endif
++ { 0, NULL }
++};
++
++struct string_conversion string_conversion_table_output_device_fancy[] = {
++ { AUDIO_DEVICE_OUT_EARPIECE, "output-earpiece" },
++ { AUDIO_DEVICE_OUT_SPEAKER, "output-speaker" },
++ { AUDIO_DEVICE_OUT_SPEAKER
++ | AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-speaker+wired_headphone" },
++ { AUDIO_DEVICE_OUT_WIRED_HEADSET, "output-wired_headset" },
++ { AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-wired_headphone" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO, "output-bluetooth_sco" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, "output-sco_headset" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, "output-sco_carkit" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, "output-a2dp" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, "output-a2dp_headphones" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, "output-a2dp_speaker" },
++ { AUDIO_DEVICE_OUT_AUX_DIGITAL, "output-aux_digital" },
++ { AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, "output-analog_dock_headset" },
++ { AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, "output-digital_dock_headset" },
++ { AUDIO_DEVICE_OUT_USB_ACCESSORY, "output-usb_accessory" },
++ { AUDIO_DEVICE_OUT_USB_DEVICE, "output-usb_device" },
++ { AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "output-remote_submix" },
++#ifdef QCOM_HARDWARE
++ { AUDIO_DEVICE_OUT_FM, "output-fm" },
++ { AUDIO_DEVICE_OUT_FM_TX, "output-fm_tx" },
++ { AUDIO_DEVICE_OUT_ANC_HEADSET, "output-anc_headset" },
++ { AUDIO_DEVICE_OUT_ANC_HEADPHONE, "output-anc_headphone" },
++ { AUDIO_DEVICE_OUT_PROXY, "output-proxy" },
++#endif
++ { 0, NULL }
++};
++
++/* Input devices */
++struct string_conversion string_conversion_table_input_device[] = {
++ STRING_ENTRY(AUDIO_DEVICE_IN_COMMUNICATION),
++ STRING_ENTRY(AUDIO_DEVICE_IN_AMBIENT),
++ STRING_ENTRY(AUDIO_DEVICE_IN_BUILTIN_MIC),
++ STRING_ENTRY(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_IN_WIRED_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_IN_AUX_DIGITAL),
++ STRING_ENTRY(AUDIO_DEVICE_IN_VOICE_CALL),
++ STRING_ENTRY(AUDIO_DEVICE_IN_BACK_MIC),
++ STRING_ENTRY(AUDIO_DEVICE_IN_REMOTE_SUBMIX),
++ STRING_ENTRY(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_IN_USB_ACCESSORY),
++ STRING_ENTRY(AUDIO_DEVICE_IN_USB_DEVICE),
++#ifdef QCOM_HARDWARE
++ STRING_ENTRY(AUDIO_DEVICE_IN_ANC_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX),
++ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX_A2DP),
++#endif
++ STRING_ENTRY(AUDIO_DEVICE_IN_DEFAULT),
++ /* Combination entries consisting of multiple devices defined above.
++ * These don't require counterpart in string_conversion_table_input_device_fancy. */
++ STRING_ENTRY(AUDIO_DEVICE_IN_ALL),
++ STRING_ENTRY(AUDIO_DEVICE_IN_ALL_SCO),
++ { 0, NULL }
++};
++
++struct string_conversion string_conversion_table_input_device_fancy[] = {
++ { AUDIO_DEVICE_IN_COMMUNICATION, "input-communication" },
++ { AUDIO_DEVICE_IN_AMBIENT, "input-ambient" },
++ { AUDIO_DEVICE_IN_BUILTIN_MIC, "input-builtin_mic" },
++ { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, "input-bluetooth_sco_headset" },
++ { AUDIO_DEVICE_IN_WIRED_HEADSET, "input-wired_headset" },
++ { AUDIO_DEVICE_IN_AUX_DIGITAL, "input-aux_digital" },
++ { AUDIO_DEVICE_IN_VOICE_CALL, "input-voice_call" },
++ { AUDIO_DEVICE_IN_BACK_MIC, "input-back_mic" },
++ { AUDIO_DEVICE_IN_REMOTE_SUBMIX, "input-remote_submix" },
++ { AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET, "input-analog_dock_headset" },
++ { AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET, "input-digital_dock_headset" },
++ { AUDIO_DEVICE_IN_USB_ACCESSORY, "input-usb_accessory" },
++ { AUDIO_DEVICE_IN_USB_DEVICE, "input-usb_device" },
++#ifdef QCOM_HARDWARE
++ { AUDIO_DEVICE_IN_ANC_HEADSET, "input-anc_headset" },
++ { AUDIO_DEVICE_IN_FM_RX, "input-fm_rx" },
++ { AUDIO_DEVICE_IN_FM_RX_A2DP, "input-fm_rx_a2dp" },
++#endif
++ { AUDIO_DEVICE_IN_DEFAULT, "input-default" },
++ { 0, NULL }
++};
++
++struct string_conversion string_conversion_table_audio_source_fancy[] = {
++ { AUDIO_SOURCE_DEFAULT, "default" },
++ { AUDIO_SOURCE_MIC, "mic" },
++ { AUDIO_SOURCE_VOICE_UPLINK, "voice uplink" },
++ { AUDIO_SOURCE_VOICE_DOWNLINK, "voice downlink" },
++ { AUDIO_SOURCE_VOICE_CALL, "voice call" },
++ { AUDIO_SOURCE_CAMCORDER, "camcorder" },
++ { AUDIO_SOURCE_VOICE_RECOGNITION, "voice recognition" },
++ { AUDIO_SOURCE_VOICE_COMMUNICATION, "voice communication" },
++ { AUDIO_SOURCE_REMOTE_SUBMIX, "remote submix" },
++#ifdef QCOM_HARDWARE
++ { AUDIO_SOURCE_FM_RX, "fm rx" },
++ { AUDIO_SOURCE_FM_RX_A2DP, "fm rx a2dp" },
++#endif
++ { (uint32_t)-1, NULL }
++};
++
++/* Flags */
++struct string_conversion string_conversion_table_output_flag[] = {
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_NONE),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DIRECT),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_PRIMARY),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_FAST),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DEEP_BUFFER),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_NON_BLOCKING),
++#ifdef QCOM_HARDWARE
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_LPA),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_TUNNEL),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_VOIP_RX),
++#endif
++ { 0, NULL }
++};
++
++/* Channels */
++struct string_conversion string_conversion_table_output_channels[] = {
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_MONO),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_STEREO),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_QUAD),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_SURROUND),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_5POINT1),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_7POINT1),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_ALL),
++ { 0, NULL }
++};
++struct string_conversion string_conversion_table_input_channels[] = {
++ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT_PROCESSED),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT_PROCESSED),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_PROCESSED),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK_PROCESSED),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_PRESSURE),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_X_AXIS),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_Y_AXIS),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_Z_AXIS),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_UPLINK),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_DNLINK),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_MONO),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_STEREO),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_ALL),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_BACK),
++#ifdef QCOM_HARDWARE
++ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_CALL_MONO),
++#endif
++ { 0, NULL }
++};
++
++/* Formats */
++struct string_conversion string_conversion_table_format[] = {
++ STRING_ENTRY(AUDIO_FORMAT_DEFAULT),
++ STRING_ENTRY(AUDIO_FORMAT_PCM),
++ STRING_ENTRY(AUDIO_FORMAT_MP3),
++ STRING_ENTRY(AUDIO_FORMAT_AMR_NB),
++ STRING_ENTRY(AUDIO_FORMAT_AMR_WB),
++ STRING_ENTRY(AUDIO_FORMAT_AAC),
++ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V1),
++ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V2),
++ STRING_ENTRY(AUDIO_FORMAT_VORBIS),
++ STRING_ENTRY(AUDIO_FORMAT_MAIN_MASK),
++ STRING_ENTRY(AUDIO_FORMAT_SUB_MASK),
++#ifdef QCOM_HARDWARE
++ STRING_ENTRY(AUDIO_FORMAT_EVRC),
++ STRING_ENTRY(AUDIO_FORMAT_QCELP),
++ STRING_ENTRY(AUDIO_FORMAT_AC3),
++ STRING_ENTRY(AUDIO_FORMAT_AC3_PLUS),
++ STRING_ENTRY(AUDIO_FORMAT_DTS),
++ STRING_ENTRY(AUDIO_FORMAT_WMA),
++ STRING_ENTRY(AUDIO_FORMAT_WMA_PRO),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_ADIF),
++ STRING_ENTRY(AUDIO_FORMAT_EVRCB),
++ STRING_ENTRY(AUDIO_FORMAT_EVRCWB),
++ STRING_ENTRY(AUDIO_FORMAT_EAC3),
++ STRING_ENTRY(AUDIO_FORMAT_DTS_LBR),
++ STRING_ENTRY(AUDIO_FORMAT_AMR_WB_PLUS),
++#endif
++ STRING_ENTRY(AUDIO_FORMAT_PCM_16_BIT),
++ STRING_ENTRY(AUDIO_FORMAT_PCM_8_BIT),
++ STRING_ENTRY(AUDIO_FORMAT_PCM_32_BIT),
++ STRING_ENTRY(AUDIO_FORMAT_PCM_8_24_BIT),
++ { 0, NULL }
++};
++#undef STRING_ENTRY
++
++#endif
+Index: overlay/src/modules/droid/droid-util-51.h
+===================================================================
+--- /dev/null
++++ overlay/src/modules/droid/droid-util-51.h
+@@ -0,0 +1,405 @@
++/*
++ * Copyright (C) 2015 Jolla Ltd.
++ *
++ * Contact: Juho Hämäläinen <juho.hamalainen at jolla.com>
++ *
++ * These PulseAudio Modules are free software; you can redistribute
++ * it and/or modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation
++ * version 2.1 of the License.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
++ * USA.
++ */
++
++#ifndef _DROID_UTIL_V51_H_
++#define _DROID_UTIL_V51_H_
++
++#define DROID_HAL 3
++
++#define DROID_HAVE_DRC
++
++#ifdef QCOM_BSP
++#define DROID_AUDIO_HAL_USE_VSID
++#endif
++
++#include <hardware/audio.h>
++#include <hardware_legacy/audio_policy_conf.h>
++
++// PulseAudio value - Android value
++
++uint32_t conversion_table_output_channel[][2] = {
++ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_OUT_MONO },
++ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_OUT_FRONT_LEFT },
++ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_OUT_FRONT_RIGHT},
++ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_OUT_FRONT_CENTER },
++ { PA_CHANNEL_POSITION_SUBWOOFER, AUDIO_CHANNEL_OUT_LOW_FREQUENCY },
++ { PA_CHANNEL_POSITION_REAR_LEFT, AUDIO_CHANNEL_OUT_BACK_LEFT },
++ { PA_CHANNEL_POSITION_REAR_RIGHT, AUDIO_CHANNEL_OUT_BACK_RIGHT },
++ { PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER },
++ { PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER },
++ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_OUT_BACK_CENTER },
++ { PA_CHANNEL_POSITION_SIDE_LEFT, AUDIO_CHANNEL_OUT_SIDE_LEFT },
++ { PA_CHANNEL_POSITION_SIDE_RIGHT, AUDIO_CHANNEL_OUT_SIDE_RIGHT },
++ { PA_CHANNEL_POSITION_TOP_CENTER, AUDIO_CHANNEL_OUT_TOP_CENTER },
++ { PA_CHANNEL_POSITION_TOP_FRONT_LEFT, AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT },
++ { PA_CHANNEL_POSITION_TOP_FRONT_CENTER, AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER },
++ { PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT },
++ { PA_CHANNEL_POSITION_TOP_REAR_LEFT, AUDIO_CHANNEL_OUT_TOP_BACK_LEFT },
++ { PA_CHANNEL_POSITION_TOP_REAR_CENTER, AUDIO_CHANNEL_OUT_TOP_BACK_CENTER },
++ { PA_CHANNEL_POSITION_TOP_REAR_RIGHT, AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT }
++};
++
++uint32_t conversion_table_input_channel[][2] = {
++ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_MONO },
++ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_IN_LEFT },
++ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_IN_RIGHT},
++ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_IN_FRONT },
++ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_IN_BACK },
++ /* Following are missing suitable counterparts on PulseAudio side. */
++ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_IN_LEFT_PROCESSED },
++ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_IN_RIGHT_PROCESSED },
++ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_IN_FRONT_PROCESSED },
++ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_IN_BACK_PROCESSED },
++ { PA_CHANNEL_POSITION_SUBWOOFER, AUDIO_CHANNEL_IN_PRESSURE },
++ { PA_CHANNEL_POSITION_AUX0, AUDIO_CHANNEL_IN_X_AXIS },
++ { PA_CHANNEL_POSITION_AUX1, AUDIO_CHANNEL_IN_Y_AXIS },
++ { PA_CHANNEL_POSITION_AUX2, AUDIO_CHANNEL_IN_Z_AXIS },
++ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_VOICE_UPLINK },
++ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_VOICE_DNLINK }
++};
++
++uint32_t conversion_table_format[][2] = {
++ { PA_SAMPLE_U8, AUDIO_FORMAT_PCM_8_BIT },
++ { PA_SAMPLE_S16LE, AUDIO_FORMAT_PCM_16_BIT },
++ { PA_SAMPLE_S32LE, AUDIO_FORMAT_PCM_32_BIT },
++ { PA_SAMPLE_S24LE, AUDIO_FORMAT_PCM_8_24_BIT }
++};
++
++uint32_t conversion_table_default_audio_source[][2] = {
++ { AUDIO_DEVICE_IN_COMMUNICATION, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_AMBIENT, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_BUILTIN_MIC, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_AUX_DIGITAL, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_VOICE_CALL, AUDIO_SOURCE_VOICE_CALL },
++ { AUDIO_DEVICE_IN_BACK_MIC, AUDIO_SOURCE_MIC },
++ { AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_SOURCE_REMOTE_SUBMIX },
++#ifdef QCOM_HARDWARE
++ { AUDIO_DEVICE_IN_FM_RX, AUDIO_SOURCE_FM_RX },
++ { AUDIO_DEVICE_IN_FM_RX_A2DP, AUDIO_SOURCE_FM_RX_A2DP },
++#endif
++ { AUDIO_DEVICE_IN_ALL, AUDIO_SOURCE_DEFAULT }
++};
++
++struct string_conversion {
++ uint32_t value;
++ const char *str;
++};
++
++#if defined(STRING_ENTRY)
++#error STRING_ENTRY already defined somewhere, fix this lib.
++#endif
++#define STRING_ENTRY(str) { str, #str }
++/* Output devices */
++struct string_conversion string_conversion_table_output_device[] = {
++ /* Each device listed here needs fancy name counterpart
++ * in string_conversion_table_output_device_fancy. */
++ STRING_ENTRY(AUDIO_DEVICE_OUT_EARPIECE),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_SPEAKER),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADPHONE),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_AUX_DIGITAL),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_HDMI),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_ACCESSORY),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_DEVICE),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_REMOTE_SUBMIX),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_TELEPHONY_TX),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_LINE),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_HDMI_ARC),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_SPDIF),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_FM),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_AUX_LINE),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_SPEAKER_SAFE),
++#ifdef QCOM_HARDWARE
++ STRING_ENTRY(AUDIO_DEVICE_OUT_FM_TX),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_PROXY),
++#endif
++ /* Combination entries consisting of multiple devices defined above.
++ * These don't require counterpart in string_conversion_table_output_device_fancy. */
++ STRING_ENTRY(AUDIO_DEVICE_OUT_DEFAULT),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_A2DP),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_SCO),
++ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_USB),
++ { 0, NULL }
++};
++
++struct string_conversion string_conversion_table_output_device_fancy[] = {
++ { AUDIO_DEVICE_OUT_EARPIECE, "output-earpiece" },
++ { AUDIO_DEVICE_OUT_SPEAKER, "output-speaker" },
++ { AUDIO_DEVICE_OUT_SPEAKER
++ | AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-speaker+wired_headphone" },
++ { AUDIO_DEVICE_OUT_WIRED_HEADSET, "output-wired_headset" },
++ { AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-wired_headphone" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO, "output-bluetooth_sco" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, "output-sco_headset" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, "output-sco_carkit" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, "output-a2dp" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, "output-a2dp_headphones" },
++ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, "output-a2dp_speaker" },
++ { AUDIO_DEVICE_OUT_AUX_DIGITAL, "output-aux_digital" },
++ { AUDIO_DEVICE_OUT_HDMI, "output-hdmi" },
++ { AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, "output-analog_dock_headset" },
++ { AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, "output-digital_dock_headset" },
++ { AUDIO_DEVICE_OUT_USB_ACCESSORY, "output-usb_accessory" },
++ { AUDIO_DEVICE_OUT_USB_DEVICE, "output-usb_device" },
++ { AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "output-remote_submix" },
++ { AUDIO_DEVICE_OUT_TELEPHONY_TX, "output-telephony" },
++ { AUDIO_DEVICE_OUT_LINE, "output-line" },
++ { AUDIO_DEVICE_OUT_HDMI_ARC, "output-hdmi_arc" },
++ { AUDIO_DEVICE_OUT_SPDIF, "output-spdif" },
++ { AUDIO_DEVICE_OUT_FM, "output-fm" },
++ { AUDIO_DEVICE_OUT_AUX_LINE, "output-aux_line" },
++ { AUDIO_DEVICE_OUT_SPEAKER_SAFE, "output-speaker_safe" },
++#ifdef QCOM_HARDWARE
++ { AUDIO_DEVICE_OUT_FM_TX, "output-fm_tx" },
++ { AUDIO_DEVICE_OUT_PROXY, "output-proxy" },
++#endif
++ { 0, NULL }
++};
++
++/* Input devices */
++struct string_conversion string_conversion_table_input_device[] = {
++ /* Each device listed here needs fancy name counterpart
++ * in string_conversion_table_input_device_fancy. */
++ STRING_ENTRY(AUDIO_DEVICE_IN_COMMUNICATION),
++ STRING_ENTRY(AUDIO_DEVICE_IN_AMBIENT),
++ STRING_ENTRY(AUDIO_DEVICE_IN_BUILTIN_MIC),
++ STRING_ENTRY(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_IN_WIRED_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_IN_AUX_DIGITAL),
++ STRING_ENTRY(AUDIO_DEVICE_IN_HDMI),
++ STRING_ENTRY(AUDIO_DEVICE_IN_VOICE_CALL),
++ STRING_ENTRY(AUDIO_DEVICE_IN_TELEPHONY_RX),
++ STRING_ENTRY(AUDIO_DEVICE_IN_BACK_MIC),
++ STRING_ENTRY(AUDIO_DEVICE_IN_REMOTE_SUBMIX),
++ STRING_ENTRY(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET),
++ STRING_ENTRY(AUDIO_DEVICE_IN_USB_ACCESSORY),
++ STRING_ENTRY(AUDIO_DEVICE_IN_USB_DEVICE),
++ STRING_ENTRY(AUDIO_DEVICE_IN_FM_TUNER),
++ STRING_ENTRY(AUDIO_DEVICE_IN_TV_TUNER),
++ STRING_ENTRY(AUDIO_DEVICE_IN_LINE),
++ STRING_ENTRY(AUDIO_DEVICE_IN_SPDIF),
++ STRING_ENTRY(AUDIO_DEVICE_IN_BLUETOOTH_A2DP),
++ STRING_ENTRY(AUDIO_DEVICE_IN_LOOPBACK),
++#ifdef QCOM_HARDWARE
++ STRING_ENTRY(AUDIO_DEVICE_IN_PROXY),
++ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX),
++ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX_A2DP),
++#endif
++ STRING_ENTRY(AUDIO_DEVICE_IN_DEFAULT),
++ /* Combination entries consisting of multiple devices defined above.
++ * These don't require counterpart in string_conversion_table_input_device_fancy. */
++ STRING_ENTRY(AUDIO_DEVICE_IN_ALL),
++ STRING_ENTRY(AUDIO_DEVICE_IN_ALL_SCO),
++ STRING_ENTRY(AUDIO_DEVICE_IN_ALL_USB),
++ { 0, NULL }
++};
++
++struct string_conversion string_conversion_table_input_device_fancy[] = {
++ { AUDIO_DEVICE_IN_COMMUNICATION, "input-communication" },
++ { AUDIO_DEVICE_IN_AMBIENT, "input-ambient" },
++ { AUDIO_DEVICE_IN_BUILTIN_MIC, "input-builtin_mic" },
++ { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, "input-bluetooth_sco_headset" },
++ { AUDIO_DEVICE_IN_WIRED_HEADSET, "input-wired_headset" },
++ { AUDIO_DEVICE_IN_AUX_DIGITAL, "input-aux_digital" },
++ { AUDIO_DEVICE_IN_HDMI, "input-hdmi" },
++ { AUDIO_DEVICE_IN_VOICE_CALL, "input-voice_call" },
++ { AUDIO_DEVICE_IN_TELEPHONY_RX, "input-telephony" },
++ { AUDIO_DEVICE_IN_BACK_MIC, "input-back_mic" },
++ { AUDIO_DEVICE_IN_REMOTE_SUBMIX, "input-remote_submix" },
++ { AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET, "input-analog_dock_headset" },
++ { AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET, "input-digital_dock_headset" },
++ { AUDIO_DEVICE_IN_USB_ACCESSORY, "input-usb_accessory" },
++ { AUDIO_DEVICE_IN_USB_DEVICE, "input-usb_device" },
++ { AUDIO_DEVICE_IN_FM_TUNER, "input-fm_tuner" },
++ { AUDIO_DEVICE_IN_TV_TUNER, "input-tv_tuner" },
++ { AUDIO_DEVICE_IN_LINE, "input-line" },
++ { AUDIO_DEVICE_IN_SPDIF, "input-spdif" },
++ { AUDIO_DEVICE_IN_BLUETOOTH_A2DP, "input-bluetooth_a2dp" },
++ { AUDIO_DEVICE_IN_LOOPBACK, "input-loopback" },
++#ifdef QCOM_HARDWARE
++ { AUDIO_DEVICE_IN_PROXY, "input-proxy" },
++ { AUDIO_DEVICE_IN_FM_RX, "input-fm_rx" },
++ { AUDIO_DEVICE_IN_FM_RX_A2DP, "input-fm_rx_a2dp" },
++#endif
++ { AUDIO_DEVICE_IN_DEFAULT, "input-default" },
++ { 0, NULL }
++};
++
++struct string_conversion string_conversion_table_audio_source_fancy[] = {
++ { AUDIO_SOURCE_DEFAULT, "default" },
++ { AUDIO_SOURCE_MIC, "mic" },
++ { AUDIO_SOURCE_VOICE_UPLINK, "voice uplink" },
++ { AUDIO_SOURCE_VOICE_DOWNLINK, "voice downlink" },
++ { AUDIO_SOURCE_VOICE_CALL, "voice call" },
++ { AUDIO_SOURCE_CAMCORDER, "camcorder" },
++ { AUDIO_SOURCE_VOICE_RECOGNITION, "voice recognition" },
++ { AUDIO_SOURCE_VOICE_COMMUNICATION, "voice communication" },
++ { AUDIO_SOURCE_REMOTE_SUBMIX, "remote submix" },
++ { AUDIO_SOURCE_FM_TUNER, "fm tuner" },
++#ifdef QCOM_HARDWARE
++ { AUDIO_SOURCE_FM_RX, "fm rx" },
++ { AUDIO_SOURCE_FM_RX_A2DP, "fm rx a2dp" },
++#endif
++ { (uint32_t)-1, NULL }
++};
++
++/* Flags */
++struct string_conversion string_conversion_table_output_flag[] = {
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_NONE),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DIRECT),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_PRIMARY),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_FAST),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DEEP_BUFFER),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_NON_BLOCKING),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_HW_AV_SYNC),
++#ifdef QCOM_HARDWARE
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_VOIP_RX),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_INCALL_MUSIC),
++ STRING_ENTRY(AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH),
++#endif
++ { 0, NULL }
++};
++
++struct string_conversion string_conversion_table_input_flag[] = {
++ STRING_ENTRY(AUDIO_INPUT_FLAG_NONE),
++ STRING_ENTRY(AUDIO_INPUT_FLAG_FAST),
++ STRING_ENTRY(AUDIO_INPUT_FLAG_HW_HOTWORD),
++ { 0, NULL }
++};
++
++/* Channels */
++struct string_conversion string_conversion_table_output_channels[] = {
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_MONO),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_STEREO),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_QUAD),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_5POINT1),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_5POINT1_BACK),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_7POINT1),
++ STRING_ENTRY(AUDIO_CHANNEL_OUT_ALL),
++ { 0, NULL }
++};
++struct string_conversion string_conversion_table_input_channels[] = {
++ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT_PROCESSED),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT_PROCESSED),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_PROCESSED),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK_PROCESSED),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_PRESSURE),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_X_AXIS),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_Y_AXIS),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_Z_AXIS),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_UPLINK),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_DNLINK),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_MONO),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_STEREO),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_ALL),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_BACK),
++ STRING_ENTRY(AUDIO_CHANNEL_IN_ALL),
++ { 0, NULL }
++};
++
++/* Formats */
++struct string_conversion string_conversion_table_format[] = {
++ STRING_ENTRY(AUDIO_FORMAT_DEFAULT),
++ STRING_ENTRY(AUDIO_FORMAT_PCM),
++ STRING_ENTRY(AUDIO_FORMAT_MP3),
++ STRING_ENTRY(AUDIO_FORMAT_AMR_NB),
++ STRING_ENTRY(AUDIO_FORMAT_AMR_WB),
++ STRING_ENTRY(AUDIO_FORMAT_AAC),
++ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V1),
++ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V2),
++ STRING_ENTRY(AUDIO_FORMAT_VORBIS),
++ STRING_ENTRY(AUDIO_FORMAT_OPUS),
++ STRING_ENTRY(AUDIO_FORMAT_AC3),
++ STRING_ENTRY(AUDIO_FORMAT_E_AC3),
++ STRING_ENTRY(AUDIO_FORMAT_PCM_16_BIT),
++ STRING_ENTRY(AUDIO_FORMAT_PCM_8_BIT),
++ STRING_ENTRY(AUDIO_FORMAT_PCM_32_BIT),
++ STRING_ENTRY(AUDIO_FORMAT_PCM_8_24_BIT),
++ STRING_ENTRY(AUDIO_FORMAT_PCM_FLOAT),
++ STRING_ENTRY(AUDIO_FORMAT_PCM_24_BIT_PACKED),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_MAIN),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_LC),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_SSR),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_LTP),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_HE_V1),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_SCALABLE),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_ERLC),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_LD),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_HE_V2),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_ELD),
++#ifdef QCOM_HARDWARE
++ STRING_ENTRY(AUDIO_FORMAT_EVRC),
++ STRING_ENTRY(AUDIO_FORMAT_QCELP),
++ STRING_ENTRY(AUDIO_FORMAT_DTS),
++ STRING_ENTRY(AUDIO_FORMAT_WMA),
++ STRING_ENTRY(AUDIO_FORMAT_WMA_PRO),
++ STRING_ENTRY(AUDIO_FORMAT_AAC_ADIF),
++ STRING_ENTRY(AUDIO_FORMAT_EVRCB),
++ STRING_ENTRY(AUDIO_FORMAT_EVRCWB),
++ STRING_ENTRY(AUDIO_FORMAT_DTS_LBR),
++ STRING_ENTRY(AUDIO_FORMAT_AMR_WB_PLUS),
++ STRING_ENTRY(AUDIO_FORMAT_MP2),
++ STRING_ENTRY(AUDIO_FORMAT_EVRCNW),
++ STRING_ENTRY(AUDIO_FORMAT_PCM_OFFLOAD),
++ STRING_ENTRY(AUDIO_FORMAT_FLAC),
++ STRING_ENTRY(AUDIO_FORMAT_E_AC3_JOC),
++#endif
++ { 0, NULL }
++};
++#undef STRING_ENTRY
++
++#endif
+Index: overlay/src/modules/droid/droid-util.c
+===================================================================
+--- overlay.orig/src/modules/droid/droid-util.c
++++ overlay/src/modules/droid/droid-util.c
+@@ -54,26 +54,10 @@
+ #include <pulsecore/refcnt.h>
+ #include <pulsecore/shared.h>
+ #include <pulsecore/mutex.h>
+-
+-#include <hardware/audio.h>
+-#include <hardware_legacy/audio_policy_conf.h>
++#include <pulsecore/strlist.h>
+
+ #include "droid-util.h"
+
+-#include <android-version.h>
+-
+-#ifndef ANDROID_VERSION_MAJOR
+-#error "ANDROID_VERSION_* not defined."
+-#endif
+-
+-#if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 1
+-#include "droid-util-41qc.h"
+-#elif ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR >= 2
+-#include "droid-util-42.h"
+-#else
+-#error "No valid ANDROID_VERSION found."
+-#endif
+-
+ #define CONVERT_FUNC(TABL) \
+ bool pa_convert_ ## TABL (uint32_t value, pa_conversion_field_t field, uint32_t *to_value) { \
+ for (unsigned int i = 0; i < sizeof( conversion_table_ ## TABL )/(sizeof(uint32_t)*2); i++) { \
+@@ -94,12 +78,15 @@ CONVERT_FUNC(input_channel);
+
+ #define DEFAULT_PRIORITY (100)
+
++/* Section defining custom global configuration variables. */
++#define GLOBAL_CONFIG_EXT_TAG "custom_properties"
++
++static void droid_port_free(pa_droid_port *p);
++
+ static bool string_convert_num_to_str(const struct string_conversion *list, const uint32_t value, const char **to_str) {
+ pa_assert(list);
+ pa_assert(to_str);
+
+- pa_log_debug("Trying to convert %x to string.", value);
+-
+ for (unsigned int i = 0; list[i].str; i++) {
+ if (list[i].value == value) {
+ *to_str = list[i].str;
+@@ -114,8 +101,6 @@ static bool string_convert_str_to_num(co
+ pa_assert(str);
+ pa_assert(to_value);
+
+- pa_log_debug("Trying to convert %s to num.", str);
+-
+ for (unsigned int i = 0; list[i].str; i++) {
+ if (pa_streq(list[i].str, str)) {
+ *to_value = list[i].value;
+@@ -125,31 +110,17 @@ static bool string_convert_str_to_num(co
+ return false;
+ }
+
+-static bool check_port_availability(const char *port) {
+- pa_assert(port);
+-
+- pa_log_debug("Checking availability for port '%s'", port);
+-
+- for (unsigned int i = 0; port_availability[i]; i++) {
+- if (pa_streq(port_availability[i], port)) {
+- return true;
+- }
+- }
+-
+- return false;
+-}
+-
+ static char *list_string(struct string_conversion *list, uint32_t flags) {
+ char *str = NULL;
+ char *tmp;
+
+-#ifdef HAL_V2
++#if DROID_HAL >= 2
+ if (flags & AUDIO_DEVICE_BIT_IN)
+ flags &= ~AUDIO_DEVICE_BIT_IN;
+ #endif
+
+ for (unsigned int i = 0; list[i].str; i++) {
+-#ifdef HAL_V2
++#if DROID_HAL >= 2
+ if (list[i].value & AUDIO_DEVICE_BIT_IN) {
+ if (popcount(list[i].value & ~AUDIO_DEVICE_BIT_IN) != 1)
+ continue;
+@@ -172,13 +143,6 @@ static char *list_string(struct string_c
+ return str;
+ }
+
+-static void droid_port_free(pa_droid_port *p) {
+- pa_assert(p);
+-
+- pa_xfree(p->name);
+- pa_xfree(p->description);
+- pa_xfree(p);
+-}
+
+ /* Output device */
+ bool pa_string_convert_output_device_num_to_str(audio_devices_t value, const char **to_str) {
+@@ -208,39 +172,67 @@ char *pa_list_string_input_device(audio_
+
+ /* Flags */
+ bool pa_string_convert_flag_num_to_str(audio_output_flags_t value, const char **to_str) {
+- return string_convert_num_to_str(string_conversion_table_flag, (uint32_t) value, to_str);
++ return string_convert_num_to_str(string_conversion_table_output_flag, (uint32_t) value, to_str);
+ }
+
+ bool pa_string_convert_flag_str_to_num(const char *str, audio_output_flags_t *to_value) {
+- return string_convert_str_to_num(string_conversion_table_flag, str, (uint32_t*) to_value);
++ return string_convert_str_to_num(string_conversion_table_output_flag, str, (uint32_t*) to_value);
+ }
+
+ char *pa_list_string_flags(audio_output_flags_t flags) {
+- return list_string(string_conversion_table_flag, flags);
++ return list_string(string_conversion_table_output_flag, flags);
++}
++
++bool pa_input_device_default_audio_source(audio_devices_t input_device, audio_source_t *default_source)
++{
++#if DROID_HAL >= 2
++ input_device &= ~AUDIO_DEVICE_BIT_IN;
++#endif
++
++ /* Note converting HAL values to different HAL values! */
++ for (unsigned int i = 0; i < sizeof(conversion_table_default_audio_source) / (sizeof(uint32_t) * 2); i++) {
++ if (conversion_table_default_audio_source[i][0] & input_device) {
++ *default_source = conversion_table_default_audio_source[i][1];
++ return true;
++ }
++ }
++ return false;
+ }
+
+ /* Config parser */
+
+ #define WHITESPACE "\n\r \t"
+
+-static int parse_list(const struct string_conversion *table, const char *str, uint32_t *dst) {
++static int parse_list(const struct string_conversion *table,
++ const char *str,
++ uint32_t *dst,
++ char **unknown_entries) {
+ int count = 0;
+ char *entry;
++ char *unknown = NULL;
+ const char *state = NULL;
+
+ pa_assert(table);
+ pa_assert(str);
+ pa_assert(dst);
++ pa_assert(unknown_entries);
+
+ *dst = 0;
++ *unknown_entries = NULL;
+
+ while ((entry = pa_split(str, "|", &state))) {
+ uint32_t d = 0;
+
+ if (!string_convert_str_to_num(table, entry, &d)) {
+- pa_log("Unknown entry %s", entry);
+- pa_xfree(entry);
+- return -1;
++ if (*unknown_entries) {
++ unknown = pa_sprintf_malloc("%s|%s", *unknown_entries, entry);
++ pa_xfree(*unknown_entries);
++ pa_xfree(entry);
++ } else
++ unknown = entry;
++
++ *unknown_entries = unknown;
++ continue;
+ }
+
+ *dst |= d;
+@@ -252,24 +244,34 @@ static int parse_list(const struct strin
+ return count;
+ }
+
+-static bool parse_sampling_rates(const char *str, uint32_t sampling_rates[32]) {
++static bool parse_sampling_rates(const char *fn, const unsigned ln,
++ const char *str, uint32_t sampling_rates[32]) {
++ pa_assert(fn);
++ pa_assert(str);
++
+ char *entry;
+ const char *state = NULL;
+
+- pa_assert(str);
+-
+ uint32_t pos = 0;
+ while ((entry = pa_split(str, "|", &state))) {
+ int32_t val;
+
++#if DROID_HAL >= 3
++ if (pos == 0 && pa_streq(entry, "dynamic")) {
++ sampling_rates[pos++] = (uint32_t) -1;
++ pa_xfree(entry);
++ break;
++ }
++#endif
++
+ if (pos == AUDIO_MAX_SAMPLING_RATES) {
+- pa_log("Too many sample rate entries (> %d)", AUDIO_MAX_SAMPLING_RATES);
++ pa_log("[%s:%u] Too many sample rate entries (> %d)", fn, ln, AUDIO_MAX_SAMPLING_RATES);
+ pa_xfree(entry);
+ return false;
+ }
+
+ if (pa_atoi(entry, &val) < 0) {
+- pa_log("Bad sample rate value %s", entry);
++ pa_log("[%s:%u] Bad sample rate value %s", fn, ln, entry);
+ pa_xfree(entry);
+ return false;
+ }
+@@ -285,14 +287,58 @@ static bool parse_sampling_rates(const c
+ return true;
+ }
+
+-static bool parse_formats(const char *str, audio_format_t *formats) {
++static bool check_and_log(const char *fn, const unsigned ln, const char *field,
++ const int count, const char *str, char *unknown,
++ const bool must_have_all) {
++ bool fail;
++
++ pa_assert(fn);
++ pa_assert(field);
++ pa_assert(str);
++
++ fail = must_have_all && unknown;
++
++ if (unknown) {
++ pa_log_warn("[%s:%u] Unknown %s entries: %s", fn, ln, field, unknown);
++ pa_xfree(unknown);
++ }
++
++ if (count == 0 || fail) {
++ pa_log("[%s:%u] Failed to parse %s (%s).", fn, ln, field, str);
++ return false;
++ }
++
++ return true;
++}
++
++static bool parse_formats(const char *fn, const unsigned ln,
++ const char *str, audio_format_t *formats) {
++ int count;
++ char *unknown = NULL;
++
++ pa_assert(fn);
+ pa_assert(str);
+ pa_assert(formats);
+
+- return parse_list(string_conversion_table_format, str, formats) > 0;
++#if DROID_HAL >= 3
++ /* Needs to be probed later */
++ if (pa_streq(str, "dynamic")) {
++ *formats = 0;
++ return true;
++ }
++#endif
++
++ count = parse_list(string_conversion_table_format, str, formats, &unknown);
++
++ return check_and_log(fn, ln, "formats", count, str, unknown, false);
+ }
+
+-static int parse_channels(const char *str, bool in_output, audio_channel_mask_t *channels) {
++static int parse_channels(const char *fn, const unsigned ln,
++ const char *str, bool in_output, audio_channel_mask_t *channels) {
++ int count;
++ char *unknown = NULL;
++
++ pa_assert(fn);
+ pa_assert(str);
+ pa_assert(channels);
+
+@@ -302,41 +348,77 @@ static int parse_channels(const char *st
+ return true;
+ }
+
+- if (in_output)
+- return parse_list(string_conversion_table_output_channels, str, channels);
+- else
+- return parse_list(string_conversion_table_input_channels, str, channels);
++ count = parse_list(in_output ? string_conversion_table_output_channels
++ : string_conversion_table_input_channels,
++ str, channels, &unknown);
++
++ return check_and_log(fn, ln, in_output ? "output channel_masks" : "input channel_masks",
++ count, str, unknown, false);
+ }
+
+-static bool parse_devices(const char *str, bool in_output, audio_devices_t *devices) {
++static bool parse_devices(const char *fn, const unsigned ln,
++ const char *str, bool in_output, audio_devices_t *devices, bool must_have_all) {
++ int count;
++ char *unknown = NULL;
++
++ pa_assert(fn);
+ pa_assert(str);
+ pa_assert(devices);
+
+- if (in_output)
+- return parse_list(string_conversion_table_output_device, str, devices) > 0;
+- else
+- return parse_list(string_conversion_table_input_device, str, devices) > 0;
++ count = parse_list(in_output ? string_conversion_table_output_device
++ : string_conversion_table_input_device,
++ str, devices, &unknown);
++
++ return check_and_log(fn, ln, in_output ? "output devices" : "input devices",
++ count, str, unknown, must_have_all);
++}
++
++static bool parse_output_flags(const char *fn, const unsigned ln,
++ const char *str, audio_output_flags_t *flags) {
++ int count;
++ char *unknown = NULL;
++
++ pa_assert(fn);
++ pa_assert(str);
++ pa_assert(flags);
++
++ count = parse_list(string_conversion_table_output_flag, str, flags, &unknown);
++
++ return check_and_log(fn, ln, "flags", count, str, unknown, false);
+ }
+
+-static bool parse_flags(const char *str, audio_output_flags_t *flags) {
++#if DROID_HAL >= 3
++static bool parse_input_flags(const char *fn, const unsigned ln,
++ const char *str, audio_input_flags_t *flags) {
++ int count;
++ char *unknown = NULL;
++
++ pa_assert(fn);
+ pa_assert(str);
+ pa_assert(flags);
+
+- return parse_list(string_conversion_table_flag, str, flags) > 0;
++ count = parse_list(string_conversion_table_input_flag, str, flags, &unknown);
++
++ return check_and_log(fn, ln, "flags", count, str, unknown, false);
+ }
++#endif
++
++#define MAX_LINE_LENGTH (1024)
+
+ bool pa_parse_droid_audio_config(const char *filename, pa_droid_config_audio *config) {
+ FILE *f;
+- int n = 0;
++ unsigned n = 0;
+ bool ret = true;
++ char *full_line = NULL;
+
+ enum config_loc {
+- IN_ROOT = 0,
+- IN_GLOBAL = 1,
+- IN_HW_MODULES = 1,
+- IN_MODULE = 2,
+- IN_OUTPUT_INPUT = 3,
+- IN_CONFIG = 4
++ IN_ROOT = 0,
++ IN_GLOBAL = 1,
++ IN_HW_MODULES = 2,
++ IN_MODULE = 3,
++ IN_OUTPUT_INPUT = 4,
++ IN_CONFIG = 5,
++ IN_GLOBAL_EXT = 6,
+ } loc = IN_ROOT;
+
+
+@@ -362,29 +444,39 @@ bool pa_parse_droid_audio_config(const c
+
+ pa_lock_fd(fileno(f), 1);
+
++ full_line = pa_xmalloc0(sizeof(char) * MAX_LINE_LENGTH);
++
+ while (!feof(f)) {
+- char ln[512];
+- char *d, *v, *val;
++ char *ln, *d, *v, *val;
+
+- if (!fgets(ln, sizeof(ln), f))
++ if (!fgets(full_line, MAX_LINE_LENGTH, f))
+ break;
+
+ n++;
+
+- pa_strip_nl(ln);
++ pa_strip_nl(full_line);
+
+- if (ln[0] == '#' || !*ln )
++ if (!*full_line)
+ continue;
+
++ ln = full_line + strspn(full_line, WHITESPACE);
++
++ if (ln[0] == '#')
++ continue;
++
++ v = ln;
++ d = v + strcspn(v, WHITESPACE);
++
++ val = d + strspn(d, WHITESPACE);
++ d[0] = '\0';
++ d = val + strcspn(val, WHITESPACE);
++ d[0] = '\0';
++
+ /* Enter section */
+- if (ln[strlen(ln)-1] == '{') {
+- d = ln+strspn(ln, WHITESPACE);
+- v = d;
+- d = v+strcspn(v, WHITESPACE);
+- d[0] = '\0';
++ if (pa_streq(val, "{")) {
+
+ if (!*v) {
+- pa_log(__FILE__ ": [%s:%u] failed to parse line - too few words", filename, n);
++ pa_log("[%s:%u] failed to parse line - too few words", filename, n);
+ goto finish;
+ }
+
+@@ -397,7 +489,17 @@ bool pa_parse_droid_audio_config(const c
+ else if (pa_streq(v, AUDIO_HW_MODULE_TAG))
+ loc = IN_HW_MODULES;
+ else {
+- pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown field (%s)", filename, n, v);
++ pa_log("[%s:%u] failed to parse line - unknown field (%s)", filename, n, v);
++ ret = false;
++ goto finish;
++ }
++ break;
++
++ case IN_GLOBAL:
++ if (pa_streq(v, GLOBAL_CONFIG_EXT_TAG))
++ loc = IN_GLOBAL_EXT;
++ else {
++ pa_log("[%s:%u] failed to parse line - unknown section (%s)", filename, n, v);
+ ret = false;
+ goto finish;
+ }
+@@ -420,7 +522,7 @@ bool pa_parse_droid_audio_config(const c
+ loc = IN_OUTPUT_INPUT;
+ in_output = false;
+ } else {
+- pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown field (%s)", filename, n, v);
++ pa_log("[%s:%u] failed to parse line - unknown field (%s)", filename, n, v);
+ ret = false;
+ goto finish;
+ }
+@@ -447,7 +549,12 @@ bool pa_parse_droid_audio_config(const c
+ break;
+
+ case IN_CONFIG:
+- pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown field in config (%s)", filename, n, v);
++ pa_log("[%s:%u] failed to parse line - unknown field in config (%s)", filename, n, v);
++ ret = false;
++ goto finish;
++
++ default:
++ pa_log("[%s:%u] failed to parse line - unknown section (%s)", filename, n, v);
+ ret = false;
+ goto finish;
+ }
+@@ -456,108 +563,125 @@ bool pa_parse_droid_audio_config(const c
+ }
+
+ /* Exit section */
+- if (ln[strlen(ln)-1] == '}') {
+- if (loc == IN_ROOT) {
+- pa_log(__FILE__ ": [%s:%u] failed to parse line - extra closing bracket", filename, n);
+- ret = false;
+- goto finish;
+- }
++ if (pa_streq(v, "}")) {
++ switch (loc) {
++ case IN_ROOT:
++ pa_log("[%s:%u] failed to parse line - extra closing bracket", filename, n);
++ ret = false;
++ goto finish;
+
+- loc--;
+- if (loc == IN_MODULE) {
+- if (in_output)
+- output = NULL;
+- else
+- input = NULL;
+- }
+- if (loc == IN_ROOT)
+- module = NULL;
++ case IN_HW_MODULES:
++ module = NULL;
++ /* fall through */
++ case IN_GLOBAL:
++ loc = IN_ROOT;
++ break;
+
++ case IN_OUTPUT_INPUT:
++ if (in_output)
++ output = NULL;
++ else
++ input = NULL;
++ /* fall through */
++ case IN_MODULE:
++ /* fall through */
++ case IN_CONFIG:
++ /* fall through */
++ case IN_GLOBAL_EXT:
++ loc--;
++ break;
++ }
+ in_global = false;
+
+ continue;
+ }
+
+- /* Parse global configuration */
+- if (in_global) {
++ if (loc == IN_GLOBAL ||
++ loc == IN_GLOBAL_EXT ||
++ loc == IN_CONFIG) {
++
+ bool success = false;
+
+- d = ln+strspn(ln, WHITESPACE);
+- v = d;
+- d = v+strcspn(v, WHITESPACE);
+-
+- val = d+strspn(d, WHITESPACE);
+- d[0] = '\0';
+- d = val+strcspn(val, WHITESPACE);
+- d[0] = '\0';
+-
+- if (pa_streq(v, ATTACHED_OUTPUT_DEVICES_TAG))
+- success = parse_devices(val, true, &config->global_config.attached_output_devices);
+- else if (pa_streq(v, DEFAULT_OUTPUT_DEVICE_TAG))
+- success = parse_devices(val, true, &config->global_config.default_output_device);
+- else if (pa_streq(v, ATTACHED_INPUT_DEVICES_TAG))
+- success = parse_devices(val, false, &config->global_config.attached_input_devices);
+- else if (pa_streq(v, SPEAKER_DRC_ENABLED_TAG)) {
+- pa_log(__FILE__ ": speaker drc is not yet supported, skipping", filename);
+- success = true;
+- } else {
+- pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown config entry %s", filename, n, v);
+- success = false;
+- }
++ if (loc == IN_GLOBAL) {
+
+- if (!success) {
+- ret = false;
+- goto finish;
+- }
+- }
++ /* Parse global configuration */
+
+- /* Parse per-output or per-input configuration */
+- if (loc == IN_CONFIG) {
+- bool success = false;
++ if (pa_streq(v, ATTACHED_OUTPUT_DEVICES_TAG)) {
++ success = parse_devices(filename, n, val, true,
++ &config->global_config.attached_output_devices, false);
++ }
++ else if (pa_streq(v, DEFAULT_OUTPUT_DEVICE_TAG)) {
++ success = parse_devices(filename, n, val, true,
++ &config->global_config.default_output_device, true);
++ }
++ else if (pa_streq(v, ATTACHED_INPUT_DEVICES_TAG)) {
++ success = parse_devices(filename, n, val, false,
++ &config->global_config.attached_input_devices, false);
++ }
++#ifdef DROID_HAVE_DRC
++ // SPEAKER_DRC_ENABLED_TAG is only from Android v4.4
++ else if (pa_streq(v, SPEAKER_DRC_ENABLED_TAG))
++ /* TODO - Add support for dynamic range control */
++ success = true; /* Do not fail while parsing speaker_drc_enabled entry */
++#endif
++ else {
++ pa_log("[%s:%u] failed to parse line - unknown config entry %s", filename, n, v);
++ success = false;
++ }
+
+- pa_assert(module);
++ } else if (loc == IN_GLOBAL_EXT) {
+
+- d = ln+strspn(ln, WHITESPACE);
+- v = d;
+- d = v+strcspn(v, WHITESPACE);
++ /* Parse custom global configuration
++ * For now just log all custom variables, don't do
++ * anything with the values.
++ * TODO: Store custom values somehow */
+
+- val = d+strspn(d, WHITESPACE);
+- d[0] = '\0';
+- d = val+strcspn(val, WHITESPACE);
+- d[0] = '\0';
++ pa_log_debug("[%s:%u] TODO custom variable: %s = %s", filename, n, v, val);
++ success = true;
+
++ } else if (loc == IN_CONFIG) {
+
+- if ((in_output && !output) || (!in_output && !input)) {
+- pa_log(__FILE__ ": [%s:%u] failed to parse line", filename, n);
+- ret = false;
+- goto finish;
+- }
++ /* Parse per-output or per-input configuration */
+
+- if (pa_streq(v, SAMPLING_RATES_TAG))
+- success = parse_sampling_rates(val, in_output ? output->sampling_rates : input->sampling_rates);
+- else if (pa_streq(v, FORMATS_TAG))
+- success = parse_formats(val, in_output ? &output->formats : &input->formats);
+- else if (pa_streq(v, CHANNELS_TAG)) {
+- if (in_output)
+- success = (parse_channels(val, true, &output->channel_masks) > 0);
+- else
+- success = (parse_channels(val, false, &input->channel_masks) > 0);
+- } else if (pa_streq(v, DEVICES_TAG)) {
+- if (in_output)
+- success = parse_devices(val, true, &output->devices);
+- else
+- success = parse_devices(val, false, &input->devices);
+- } else if (pa_streq(v, FLAGS_TAG)) {
+- if (in_output)
+- success = parse_flags(val, &output->flags);
+- else {
+- pa_log(__FILE__ ": [%s:%u] failed to parse line - output flags inside input definition", filename, n);
++ if ((in_output && !output) || (!in_output && !input)) {
++ pa_log("[%s:%u] failed to parse line", filename, n);
++ ret = false;
++ goto finish;
++ }
++
++ if (pa_streq(v, SAMPLING_RATES_TAG))
++ success = parse_sampling_rates(filename, n, val,
++ in_output ? output->sampling_rates : input->sampling_rates);
++ else if (pa_streq(v, FORMATS_TAG))
++ success = parse_formats(filename, n, val, in_output ? &output->formats : &input->formats);
++ else if (pa_streq(v, CHANNELS_TAG)) {
++ if (in_output)
++ success = parse_channels(filename, n, val, true, &output->channel_masks);
++ else
++ success = parse_channels(filename, n, val, false, &input->channel_masks);
++ } else if (pa_streq(v, DEVICES_TAG)) {
++ if (in_output)
++ success = parse_devices(filename, n, val, true, &output->devices, false);
++ else
++ success = parse_devices(filename, n, val, false, &input->devices, false);
++ } else if (pa_streq(v, FLAGS_TAG)) {
++ if (in_output)
++ success = parse_output_flags(filename, n, val, &output->flags);
++ else {
++#if DROID_HAL >= 3
++ success = parse_input_flags(filename, n, val, &input->flags);
++#else
++ pa_log("[%s:%u] failed to parse line - output flags inside input definition", filename, n);
++ success = false;
++#endif
++ }
++ } else {
++ pa_log("[%s:%u] failed to parse line - unknown config entry %s", filename, n, v);
+ success = false;
+ }
+- } else {
+- pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown config entry %s", filename, n, v);
+- success = false;
+- }
++
++ } else
++ pa_assert_not_reached();
+
+ if (!success) {
+ ret = false;
+@@ -574,6 +698,8 @@ finish:
+ fclose(f);
+ }
+
++ pa_xfree(full_line);
++
+ return ret;
+ }
+
+@@ -614,21 +740,50 @@ const pa_droid_config_hw_module *pa_droi
+ return NULL;
+ }
+
+-pa_droid_profile *pa_droid_profile_new(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input) {
++static pa_droid_profile *profile_new(pa_droid_profile_set *ps,
++ const pa_droid_config_hw_module *module,
++ const char *name,
++ const char *description) {
+ pa_droid_profile *p;
+
+ pa_assert(ps);
+- pa_assert(output);
++ pa_assert(module);
++ pa_assert(name);
++ pa_assert(description);
+
+ p = pa_xnew0(pa_droid_profile, 1);
+ p->profile_set = ps;
+- p->module = output->module;
+- p->name = pa_sprintf_malloc("%s%s%s", output->name, input ? "-" : "", input ? input->name : "");
+- p->description = pa_sprintf_malloc("%s output%s%s%s", output->name,
+- input ? " and " : "",
+- input ? input->name : "",
+- input ? " input." : "");
++ p->module = module;
++ p->name = pa_xstrdup(name);
++ p->description = pa_xstrdup(description);
+ p->priority = DEFAULT_PRIORITY;
++
++ p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
++ p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
++
++ pa_hashmap_put(ps->profiles, p->name, p);
++
++ return p;
++}
++
++pa_droid_profile *pa_droid_profile_new(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input) {
++ pa_droid_profile *p;
++ char *name;
++ char *description;
++
++ pa_assert(ps);
++ pa_assert(output);
++
++ name = pa_sprintf_malloc("%s%s%s", output->name, input ? "-" : "", input ? input->name : "");
++ description = pa_sprintf_malloc("%s output%s%s%s", output->name,
++ input ? " and " : "",
++ input ? input->name : "",
++ input ? " input." : "");
++
++ p = profile_new(ps, output->module, name, description);
++ pa_xfree(name);
++ pa_xfree(description);
++
+ if (pa_streq(output->name, "primary")) {
+ p->priority += DEFAULT_PRIORITY;
+
+@@ -637,16 +792,24 @@ pa_droid_profile *pa_droid_profile_new(p
+ }
+
+ if (output)
+- p->output = pa_droid_mapping_get(ps, PA_DIRECTION_OUTPUT, output);
++ pa_idxset_put(p->output_mappings, pa_droid_mapping_get(ps, PA_DIRECTION_OUTPUT, output), NULL);
+ if (input)
+- p->input = pa_droid_mapping_get(ps, PA_DIRECTION_INPUT, input);
+-
+- pa_hashmap_put(ps->profiles, p->name, p);
++ pa_idxset_put(p->input_mappings, pa_droid_mapping_get(ps, PA_DIRECTION_INPUT, input), NULL);
+
+ return p;
+ }
+
+-static void add_profile(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input) {
++void pa_droid_profile_add_mapping(pa_droid_profile *p, pa_droid_mapping *am) {
++ pa_assert(p);
++ pa_assert(am);
++
++ if (am->direction == PA_DIRECTION_OUTPUT)
++ pa_idxset_put(p->output_mappings, am, NULL);
++ else
++ pa_idxset_put(p->input_mappings, am, NULL);
++}
++
++static pa_droid_profile *add_profile(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input) {
+ pa_droid_profile *ap;
+
+ pa_log_debug("New profile: %s-%s", output->name, input ? input->name : "no input");
+@@ -654,19 +817,121 @@ static void add_profile(pa_droid_profile
+ ap = pa_droid_profile_new(ps, output, input);
+
+ pa_hashmap_put(ps->profiles, ap->name, ap);
++
++ return ap;
+ }
+
+-pa_droid_profile_set *pa_droid_profile_set_new(const pa_droid_config_hw_module *module) {
++static bool str_in_strlist(const char *str, pa_strlist *list) {
++ pa_strlist *iter;
++
++ pa_assert(str);
++ pa_assert(list);
++
++ for (iter = list; iter; iter = pa_strlist_next(iter)) {
++ if (pa_streq(str, pa_strlist_data(iter)))
++ return true;
++ }
++
++ return false;
++}
++
++/* outputs or inputs string lists can be NULL, which means include all outputs and inputs
++ * from module. */
++static pa_droid_profile *add_combined_profile(pa_droid_profile_set *ps,
++ const pa_droid_config_hw_module *module,
++ pa_strlist *outputs,
++ pa_strlist *inputs) {
++ pa_droid_profile *p;
++ char *description;
++ char *o_str;
++ char *i_str;
++ pa_strlist *to_outputs = NULL;
++ pa_strlist *to_inputs = NULL;
++ pa_droid_mapping *am;
++
++ pa_assert(ps);
++ pa_assert(module);
++
++ for (unsigned i = 0; i < module->outputs_size; i++) {
++ if (outputs && !str_in_strlist(module->outputs[i].name, outputs))
++ continue;
++
++ to_outputs = pa_strlist_prepend(to_outputs, module->outputs[i].name);
++ }
++ to_outputs = pa_strlist_reverse(to_outputs);
++
++ for (unsigned i = 0; i < module->inputs_size; i++) {
++ if (inputs && !str_in_strlist(module->inputs[i].name, inputs))
++ continue;
++
++ to_inputs = pa_strlist_prepend(to_inputs, module->inputs[i].name);
++ }
++ to_inputs = pa_strlist_reverse(to_inputs);
++
++ o_str = pa_strlist_tostring(to_outputs);
++ i_str = pa_strlist_tostring(to_inputs);
++
++ pa_log_debug("New combined profile: %s (outputs: %s, inputs: %s)", module->name, o_str, i_str);
++
++ description = pa_sprintf_malloc("Combined outputs (%s) and inputs (%s) of %s.", o_str,
++ i_str,
++ module->name);
++ p = profile_new(ps, module, module->name, description);
++ pa_xfree(description);
++ pa_xfree(o_str);
++ pa_xfree(i_str);
++
++ for (unsigned i = 0; i < module->outputs_size; i++) {
++ if (!str_in_strlist(module->outputs[i].name, to_outputs))
++ continue;
++
++ am = pa_droid_mapping_get(ps, PA_DIRECTION_OUTPUT, &module->outputs[i]);
++ pa_droid_profile_add_mapping(p, am);
++
++ if (pa_streq(module->outputs[i].name, "primary"))
++ p->priority += DEFAULT_PRIORITY;
++ }
++
++ for (unsigned i = 0; i < module->inputs_size; i++) {
++ if (!str_in_strlist(module->inputs[i].name, to_inputs))
++ continue;
++
++ am = pa_droid_mapping_get(ps, PA_DIRECTION_INPUT, &module->inputs[i]);
++ pa_droid_profile_add_mapping(p, am);
++
++ if (pa_streq(module->inputs[i].name, "primary"))
++ p->priority += DEFAULT_PRIORITY;
++ }
++
++ pa_strlist_free(to_outputs);
++ pa_strlist_free(to_inputs);
++
++ return p;
++}
++
++static pa_droid_profile_set *profile_set_new(const pa_droid_config_hw_module *module) {
+ pa_droid_profile_set *ps;
+
+ pa_assert(module);
+
+ ps = pa_xnew0(pa_droid_profile_set, 1);
+ ps->config = module->config;
+- ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_profile_free);
+- ps->output_mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_mapping_free);
+- ps->input_mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_mapping_free);
+- ps->all_ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) droid_port_free);
++ ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
++ NULL, (pa_free_cb_t) pa_droid_profile_free);
++ ps->output_mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
++ NULL, (pa_free_cb_t) pa_droid_mapping_free);
++ ps->input_mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
++ NULL, (pa_free_cb_t) pa_droid_mapping_free);
++ ps->all_ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
++ NULL, (pa_free_cb_t) droid_port_free);
++
++ return ps;
++}
++
++pa_droid_profile_set *pa_droid_profile_set_new(const pa_droid_config_hw_module *module) {
++ pa_droid_profile_set *ps;
++
++ ps = profile_set_new(module);
+
+ /* Each distinct hw module output matches one profile. If there are multiple inputs
+ * combinations are made so that all possible outputs and inputs can be selected.
+@@ -686,6 +951,15 @@ pa_droid_profile_set *pa_droid_profile_s
+ return ps;
+ }
+
++pa_droid_profile_set *pa_droid_profile_set_combined_new(const pa_droid_config_hw_module *module, pa_strlist *inputs, pa_strlist *outputs) {
++ pa_droid_profile_set *ps;
++
++ ps = profile_set_new(module);
++ add_combined_profile(ps, module, inputs, outputs);
++
++ return ps;
++}
++
+ void pa_droid_mapping_free(pa_droid_mapping *am) {
+ pa_assert(am);
+
+@@ -700,9 +974,21 @@ void pa_droid_profile_free(pa_droid_prof
+
+ pa_xfree(ap->name);
+ pa_xfree(ap->description);
++ if (ap->output_mappings)
++ pa_idxset_free(ap->output_mappings, NULL);
++ if (ap->input_mappings)
++ pa_idxset_free(ap->input_mappings, NULL);
+ pa_xfree(ap);
+ }
+
++static void droid_port_free(pa_droid_port *p) {
++ pa_assert(p);
++
++ pa_xfree(p->name);
++ pa_xfree(p->description);
++ pa_xfree(p);
++}
++
+ void pa_droid_profile_set_free(pa_droid_profile_set *ps) {
+ pa_assert(ps);
+
+@@ -749,9 +1035,6 @@ static pa_droid_port *create_o_port(pa_d
+ if (am->profile_set->config->global_config.default_output_device & device)
+ p->priority += DEFAULT_PRIORITY;
+
+- if (check_port_availability(p->name))
+- p->priority += (DEFAULT_PRIORITY * 3);
+-
+ return p;
+ }
+
+@@ -766,7 +1049,7 @@ static void add_o_ports(pa_droid_mapping
+
+ devices = am->output->devices;
+
+- devices &= ~AUDIO_DEVICE_OUT_DEFAULT;
++ devices &= ~AUDIO_DEVICE_BIT_DEFAULT;
+
+ /* IHF combo devices, these devices are combined with IHF */
+ combo_devices = AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
+@@ -819,6 +1102,35 @@ static void add_o_ports(pa_droid_mapping
+ pa_idxset_put(am->ports, p, NULL);
+ }
+
++static void add_i_port(pa_droid_mapping *am, uint32_t device, const char *name) {
++ pa_droid_port *p;
++ char *desc;
++
++ pa_assert(am);
++ pa_assert(name);
++
++ if (!(p = pa_hashmap_get(am->profile_set->all_ports, name))) {
++ pa_log_debug(" New input port %s", name);
++ p = pa_xnew0(pa_droid_port, 1);
++
++ p->mapping = am;
++ p->name = pa_xstrdup(name);
++ desc = pa_replace(name, "input-", "Input from ");
++ p->description = pa_replace(desc, "_", " ");
++ pa_xfree(desc);
++ p->priority = DEFAULT_PRIORITY;
++ p->device = device;
++
++ if (am->profile_set->config->global_config.attached_input_devices & device)
++ p->priority += DEFAULT_PRIORITY;
++
++ pa_hashmap_put(am->profile_set->all_ports, p->name, p);
++ } else
++ pa_log_debug(" Input port %s from cache", name);
++
++ pa_idxset_put(am->ports, p, NULL);
++}
++
+ static void add_i_ports(pa_droid_mapping *am) {
+ pa_droid_port *p;
+ const char *name;
+@@ -828,9 +1140,9 @@ static void add_i_ports(pa_droid_mapping
+
+ pa_assert(am);
+
+- devices = am->input->devices;
+-#ifdef HAL_V2
+- devices &= ~AUDIO_DEVICE_IN_DEFAULT;
++ devices = am->input->devices | AUDIO_DEVICE_IN_DEFAULT;
++#if DROID_HAL >= 2
++ devices &= ~AUDIO_DEVICE_BIT_IN;
+ #endif
+
+ while (devices) {
+@@ -838,44 +1150,25 @@ static void add_i_ports(pa_droid_mapping
+
+ if (devices & cur_device) {
+
+-#ifdef HAL_V2
++#if DROID_HAL >= 2
++#ifndef DROID_DEVICE_MAKO
+ cur_device |= AUDIO_DEVICE_BIT_IN;
+ #endif
++#endif
+
+ pa_assert_se(pa_droid_input_port_name(cur_device, &name));
+-
+- if (!(p = pa_hashmap_get(am->profile_set->all_ports, name))) {
+- pa_log_debug(" New input port %s", name);
+- p = pa_xnew0(pa_droid_port, 1);
+-
+- p->mapping = am;
+- p->name = pa_xstrdup(name);
+- desc = pa_replace(name, "input-", "Input from ");
+- p->description = pa_replace(desc, "_", " ");
+- pa_xfree(desc);
+- p->priority = DEFAULT_PRIORITY;
+- p->device = cur_device;
+-
+- if (am->profile_set->config->global_config.attached_input_devices & cur_device & ~AUDIO_DEVICE_BIT_IN)
+- p->priority += DEFAULT_PRIORITY;
+-
+- /* Make builtin mic the default input device */
+- if (cur_device == AUDIO_DEVICE_IN_BUILTIN_MIC)
+- p->priority += DEFAULT_PRIORITY;
+-
+- if (check_port_availability(p->name))
+- p->priority += (DEFAULT_PRIORITY * 3);
+-
+- pa_hashmap_put(am->profile_set->all_ports, p->name, p);
+- } else
+- pa_log_debug(" Input port %s from cache", name);
+-
+- pa_idxset_put(am->ports, p, NULL);
++ add_i_port(am, cur_device, name);
+
+ devices &= ~cur_device;
+ }
+ }
+
++#if DROID_HAL == 1
++ /* HAL v1 has default input device defined as another input device,
++ * so we need to add it by hand here. */
++ add_i_port(am, AUDIO_DEVICE_IN_DEFAULT, "input-default");
++#endif
++
+ if (!(p = pa_hashmap_get(am->profile_set->all_ports, PA_DROID_INPUT_PARKING))) {
+ pa_log_debug(" New input port %s", PA_DROID_INPUT_PARKING);
+ /* Create parking port for input mapping to be used when audio_mode_t changes. */
+@@ -935,6 +1228,32 @@ pa_droid_mapping *pa_droid_mapping_get(p
+ return am;
+ }
+
++bool pa_droid_mapping_is_primary(pa_droid_mapping *am) {
++ pa_assert(am);
++
++ if (am->direction == PA_DIRECTION_OUTPUT) {
++ pa_assert(am->output);
++ return pa_streq(am->output->name, PA_DROID_PRIMARY_DEVICE);
++ } else {
++ pa_assert(am->input);
++ return pa_streq(am->input->name, PA_DROID_PRIMARY_DEVICE);
++ }
++}
++
++pa_droid_mapping *pa_droid_idxset_get_primary(pa_idxset *i) {
++ pa_droid_mapping *am;
++ uint32_t idx;
++
++ pa_assert(i);
++
++ PA_IDXSET_FOREACH(am, i, idx) {
++ if (pa_droid_mapping_is_primary(am))
++ return am;
++ }
++
++ return NULL;
++}
++
+ bool pa_droid_output_port_name(audio_devices_t value, const char **to_str) {
+ return string_convert_num_to_str(string_conversion_table_output_device_fancy, (uint32_t) value, to_str);
+ }
+@@ -943,8 +1262,13 @@ bool pa_droid_input_port_name(audio_devi
+ return string_convert_num_to_str(string_conversion_table_input_device_fancy, (uint32_t) value, to_str);
+ }
+
++bool pa_droid_audio_source_name(audio_source_t value, const char **to_str) {
++ return string_convert_num_to_str(string_conversion_table_audio_source_fancy, (uint32_t) value, to_str);
++}
++
+ static int add_ports(pa_core *core, pa_card_profile *cp, pa_hashmap *ports, pa_droid_mapping *am, pa_hashmap *extra) {
+ pa_droid_port *p;
++ pa_device_port_new_data dp_data;
+ pa_device_port *dp;
+ pa_droid_port_data *data;
+ uint32_t idx;
+@@ -955,35 +1279,37 @@ static int add_ports(pa_core *core, pa_c
+ PA_IDXSET_FOREACH(p, am->ports, idx) {
+ if (!(dp = pa_hashmap_get(ports, p->name))) {
+ pa_log_debug(" New port %s", p->name);
++ pa_device_port_new_data_init(&dp_data);
++ pa_device_port_new_data_set_name(&dp_data, p->name);
++ pa_device_port_new_data_set_description(&dp_data, p->description);
++ pa_device_port_new_data_set_direction(&dp_data, p->mapping->direction);
++ pa_device_port_new_data_set_available(&dp_data, PA_AVAILABLE_YES);
+
+- pa_device_port_new_data port_data;
+- pa_device_port_new_data_init(&port_data);
+- pa_device_port_new_data_set_name(&port_data, p->name);
+- pa_device_port_new_data_set_description(&port_data, p->description);
+- pa_device_port_new_data_set_direction(&port_data, p->mapping->direction);
+- dp = pa_device_port_new(core, &port_data, sizeof(pa_droid_port_data));
+- pa_device_port_new_data_done(&port_data);
++ dp = pa_device_port_new(core, &dp_data, sizeof(pa_droid_port_data));
+ dp->priority = p->priority;
+
++ pa_device_port_new_data_done(&dp_data);
++
+ pa_hashmap_put(ports, dp->name, dp);
+- dp->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_profile_free);
++ dp->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ data = PA_DEVICE_PORT_DATA(dp);
+ data->device = p->device;
+ } else
+ pa_log_debug(" Port %s from cache", p->name);
+
+- /* If port/jack detection is available, start as not available by default */
+- dp->available = check_port_availability(p->name) ? PA_AVAILABLE_NO : PA_AVAILABLE_UNKNOWN;
+-
+- if (cp)
+- pa_hashmap_put(dp->profiles, cp->name, cp);
++ if (cp) {
++ if (!pa_hashmap_get(dp->profiles, cp->name))
++ pa_hashmap_put(dp->profiles, cp->name, cp);
++ }
+
+ count++;
+
+ if (extra) {
+- pa_hashmap_put(extra, dp->name, dp);
+- pa_device_port_ref(dp);
++ if (!pa_hashmap_get(extra, dp->name)) {
++ pa_hashmap_put(extra, dp->name, dp);
++ pa_device_port_ref(dp);
++ }
+ }
+ }
+
+@@ -1032,15 +1358,14 @@ static pa_droid_hw_module *droid_hw_modu
+
+ hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, module->name, (const hw_module_t**) &hwmod);
+ if (!hwmod) {
+- pa_log("Failed to get hw module id: %s name: %s, trying alternative.", AUDIO_HARDWARE_MODULE_ID, module->name);
+- hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID2, module->name, (const hw_module_t**) &hwmod);
+- if (!hwmod) {
+- pa_log("Failed to get hw module id: %s name: %s.", AUDIO_HARDWARE_MODULE_ID2, module->name);
+- goto fail;
+- }
++ pa_log("Failed to get hw module %s.", module->name);
++ goto fail;
+ }
+
+- pa_log_info("Loaded hw module %s", module->name);
++ pa_log_info("Loaded hw module %s (HAL %d.%d.%d)", module->name,
++ ANDROID_VERSION_MAJOR,
++ ANDROID_VERSION_MINOR,
++ ANDROID_VERSION_PATCH);
+
+ ret = audio_hw_device_open(hwmod, &device);
+ if (!device) {
+@@ -1058,11 +1383,15 @@ static pa_droid_hw_module *droid_hw_modu
+ hw->core = core;
+ hw->hwmod = hwmod;
+ hw->hw_mutex = pa_mutex_new(true, false);
++ hw->output_mutex = pa_mutex_new(true, false);
++ hw->input_mutex = pa_mutex_new(true, false);
+ hw->device = device;
+ hw->config = config; /* We take ownership of config struct. */
+ hw->enabled_module = pa_droid_config_find_module(hw->config, module_id);
+ hw->module_id = hw->enabled_module->name;
+ hw->shared_name = shared_name_get(hw->module_id);
++ hw->outputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
++ hw->inputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+ pa_assert_se(pa_shared_set(core, hw->shared_name, hw) >= 0);
+
+@@ -1117,9 +1446,25 @@ static void droid_hw_module_close(pa_dro
+ if (hw->hw_mutex)
+ pa_mutex_free(hw->hw_mutex);
+
++ if (hw->output_mutex)
++ pa_mutex_free(hw->output_mutex);
++
++ if (hw->input_mutex)
++ pa_mutex_free(hw->input_mutex);
++
+ if (hw->shared_name)
+ pa_xfree(hw->shared_name);
+
++ if (hw->outputs) {
++ pa_assert(pa_idxset_size(hw->outputs) == 0);
++ pa_idxset_free(hw->outputs, NULL);
++ }
++
++ if (hw->inputs) {
++ pa_assert(pa_idxset_size(hw->inputs) == 0);
++ pa_idxset_free(hw->inputs, NULL);
++ }
++
+ pa_xfree(hw);
+ }
+
+@@ -1187,3 +1532,404 @@ void pa_droid_hw_module_unlock(pa_droid_
+
+ pa_mutex_unlock(hw->hw_mutex);
+ }
++
++static pa_droid_stream *droid_stream_new(pa_droid_hw_module *module) {
++ pa_droid_stream *s;
++
++ s = pa_xnew0(pa_droid_stream, 1);
++ PA_REFCNT_INIT(s);
++
++ s->module = module;
++
++ return s;
++}
++
++pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
++ const pa_sample_spec *spec,
++ const pa_channel_map *map,
++ audio_output_flags_t flags,
++ audio_devices_t devices) {
++ pa_droid_stream *s = NULL;
++ int ret;
++ struct audio_stream_out *stream;
++ audio_format_t hal_audio_format = 0;
++ audio_channel_mask_t hal_channel_mask = 0;
++ struct audio_config config_out;
++ size_t buffer_size;
++
++ pa_assert(module);
++ pa_assert(spec);
++ pa_assert(map);
++
++ if (!pa_convert_format(spec->format, CONV_FROM_PA, &hal_audio_format)) {
++ pa_log("Sample spec format %u not supported.", spec->format);
++ goto fail;
++ }
++
++ for (int i = 0; i < map->channels; i++) {
++ audio_channel_mask_t c;
++ if (!pa_convert_output_channel(map->map[i], CONV_FROM_PA, &c)) {
++ pa_log("Failed to convert channel map.");
++ goto fail;
++ }
++ hal_channel_mask |= c;
++ }
++
++ config_out.sample_rate = spec->rate;
++ config_out.channel_mask = hal_channel_mask;
++ config_out.format = hal_audio_format;
++
++ pa_droid_hw_module_lock(module);
++ ret = module->device->open_output_stream(module->device,
++ module->stream_out_id++,
++ devices,
++ flags,
++ &config_out,
++ &stream
++#if DROID_HAL >= 3
++ /* Go with empty address, should work
++ * with most devices for now. */
++ , NULL
++#endif
++ );
++ pa_droid_hw_module_unlock(module);
++
++ if (ret < 0 || !stream) {
++ pa_log("Failed to open output stream: %d", ret);
++ goto fail;
++ }
++
++ s = droid_stream_new(module);
++ s->out = stream;
++ s->sample_spec = *spec;
++ s->channel_map = *map;
++ s->flags = flags;
++
++ if ((s->sample_spec.rate = s->out->common.get_sample_rate(&s->out->common)) != spec->rate)
++ pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate);
++
++ pa_idxset_put(module->outputs, s, NULL);
++
++ buffer_size = s->out->common.get_buffer_size(&s->out->common);
++
++ pa_log_info("Opened droid output stream %p with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u) buffer size: %u (%llu usec)",
++ (void *) s,
++ devices,
++ s->flags,
++ s->sample_spec.rate,
++ s->sample_spec.channels, hal_channel_mask,
++ s->sample_spec.format, hal_audio_format,
++ buffer_size,
++ pa_bytes_to_usec(buffer_size, &s->sample_spec));
++
++ return s;
++
++fail:
++ pa_xfree(s);
++
++ return NULL;
++}
++
++pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *module,
++ const pa_sample_spec *spec,
++ const pa_channel_map *map,
++ audio_devices_t devices) {
++
++ pa_droid_stream *s = NULL;
++ int ret;
++ audio_stream_in_t *stream;
++ audio_format_t hal_audio_format = 0;
++ audio_channel_mask_t hal_channel_mask = 0;
++ pa_channel_map channel_map;
++ pa_sample_spec sample_spec;
++ bool voicecall_record = false;
++ struct audio_config config_in;
++ size_t buffer_size;
++
++#if DROID_HAL >= 2
++ if ((devices & ~AUDIO_DEVICE_BIT_IN) & AUDIO_DEVICE_IN_VOICE_CALL)
++#else
++ if (devices & AUDIO_DEVICE_IN_VOICE_CALL)
++#endif
++ voicecall_record = true;
++
++ channel_map = *map;
++ sample_spec = *spec;
++
++ if (!pa_convert_format(spec->format, CONV_FROM_PA, &hal_audio_format)) {
++ pa_log("Sample spec format %u not supported.", spec->format);
++ goto fail;
++ }
++
++ for (int i = 0; i < map->channels; i++) {
++ audio_channel_mask_t c;
++ if (!pa_convert_input_channel(map->map[i], CONV_FROM_PA, &c)) {
++ pa_log("Failed to convert channel map.");
++ goto fail;
++ }
++ hal_channel_mask |= c;
++ }
++
++ if (voicecall_record) {
++ pa_channel_map_init_mono(&channel_map);
++ sample_spec.channels = 1;
++ /* Only allow recording both downlink and uplink. */
++#ifdef QCOM_HARDWARE
++ hal_channel_mask = AUDIO_CHANNEL_IN_VOICE_CALL_MONO;
++#else
++ hal_channel_mask = AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK;
++#endif
++ }
++
++ config_in.sample_rate = sample_spec.rate;
++ config_in.channel_mask = hal_channel_mask;
++ config_in.format = hal_audio_format;
++
++ pa_droid_hw_module_lock(module);
++ ret = module->device->open_input_stream(module->device,
++ module->stream_in_id++,
++ devices,
++ &config_in,
++ &stream
++#if DROID_HAL >= 3
++ , AUDIO_INPUT_FLAG_NONE /* Default to no input flags */
++ , NULL /* Don't define address */
++ , AUDIO_SOURCE_DEFAULT /* Default audio source */
++#endif
++ );
++ pa_droid_hw_module_unlock(module);
++
++ if (ret < 0 || !stream) {
++ pa_log("Failed to open input stream: %d", ret);
++ goto fail;
++ }
++
++ s = droid_stream_new(module);
++ s->in = stream;
++ s->sample_spec = sample_spec;
++ s->channel_map = channel_map;
++ s->flags = 0;
++
++ if ((s->sample_spec.rate = s->in->common.get_sample_rate(&s->in->common)) != spec->rate)
++ pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate);
++
++ pa_idxset_put(module->inputs, s, NULL);
++
++ buffer_size = s->in->common.get_buffer_size(&s->in->common);
++
++ pa_log_info("Opened droid input stream %p with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u) buffer size: %u (%llu usec)",
++ (void *) s,
++ devices,
++ s->flags,
++ s->sample_spec.rate,
++ s->sample_spec.channels, hal_channel_mask,
++ s->sample_spec.format, hal_audio_format,
++ buffer_size,
++ pa_bytes_to_usec(buffer_size, &s->sample_spec));
++
++ return s;
++
++fail:
++ pa_xfree(s);
++
++ return NULL;
++}
++
++pa_droid_stream *pa_droid_stream_ref(pa_droid_stream *s) {
++ pa_assert(s);
++ pa_assert(s->out || s->in);
++ pa_assert(PA_REFCNT_VALUE(s) >= 1);
++
++ PA_REFCNT_INC(s);
++ return s;
++}
++
++void pa_droid_stream_unref(pa_droid_stream *s) {
++ pa_assert(s);
++ pa_assert(s->out || s->in);
++ pa_assert(PA_REFCNT_VALUE(s) >= 1);
++
++ if (PA_REFCNT_DEC(s) > 0)
++ return;
++
++ if (s->out) {
++ pa_mutex_lock(s->module->output_mutex);
++ pa_idxset_remove_by_data(s->module->outputs, s, NULL);
++ s->module->device->close_output_stream(s->module->device, s->out);
++ pa_mutex_unlock(s->module->output_mutex);
++ } else {
++ pa_mutex_lock(s->module->input_mutex);
++ pa_idxset_remove_by_data(s->module->inputs, s, NULL);
++ s->module->device->close_input_stream(s->module->device, s->in);
++ pa_mutex_unlock(s->module->input_mutex);
++ }
++
++ pa_xfree(s);
++}
++
++static pa_droid_stream *get_primary_output(pa_droid_hw_module *hw) {
++ pa_droid_stream *s;
++ uint32_t idx;
++
++ pa_assert(hw);
++ pa_assert(hw->outputs);
++
++ PA_IDXSET_FOREACH(s, hw->outputs, idx) {
++ if (s->flags & AUDIO_OUTPUT_FLAG_PRIMARY)
++ return s;
++ }
++
++ return NULL;
++}
++
++int pa_droid_stream_set_output_route(pa_droid_stream *s, audio_devices_t device) {
++ pa_droid_stream *slave;
++ uint32_t idx;
++ char *parameters;
++ int ret;
++
++ pa_assert(s);
++ pa_assert(s->out);
++ pa_assert(s->module);
++ pa_assert(s->module->output_mutex);
++
++ pa_mutex_lock(s->module->output_mutex);
++
++ parameters = pa_sprintf_malloc("%s=%u;", AUDIO_PARAMETER_STREAM_ROUTING, device);
++
++ if (s->flags & AUDIO_OUTPUT_FLAG_PRIMARY || get_primary_output(s->module) == NULL) {
++ pa_log_debug("output stream %p set_parameters(%s) %#010x", (void *) s, parameters, device);
++ ret = s->out->common.set_parameters(&s->out->common, parameters);
++
++ if (ret < 0) {
++ if (ret == -ENOSYS)
++ pa_log_warn("output set_parameters(%s) not allowed while stream is active", parameters);
++ else
++ pa_log_warn("output set_parameters(%s) failed", parameters);
++ }
++ }
++
++ if (s->flags & AUDIO_OUTPUT_FLAG_PRIMARY && pa_idxset_size(s->module->outputs) > 1) {
++
++ PA_IDXSET_FOREACH(slave, s->module->outputs, idx) {
++ if (slave == s)
++ continue;
++
++ pa_log_debug("slave output stream %p set_parameters(%s)", (void *) slave, parameters);
++ ret = slave->out->common.set_parameters(&slave->out->common, parameters);
++
++ if (ret < 0) {
++ if (ret == -ENOSYS)
++ pa_log_warn("output set_parameters(%s) not allowed while stream is active", parameters);
++ else
++ pa_log_warn("output set_parameters(%s) failed", parameters);
++ }
++ }
++ }
++
++ pa_xfree(parameters);
++
++ pa_mutex_unlock(s->module->output_mutex);
++
++ return ret;
++}
++
++int pa_droid_stream_set_input_route(pa_droid_stream *s, audio_devices_t device, audio_source_t *new_source) {
++ audio_source_t source = (uint32_t) -1;
++ char *parameters;
++ int ret;
++
++ pa_assert(s);
++ pa_assert(s->in);
++
++#ifdef DROID_DEVICE_I9305
++ device &= ~AUDIO_DEVICE_BIT_IN;
++#endif
++
++ if (pa_input_device_default_audio_source(device, &source))
++#ifdef DROID_AUDIO_HAL_ATOI_FIX
++ parameters = pa_sprintf_malloc("%s=%d;%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, (int32_t) device,
++ AUDIO_PARAMETER_STREAM_INPUT_SOURCE, source);
++#else
++ parameters = pa_sprintf_malloc("%s=%u;%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, device,
++ AUDIO_PARAMETER_STREAM_INPUT_SOURCE, source);
++#endif
++ else
++ parameters = pa_sprintf_malloc("%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, device);
++
++ pa_log_debug("input stream %p set_parameters(%s) %#010x ; %#010x",
++ (void *) s, parameters, device, source);
++
++
++#if defined(DROID_DEVICE_MAKO) || defined(DROID_DEVICE_ANZU) ||\
++ defined(DROID_DEVICE_COCONUT) || defined(DROID_DEVICE_HAIDA) ||\
++ defined(DROID_DEVICE_HALLON) || defined(DROID_DEVICE_IYOKAN) ||\
++ defined(DROID_DEVICE_MANGO) || defined(DROID_DEVICE_SATSUMA) ||\
++ defined(DROID_DEVICE_SMULTRON) || defined(DROID_DEVICE_URUSHI)
++#warning Using mako set_parameters hack.
++ pa_mutex_lock(s->module->hw_mutex);
++ ret = s->module->device->set_parameters(s->module->device, parameters);
++ pa_mutex_unlock(s->module->hw_mutex);
++#else
++ pa_mutex_lock(s->module->input_mutex);
++ ret = s->in->common.set_parameters(&s->in->common, parameters);
++ pa_mutex_unlock(s->module->input_mutex);
++#endif
++
++ if (ret < 0) {
++ if (ret == -ENOSYS)
++ pa_log_warn("input set_parameters(%s) not allowed while stream is active", parameters);
++ else
++ pa_log_warn("input set_parameters(%s) failed", parameters);
++ }
++
++ if (new_source)
++ *new_source = source;
++
++ pa_xfree(parameters);
++
++ return ret;
++}
++
++int pa_droid_stream_set_parameters(pa_droid_stream *s, const char *parameters) {
++ int ret;
++
++ pa_assert(s);
++ pa_assert(s->out || s->in);
++ pa_assert(parameters);
++
++ if (s->out) {
++ pa_log_debug("output stream %p set_parameters(%s)", (void *) s, parameters);
++ pa_mutex_lock(s->module->output_mutex);
++ ret = s->out->common.set_parameters(&s->out->common, parameters);
++ pa_mutex_unlock(s->module->output_mutex);
++ } else {
++ pa_log_debug("input stream %p set_parameters(%s)", (void *) s, parameters);
++ pa_mutex_lock(s->module->input_mutex);
++ ret = s->in->common.set_parameters(&s->in->common, parameters);
++ pa_mutex_unlock(s->module->input_mutex);
++ }
++
++ if (ret < 0)
++ pa_log("%s stream %p set_parameters(%s) failed: %d",
++ s->out ? "output" : "input", (void *) s, parameters, ret);
++
++ return ret;
++}
++
++int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters) {
++ int ret;
++
++ pa_assert(hw);
++ pa_assert(parameters);
++
++ pa_log_debug("hw %p set_parameters(%s)", (void *) hw, parameters);
++ pa_mutex_lock(hw->hw_mutex);
++ ret = hw->device->set_parameters(hw->device, parameters);
++ pa_mutex_unlock(hw->hw_mutex);
++
++ if (ret < 0)
++ pa_log("hw module %p set_parameters(%s) failed: %d", (void *) hw, parameters, ret);
++
++ return ret;
++}
+Index: overlay/src/modules/droid/droid-util.h
+===================================================================
+--- overlay.orig/src/modules/droid/droid-util.h
++++ overlay/src/modules/droid/droid-util.h
+@@ -28,24 +28,36 @@
+ #include <pulsecore/core-util.h>
+ #include <pulsecore/macro.h>
+ #include <pulsecore/mutex.h>
+-#include <pulsecore/modargs.h>
++#include <pulsecore/strlist.h>
+
+-#include <hardware/audio.h>
+-#include <hardware_legacy/audio_policy_conf.h>
++#include <android-config.h>
++
++#if !defined(ANDROID_VERSION_MAJOR) || !defined(ANDROID_VERSION_MINOR) || !defined(ANDROID_VERSION_PATCH)
++#error "ANDROID_VERSION_* not defined. Did you get your headers via extract-headers.sh?"
++#endif
++
++#if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 1
++#include "droid-util-41qc.h"
++#elif ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 2
++#include "droid-util-42.h"
++#elif ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 4
++#include "droid-util-44.h"
++#elif ANDROID_VERSION_MAJOR == 5 && ANDROID_VERSION_MINOR == 1
++#include "droid-util-51.h"
++#else
++#error "No valid ANDROID_VERSION found."
++#endif
+
+ #define PROP_DROID_DEVICES "droid.devices"
+ #define PROP_DROID_FLAGS "droid.flags"
+ #define PROP_DROID_HW_MODULE "droid.hw_module"
+
+-/* Alternative module ID */
+-#define AUDIO_HARDWARE_MODULE_ID2 "libaudio"
+-
+-/* From module-device-restore */
+-#define MODULE_DEVICE_RESTORE_SKIP_PROPERTY "module-device-restore.skip"
++#define PA_DROID_PRIMARY_DEVICE "primary"
+
+ typedef struct pa_droid_hw_module pa_droid_hw_module;
++typedef struct pa_droid_stream pa_droid_stream;
+ typedef struct pa_droid_card_data pa_droid_card_data;
+-typedef void (*common_set_parameters_cb_t)(pa_droid_card_data *card_data, const char *str);
++typedef int (*common_set_parameters_cb_t)(pa_droid_card_data *card_data, const char *str);
+
+ typedef struct pa_droid_config_audio pa_droid_config_audio;
+ typedef struct pa_droid_config_hw_module pa_droid_config_hw_module;
+@@ -59,6 +71,8 @@ struct pa_droid_hw_module {
+ pa_droid_config_audio *config;
+ const pa_droid_config_hw_module *enabled_module;
+ pa_mutex *hw_mutex;
++ pa_mutex *output_mutex;
++ pa_mutex *input_mutex;
+
+ struct hw_module_t *hwmod;
+ audio_hw_device_t *device;
+@@ -68,6 +82,21 @@ struct pa_droid_hw_module {
+ uint32_t stream_out_id;
+ uint32_t stream_in_id;
+
++ pa_idxset *outputs;
++ pa_idxset *inputs;
++};
++
++struct pa_droid_stream {
++ PA_REFCNT_DECLARE;
++
++ pa_droid_hw_module *module;
++
++ pa_sample_spec sample_spec;
++ pa_channel_map channel_map;
++ uint32_t flags;
++
++ struct audio_stream_out *out;
++ struct audio_stream_in *in;
+ };
+
+ struct pa_droid_card_data {
+@@ -92,9 +121,9 @@ typedef struct pa_droid_config_output {
+ const pa_droid_config_hw_module *module;
+
+ char name[AUDIO_HARDWARE_MODULE_ID_MAX_LEN];
+- uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES];
++ uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; /* (uint32_t) -1 -> dynamic */
+ audio_channel_mask_t channel_masks; /* 0 -> dynamic */
+- audio_format_t formats;
++ audio_format_t formats; /* 0 -> dynamic */
+ audio_devices_t devices;
+ audio_output_flags_t flags;
+ } pa_droid_config_output;
+@@ -103,10 +132,13 @@ typedef struct pa_droid_config_input {
+ const pa_droid_config_hw_module *module;
+
+ char name[AUDIO_HARDWARE_MODULE_ID_MAX_LEN];
+- uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES];
++ uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; /* (uint32_t) -1 -> dynamic */
+ audio_channel_mask_t channel_masks; /* 0 -> dynamic */
+- audio_format_t formats;
++ audio_format_t formats; /* 0 -> dynamic */
+ audio_devices_t devices;
++#if DROID_HAL >= 3
++ audio_input_flags_t flags;
++#endif
+ } pa_droid_config_input;
+
+ struct pa_droid_config_hw_module {
+@@ -173,9 +205,10 @@ typedef struct pa_droid_profile {
+ char *description;
+ unsigned priority;
+
+- /* Profile doesn't own the mappings */
+- pa_droid_mapping *output;
+- pa_droid_mapping *input;
++ /* Idxsets contain pa_droid_mapping objects.
++ * Profile doesn't own the mappings. */
++ pa_idxset *output_mappings;
++ pa_idxset *input_mappings;
+
+ } pa_droid_profile;
+
+@@ -225,6 +258,10 @@ char *pa_list_string_output_device(audio
+ char *pa_list_string_input_device(audio_devices_t devices);
+ char *pa_list_string_flags(audio_output_flags_t flags);
+
++/* Get default audio source associated with input device.
++ * Return true if default source was found, false if not. */
++bool pa_input_device_default_audio_source(audio_devices_t input_device, audio_source_t *default_source);
++
+ /* Config parser */
+ bool pa_parse_droid_audio_config(const char *filename, pa_droid_config_audio *config);
+ pa_droid_config_audio *pa_droid_config_load(pa_modargs *ma);
+@@ -236,21 +273,65 @@ const pa_droid_config_hw_module *pa_droi
+
+ /* Profiles */
+ pa_droid_profile_set *pa_droid_profile_set_new(const pa_droid_config_hw_module *module);
++pa_droid_profile_set *pa_droid_profile_set_combined_new(const pa_droid_config_hw_module *module,
++ pa_strlist *inputs,
++ pa_strlist *outputs);
+ void pa_droid_profile_set_free(pa_droid_profile_set *ps);
+
+ pa_droid_profile *pa_droid_profile_new(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input);
++void pa_droid_profile_add_mapping(pa_droid_profile *p, pa_droid_mapping *am);
+ void pa_droid_profile_free(pa_droid_profile *p);
+
+ pa_droid_mapping *pa_droid_mapping_get(pa_droid_profile_set *ps, pa_direction_t direction, const void *data);
++bool pa_droid_mapping_is_primary(pa_droid_mapping *am);
++/* Go through idxset containing pa_droid_mapping objects and if primary output or input
++ * mapping is found, return pointer to that mapping. */
++pa_droid_mapping *pa_droid_idxset_get_primary(pa_idxset *i);
+ void pa_droid_mapping_free(pa_droid_mapping *am);
+
+-/* Add ports from sinks/sources */
++/* Add ports from sinks/sources.
++ * May be called multiple times for one sink/source. */
+ void pa_droid_add_ports(pa_hashmap *ports, pa_droid_mapping *am, pa_card *card);
+-/* Add ports from card */
++/* Add ports from card.
++ * May be called multiple times for one card profile. */
+ void pa_droid_add_card_ports(pa_card_profile *cp, pa_hashmap *ports, pa_droid_mapping *am, pa_core *core);
+
+ /* Pretty port names */
+ bool pa_droid_output_port_name(audio_devices_t value, const char **to_str);
+ bool pa_droid_input_port_name(audio_devices_t value, const char **to_str);
+
++/* Pretty audio source names */
++bool pa_droid_audio_source_name(audio_source_t value, const char **to_str);
++
++/* Module operations */
++int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters);
++
++/* Stream operations */
++pa_droid_stream *pa_droid_stream_ref(pa_droid_stream *s);
++void pa_droid_stream_unref(pa_droid_stream *s);
++
++int pa_droid_stream_set_parameters(pa_droid_stream *s, const char *parameters);
++
++/* Output stream operations */
++pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
++ const pa_sample_spec *spec,
++ const pa_channel_map *map,
++ audio_output_flags_t flags,
++ audio_devices_t devices);
++
++/* Set routing to the output stream, with following side-effects:
++ * - if routing is set to primary output stream, set routing to all other
++ * open streams as well
++ * - if routing is set to non-primary stream and primary stream exists, do nothing
++ * - if routing is set to non-primary stream and primary stream doesn't exist, set routing
++ */
++int pa_droid_stream_set_output_route(pa_droid_stream *s, audio_devices_t device);
++
++/* Input stream operations */
++pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *module,
++ const pa_sample_spec *spec,
++ const pa_channel_map *map,
++ audio_devices_t devices);
++int pa_droid_stream_set_input_route(pa_droid_stream *s, audio_devices_t device, audio_source_t *new_source);
++
+ #endif
+Index: overlay/src/modules/droid/module-droid-card.c
+===================================================================
+--- overlay.orig/src/modules/droid/module-droid-card.c
++++ overlay/src/modules/droid/module-droid-card.c
+@@ -54,9 +54,7 @@
+ #include <pulsecore/card.h>
+ #include <pulsecore/device-port.h>
+ #include <pulsecore/idxset.h>
+-
+-#include <hardware/audio.h>
+-#include <system/audio.h>
++#include <pulsecore/strlist.h>
+
+ //#include <droid/hardware/audio_policy.h>
+ //#include <droid/system/audio_policy.h>
+@@ -68,7 +66,11 @@
+ #include "droid-extcon.h"
+ #endif
+
+-#include "module-droid-card-symdef.h"
++#if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 2
++#include "module-droid-card-19-symdef.h"
++#elif ANDROID_VERSION_MAJOR == 5 && ANDROID_VERSION_MINOR == 1
++#include "module-droid-card-22-symdef.h"
++#endif
+
+ PA_MODULE_AUTHOR("Juho Hämäläinen");
+ PA_MODULE_DESCRIPTION("Droid card");
+@@ -84,10 +86,9 @@ PA_MODULE_USAGE(
+ "voice_source_routing=<route source ports during voice call, default false> "
+ "deferred_volume=<synchronize software and hardware volume changes to avoid momentary jumps?> "
+ "config=<location for droid audio configuration> "
+- "voice_volume_call_mode=<sink volume controls voice volume during call mode, default false> "
+ "voice_property_key=<proplist key searched for sink-input that should control voice call volume> "
+ "voice_property_value=<proplist value for the key for voice control sink-input> "
+- "voice_virtual_stream=<true/false> create virtual stream for voice call volume control (default false)"
++ "combine=<comma separated list of outputs that should be merged to one profile. defaults to none>"
+ );
+
+ static const char* const valid_modargs[] = {
+@@ -97,6 +98,14 @@ static const char* const valid_modargs[]
+ "namereg_fail",
+ "format",
+ "rate",
++ "channels",
++ "channel_map",
++ "sink_rate",
++ "sink_format",
++ "sink_channel_map",
++ "source_rate",
++ "source_format",
++ "source_channel_map",
+ "output_flags",
+ "module_id",
+ "voice_source_routing",
+@@ -110,21 +119,28 @@ static const char* const valid_modargs[]
+ "voice_property_key",
+ "voice_property_value",
+ "voice_virtual_stream",
++ "combine",
+ NULL,
+ };
+
+ #define DEFAULT_MODULE_ID "primary"
+-#define DEFAULT_AUDIO_POLICY_CONF "/system/etc/audio_policy.conf"
+ #define VOICE_CALL_PROFILE_NAME "voicecall"
+ #define VOICE_CALL_PROFILE_DESC "Call mode"
++#define VOICE_RECORD_PROFILE_NAME "voicecall-record"
++#define VOICE_RECORD_PROFILE_DESC "Call mode record"
+ #define RINGTONE_PROFILE_NAME "ringtone"
+ #define RINGTONE_PROFILE_DESC "Ringtone mode"
+ #define COMMUNICATION_PROFILE_NAME "communication"
+ #define COMMUNICATION_PROFILE_DESC "Communication mode"
+
++struct userdata;
++
++typedef bool (*virtual_profile_event_cb)(struct userdata *u, pa_droid_profile *p, bool enabling);
++
+ struct virtual_profile {
+- pa_droid_profile *profile;
+- audio_mode_t mode;
++ pa_card_profile *parent;
++ pa_card_profile *extension;
++ virtual_profile_event_cb event_cb;
+ };
+
+ struct userdata {
+@@ -140,11 +156,8 @@ struct userdata {
+ pa_droid_hw_module *hw_module;
+ pa_droid_card_data card_data;
+
+- struct virtual_profile call_profile;
+- struct virtual_profile comm_profile;
+- struct virtual_profile ring_profile;
+ pa_droid_profile *old_profile;
+-
++ pa_source *voicecall_source;
+ #ifdef HAVE_UDEV
+ pa_droid_extcon *extcon;
+ #endif
+@@ -157,8 +170,50 @@ struct userdata {
+
+ struct profile_data {
+ pa_droid_profile *profile;
++ audio_mode_t mode;
++ bool virtual_profile;
++ /* Variables for virtual profiles: */
++ struct virtual_profile virtual;
+ };
+
++#ifdef DROID_AUDIO_HAL_USE_VSID
++
++/* From hal/voice_extn/voice_extn.c */
++#define AUDIO_PARAMETER_KEY_VSID "vsid"
++#define AUDIO_PARAMETER_KEY_CALL_STATE "call_state"
++
++/* From hal/voice_extn/voice_extn.c */
++#define VOICE2_VSID 0x10DC1000
++#define VOLTE_VSID 0x10C02000
++#define QCHAT_VSID 0x10803000
++#define VOWLAN_VSID 0x10002000
++
++/* From hal/voice.h */
++#define BASE_CALL_STATE 1
++#define CALL_INACTIVE (BASE_CALL_STATE)
++#define CALL_ACTIVE (BASE_CALL_STATE + 1)
++#define VOICE_VSID 0x10C01000
++
++/* For virtual profiles */
++#define VOICE_SESSION_VOICE1_PROFILE_NAME "voicecall-voice1"
++#define VOICE_SESSION_VOICE1_PROFILE_DESC "Call mode, default to voice 1 vsid"
++#define VOICE_SESSION_VOICE2_PROFILE_NAME "voicecall-voice2"
++#define VOICE_SESSION_VOICE2_PROFILE_DESC "Call mode, default to voice 2 vsid"
++#define VOICE_SESSION_VOLTE_PROFILE_NAME "voicecall-volte"
++#define VOICE_SESSION_VOLTE_PROFILE_DESC "Call mode, default to volte vsid"
++#define VOICE_SESSION_QCHAT_PROFILE_NAME "voicecall-qchat"
++#define VOICE_SESSION_QCHAT_PROFILE_DESC "Call mode, default to qchat vsid"
++#define VOICE_SESSION_VOWLAN_PROFILE_NAME "voicecall-vowlan"
++#define VOICE_SESSION_VOWLAN_PROFILE_DESC "Call mode, default to vowlan vsid"
++
++static bool voicecall_voice1_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling);
++static bool voicecall_voice2_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling);
++static bool voicecall_volte_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling);
++static bool voicecall_qchat_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling);
++static bool voicecall_vowlan_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling);
++
++#endif /* DROID_AUDIO_HAL_USE_VSID */
++
+ static void add_disabled_profile(pa_hashmap *profiles) {
+ pa_card_profile *cp;
+ struct profile_data *d;
+@@ -172,10 +227,13 @@ static void add_disabled_profile(pa_hash
+ }
+
+ /* Special profile for calls */
+-static pa_droid_profile* add_virtual_profile(struct userdata *u, const char *name, const char *description, pa_hashmap *profiles) {
++static pa_card_profile* add_virtual_profile(struct userdata *u, const char *name, const char *description,
++ audio_mode_t audio_mode, virtual_profile_event_cb event_cb,
++ pa_available_t available, pa_card_profile *extension_to,
++ pa_hashmap *profiles) {
+ pa_droid_profile *ap;
+ pa_card_profile *cp;
+- struct profile_data *d;
++ struct profile_data *d, *ext;
+
+ pa_assert(u);
+ pa_assert(u->profile_set);
+@@ -191,28 +249,34 @@ static pa_droid_profile* add_virtual_pro
+ pa_hashmap_put(u->profile_set->profiles, ap->name, ap);
+
+ cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data));
++ cp->available = available;
+ d = PA_CARD_PROFILE_DATA(cp);
+ d->profile = ap;
++ d->virtual_profile = true;
++ d->mode = audio_mode;
++ d->virtual.event_cb = event_cb;
++ d->virtual.extension = NULL;
++ if (extension_to) {
++ ext = PA_CARD_PROFILE_DATA(extension_to);
++ ext->virtual.extension = cp;
++ d->virtual.parent = extension_to;
++ } else
++ d->virtual.parent = NULL;
+
+ pa_hashmap_put(profiles, cp->name, cp);
+
+- return ap;
++ return cp;
+ }
+
+-static void set_parameters_cb(pa_droid_card_data *card_data, const char *str) {
++static int set_parameters_cb(pa_droid_card_data *card_data, const char *str) {
+ struct userdata *u;
++ int ret;
+
+ pa_assert(card_data);
++ pa_assert_se((u = card_data->userdata));
+ pa_assert(str);
+
+- u = card_data->userdata;
+-
+- if (u) {
+- pa_log_debug("Setting parameters: %s", str);
+- pa_droid_hw_module_lock(u->hw_module);
+- u->hw_module->device->set_parameters(u->hw_module->device, str);
+- pa_droid_hw_module_unlock(u->hw_module);
+- }
++ return pa_droid_set_parameters(u->hw_module, str);
+ }
+
+ static void set_card_name(pa_modargs *ma, pa_card_new_data *data, const char *module_id) {
+@@ -238,6 +302,9 @@ static void set_card_name(pa_modargs *ma
+ static void add_profile(struct userdata *u, pa_hashmap *h, pa_hashmap *ports, pa_droid_profile *ap) {
+ pa_card_profile *cp;
+ struct profile_data *d;
++ pa_droid_mapping *am;
++ int max_channels;
++ uint32_t idx;
+
+ pa_assert(u);
+ pa_assert(h);
+@@ -247,19 +314,31 @@ static void add_profile(struct userdata
+ pa_log_debug("Card profile %s", ap->name);
+
+ cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data));
++ cp->available = PA_AVAILABLE_YES;
+ cp->priority = ap->priority;
+
+- cp->n_sinks = 1;
+- pa_droid_add_card_ports(cp, ports, ap->output, u->core);
+- cp->max_sink_channels = popcount(ap->output->output->channel_masks);
+- if (ap->input) {
+- pa_droid_add_card_ports(cp, ports, ap->input, u->core);
+- cp->n_sources = 1;
+- cp->max_source_channels = popcount(ap->input->input->channel_masks);
++ max_channels = 0;
++ PA_IDXSET_FOREACH(am, ap->output_mappings, idx) {
++ cp->n_sinks++;
++ pa_droid_add_card_ports(cp, ports, am, u->core);
++ max_channels = popcount(am->output->channel_masks) > max_channels
++ ? popcount(am->output->channel_masks) : max_channels;
++ }
++ cp->max_sink_channels = max_channels;
++
++ max_channels = 0;
++ PA_IDXSET_FOREACH(am, ap->input_mappings, idx) {
++ cp->n_sources++;
++ pa_droid_add_card_ports(cp, ports, am, u->core);
++ max_channels = popcount(am->input->channel_masks) > max_channels
++ ? popcount(am->input->channel_masks) : max_channels;
+ }
++ cp->max_source_channels = max_channels;
+
+ d = PA_CARD_PROFILE_DATA(cp);
+ d->profile = ap;
++ d->virtual_profile = false;
++ d->mode = AUDIO_MODE_NORMAL;
+
+ pa_hashmap_put(h, cp->name, cp);
+ }
+@@ -280,6 +359,7 @@ static void add_profiles(struct userdata
+ static void init_profile(struct userdata *u) {
+ pa_droid_mapping *am;
+ struct profile_data *d;
++ uint32_t idx;
+
+ pa_assert(u);
+
+@@ -287,14 +367,16 @@ static void init_profile(struct userdata
+
+ d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+
+- if (d->profile && d->profile->output) {
+- am = d->profile->output;
+- am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card);
++ if (d->profile && pa_idxset_size(d->profile->output_mappings) > 0) {
++ PA_IDXSET_FOREACH(am, d->profile->output_mappings, idx) {
++ am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card);
++ }
+ }
+
+- if (d->profile && d->profile->input) {
+- am = d->profile->input;
+- am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, &u->card_data, am, u->card);
++ if (d->profile && pa_idxset_size(d->profile->input_mappings) > 0) {
++ PA_IDXSET_FOREACH(am, d->profile->input_mappings, idx) {
++ am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card);
++ }
+ }
+ }
+
+@@ -332,77 +414,300 @@ static int set_mode(struct userdata *u,
+ }
+
+ static void park_profile(pa_droid_profile *dp) {
++ struct profile_data *data;
++ pa_droid_mapping *am;
++ uint32_t idx;
++
+ pa_assert(dp);
+
+- if (dp->output && dp->output->sink)
+- pa_sink_set_port(dp->output->sink, PA_DROID_OUTPUT_PARKING, false);
+- if (dp->input && dp->input->source)
+- pa_source_set_port(dp->input->source, PA_DROID_INPUT_PARKING, false);
++ /* Virtual profiles don't have output mappings. */
++ if (dp->output_mappings) {
++ PA_IDXSET_FOREACH(am, dp->output_mappings, idx) {
++ if (pa_droid_mapping_is_primary(am))
++ pa_sink_set_port(am->sink, PA_DROID_OUTPUT_PARKING, false);
++ }
++ };
++
++ /* Virtual profiles don't have input mappings. */
++ if (dp->input_mappings) {
++ PA_IDXSET_FOREACH(am, dp->input_mappings, idx) {
++ if (pa_droid_mapping_is_primary(am))
++ pa_source_set_port(am->source, PA_DROID_INPUT_PARKING, false);
++ }
++ };
++}
++
++static bool voicecall_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) {
++ pa_card_profile *cp;
++ pa_droid_mapping *am_output, *am_input;
++
++ pa_assert(u);
++ pa_assert(p);
++ pa_assert(u->old_profile);
++
++ if (!(am_output = pa_droid_idxset_get_primary(u->old_profile->output_mappings))) {
++ pa_log("Active profile doesn't have primary output device.");
++ return false;
++ }
++
++ if (!(am_input = pa_droid_idxset_get_primary(u->old_profile->input_mappings)))
++ pa_log_warn("Active profile doesn't have primary input device.");
++
++ /* call mode specialities */
++ if (enabling) {
++ pa_droid_sink_set_voice_control(am_output->sink, true);
++ if (am_input && !u->voice_source_routing)
++ pa_droid_source_set_routing(am_input->source, false);
++ if (am_input && am_input->input->devices & AUDIO_DEVICE_IN_VOICE_CALL &&
++ (cp = pa_hashmap_get(u->card->profiles, VOICE_RECORD_PROFILE_NAME))) {
++ if (cp->available == PA_AVAILABLE_NO) {
++ pa_log_debug("Enable %s profile.", VOICE_RECORD_PROFILE_NAME);
++ pa_card_profile_set_available(cp, PA_AVAILABLE_YES);
++ }
++ }
++ } else {
++ pa_droid_sink_set_voice_control(am_output->sink, false);
++ if (am_input && !u->voice_source_routing)
++ pa_droid_source_set_routing(am_input->source, true);
++ if (am_input && am_input->input->devices & AUDIO_DEVICE_IN_VOICE_CALL &&
++ (cp = pa_hashmap_get(u->card->profiles, VOICE_RECORD_PROFILE_NAME))) {
++ if (cp->available == PA_AVAILABLE_YES) {
++ pa_log_debug("Disable %s profile.", VOICE_RECORD_PROFILE_NAME);
++ pa_card_profile_set_available(cp, PA_AVAILABLE_NO);
++ }
++ }
++ }
++
++ return true;
++}
++
++#if DROID_HAL == 1
++static bool voicecall_record_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) {
++ pa_queue *source_outputs = NULL;
++ pa_droid_mapping *am;
++
++ pa_assert_ctl_context();
++ pa_assert(u);
++ pa_assert(p);
++ pa_assert(u->old_profile);
++
++ if (enabling) {
++ /* don't do anything if voicecall source has already been created. */
++ if (u->voicecall_source)
++ return true;
++
++ pa_log_info("Enabling voice call record.");
++
++ am = pa_droid_idxset_get_primary(u->old_profile->input_mappings);
++
++ if (am && am->source) {
++ source_outputs = pa_source_move_all_start(am->source, source_outputs);
++ pa_droid_source_free(am->source);
++ am->source = NULL;
++ }
++
++ u->voicecall_source = pa_droid_source_new(u->module, u->modargs, __FILE__, AUDIO_DEVICE_IN_VOICE_CALL, &u->card_data, am, u->card);
++ if (!u->voicecall_source)
++ pa_log("Failed to enable voice call recording.");
++
++ if (u->voicecall_source && source_outputs) {
++ pa_source_move_all_finish(u->voicecall_source, source_outputs, false);
++ source_outputs = NULL;
++ }
++
++ } else {
++ /* don't do anything if voicecall source has already been destroyed. */
++ if (!u->voicecall_source)
++ return true;
++
++ pa_log_info("Disabling voice call record.");
++
++ source_outputs = pa_source_move_all_start(u->voicecall_source, source_outputs);
++ pa_droid_source_free(u->voicecall_source);
++ u->voicecall_source = NULL;
++
++ am = pa_droid_idxset_get_primary(u->old_profile->input_mappings);
++
++ if (am && !am->source) {
++ am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card);
++
++ if (source_outputs && am->source) {
++ pa_source_move_all_finish(am->source, source_outputs, false);
++ source_outputs = NULL;
++ }
++ }
++ }
++
++ if (source_outputs)
++ pa_source_move_all_fail(source_outputs);
++
++ return true;
++}
++
++#else
++
++static bool voicecall_record_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) {
++ pa_droid_mapping *am;
++ pa_device_port *port;
++ const char *port_name;
++
++ pa_assert_ctl_context();
++ pa_assert(u);
++ pa_assert(p);
++ pa_assert(u->old_profile);
++
++ if (!(am = pa_droid_idxset_get_primary(u->old_profile->input_mappings))) {
++ pa_log("Active profile doesn't have primary input device. Cannot enable record profile.");
++ return false;
++ }
++
++ if (!am->source) {
++ pa_log("No active source, refusing to switch source port.");
++ return false;
++ }
++
++ pa_source_assert_ref(am->source);
++
++ pa_assert_se(pa_droid_input_port_name(enabling ? AUDIO_DEVICE_IN_VOICE_CALL : AUDIO_DEVICE_IN_DEFAULT,
++ &port_name));
++ pa_assert_se((port = pa_hashmap_get(am->source->ports, port_name)));
++
++ if (pa_droid_source_set_port(am->source, port) != 0)
++ return false;
++
++ pa_hook_fire(&u->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], am->source);
++
++ return true;
++}
++#endif
++
++#ifdef DROID_AUDIO_HAL_USE_VSID
++static bool voicecall_vsid(struct userdata *u, pa_droid_profile *p, uint32_t vsid, bool enabling)
++{
++ char *setparam;
++
++ voicecall_profile_event_cb(u, p, enabling);
++
++ setparam = pa_sprintf_malloc("%s=%u;%s=%d", AUDIO_PARAMETER_KEY_VSID, vsid,
++ AUDIO_PARAMETER_KEY_CALL_STATE,
++ enabling ? CALL_ACTIVE : CALL_INACTIVE);
++
++ pa_droid_set_parameters(u->hw_module, setparam);
++ pa_xfree(setparam);
++
++ return true;
++}
++
++static bool voicecall_voice1_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling)
++{
++ return voicecall_vsid(u, p, VOICE_VSID, enabling);
++}
++
++static bool voicecall_voice2_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling)
++{
++ return voicecall_vsid(u, p, VOICE2_VSID, enabling);
++}
++
++static bool voicecall_volte_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling)
++{
++ return voicecall_vsid(u, p, VOLTE_VSID, enabling);
++}
++
++static bool voicecall_qchat_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling)
++{
++ return voicecall_vsid(u, p, QCHAT_VSID, enabling);
++}
++
++static bool voicecall_vowlan_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling)
++{
++ return voicecall_vsid(u, p, VOWLAN_VSID, enabling);
++}
++#endif /* DROID_AUDIO_HAL_USE_VSID */
++
++static void leave_virtual_profile(struct userdata *u, pa_card *c, pa_card_profile *cp, pa_card_profile *new_profile) {
++ struct profile_data *pd, *parent;
++
++ pa_assert(u);
++ pa_assert(c);
++ pa_assert(cp);
++ pa_assert_se((pd = PA_CARD_PROFILE_DATA(cp)));
++
++ if (pd->virtual.parent)
++ pa_log_debug("Leaving extension %s.", cp->name);
++
++ /* First run event for old virtual profile, unless new profile is extension
++ * to the old profile. */
++ if (pd->virtual.extension) {
++ if (pd->virtual.extension != new_profile && pd->virtual.event_cb)
++ pd->virtual.event_cb(u, pd->profile, false);
++ } else {
++ if (pd->virtual.event_cb)
++ pd->virtual.event_cb(u, pd->profile, false);
++ }
++
++ /* If old virtual profile was extension, and new profile is not extensions parent, run event
++ * for extension's parent as well. */
++ if (pd->virtual.parent != new_profile && pd->virtual.parent) {
++ parent = PA_CARD_PROFILE_DATA(pd->virtual.parent);
++
++ if (parent->virtual.event_cb)
++ parent->virtual.event_cb(u, parent->profile, false);
++ }
+ }
+
+ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
+ struct userdata *u;
+ pa_droid_mapping *am;
+- struct virtual_profile *new_vp = NULL;
+- struct virtual_profile *old_vp = NULL;
+ struct profile_data *nd, *od;
+ pa_queue *sink_inputs = NULL, *source_outputs = NULL;
++ uint32_t idx;
+
+ pa_assert(c);
+ pa_assert(new_profile);
+ pa_assert_se(u = c->userdata);
+
++ if (new_profile->available != PA_AVAILABLE_YES) {
++ pa_log("Profile %s is not available.", new_profile->name);
++ return -1;
++ }
++
+ nd = PA_CARD_PROFILE_DATA(new_profile);
+ od = PA_CARD_PROFILE_DATA(c->active_profile);
+
+- if (nd->profile == u->call_profile.profile)
+- new_vp = &u->call_profile;
+- if (nd->profile == u->ring_profile.profile)
+- new_vp = &u->ring_profile;
+- if (nd->profile == u->comm_profile.profile)
+- new_vp = &u->comm_profile;
+-
+- if (new_vp) {
+- pa_log_debug("Setting new virtual profile.");
+- if (u->old_profile == NULL)
++ if (nd->virtual_profile) {
++ /* old_profile should always be real profile. */
++ if (u->old_profile == NULL) {
++ pa_assert(!od->virtual_profile);
+ u->old_profile = od->profile;
++ }
++
++ if (od->virtual_profile)
++ leave_virtual_profile(u, c, c->active_profile, new_profile);
+
+ park_profile(od->profile);
+
+- set_mode(u, new_vp->mode);
++ if (nd->mode != od->mode)
++ set_mode(u, nd->mode);
++
++ if (od->virtual.parent != new_profile && nd->virtual.event_cb)
++ nd->virtual.event_cb(u, nd->profile, true);
+
+- /* call mode specialities */
+- if (new_vp->profile == u->call_profile.profile) {
+- pa_droid_sink_set_voice_control(u->old_profile->output->sink, true);
+- if (!u->voice_source_routing)
+- pa_droid_source_set_routing(u->old_profile->input->source, false);
+- }
+ return 0;
+ }
+
+- if (od->profile == u->call_profile.profile)
+- old_vp = &u->call_profile;
+- if (od->profile == u->ring_profile.profile)
+- old_vp = &u->ring_profile;
+- if (od->profile == u->comm_profile.profile)
+- old_vp = &u->comm_profile;
+-
+- if (old_vp) {
++ if (od->virtual_profile) {
+ pa_assert(u->old_profile);
+
+- park_profile(nd->profile);
++ if (od->virtual_profile)
++ leave_virtual_profile(u, c, c->active_profile, NULL);
+
+- set_mode(u, AUDIO_MODE_NORMAL);
++ park_profile(nd->profile);
+
+- /* call mode specialities */
+- if (old_vp->profile == u->call_profile.profile) {
+- pa_droid_sink_set_voice_control(u->old_profile->output->sink, false);
+- if (!u->voice_source_routing)
+- pa_droid_source_set_routing(u->old_profile->input->source, true);
+- }
++ if (nd->mode != od->mode)
++ set_mode(u, nd->mode);
+
+ /* If new profile is the same as from which we switched to
+- * call profile, transfer ownership back to that profile.
++ * virtual profile, transfer ownership back to that profile.
+ * Otherwise destroy sinks & sources and switch to new profile. */
+ if (nd->profile == u->old_profile) {
+ u->old_profile = NULL;
+@@ -411,66 +716,64 @@ static int card_set_profile(pa_card *c,
+ od->profile = u->old_profile;
+ u->old_profile = NULL;
+
+- /* Continue to sink-input transfer below */
++ /* Continue to sink-input/source-output transfer below. */
+ }
+ }
+
+ /* If there are connected sink inputs/source outputs in old profile's sinks/sources move
+ * them all to new sinks/sources. */
+
+- if (od->profile && od->profile->output) {
+- do {
+- am = od->profile->output;
+-
++ if (od->profile && pa_idxset_size(od->profile->output_mappings) > 0) {
++ PA_IDXSET_FOREACH(am, od->profile->output_mappings, idx) {
+ if (!am->sink)
+ continue;
+
+- if (nd->profile && nd->profile->output && am == nd->profile->output)
++ if (nd->profile &&
++ pa_idxset_get_by_data(nd->profile->output_mappings, am, NULL))
+ continue;
+
+ sink_inputs = pa_sink_move_all_start(am->sink, sink_inputs);
+ pa_droid_sink_free(am->sink);
+ am->sink = NULL;
+- } while(0);
++ }
+ }
+
+- if (od->profile && od->profile->input) {
+- do {
+- am = od->profile->input;
+-
++ if (od->profile && pa_idxset_size(od->profile->input_mappings) > 0) {
++ PA_IDXSET_FOREACH(am, od->profile->input_mappings, idx) {
+ if (!am->source)
+ continue;
+
+- if (nd->profile && nd->profile->input && am == nd->profile->input)
++ if (nd->profile &&
++ pa_idxset_get_by_data(nd->profile->input_mappings, am, NULL))
+ continue;
+
+ source_outputs = pa_source_move_all_start(am->source, source_outputs);
+ pa_droid_source_free(am->source);
+ am->source = NULL;
+- } while(0);
++ }
+ }
+
+- if (nd->profile && nd->profile->output) {
+- am = nd->profile->output;
+-
+- if (!am->sink)
+- am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card);
++ if (nd->profile && pa_idxset_size(nd->profile->output_mappings) > 0) {
++ PA_IDXSET_FOREACH(am, nd->profile->output_mappings, idx) {
++ if (!am->sink)
++ am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card);
+
+- if (sink_inputs && am->sink) {
+- pa_sink_move_all_finish(am->sink, sink_inputs, false);
+- sink_inputs = NULL;
++ if (sink_inputs && am->sink) {
++ pa_sink_move_all_finish(am->sink, sink_inputs, false);
++ sink_inputs = NULL;
++ }
+ }
+ }
+
+- if (nd->profile && nd->profile->input) {
+- am = nd->profile->input;
+-
+- if (!am->source)
+- am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, &u->card_data, am, u->card);
++ if (nd->profile && pa_idxset_size(nd->profile->input_mappings) > 0) {
++ PA_IDXSET_FOREACH(am, nd->profile->input_mappings, idx) {
++ if (!am->source)
++ am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card);
+
+- if (source_outputs && am->source) {
+- pa_source_move_all_finish(am->source, source_outputs, false);
+- source_outputs = NULL;
++ if (source_outputs && am->source) {
++ pa_source_move_all_finish(am->source, source_outputs, false);
++ source_outputs = NULL;
++ }
+ }
+ }
+
+@@ -491,14 +794,18 @@ int pa__init(pa_module *m) {
+ const char *module_id;
+ bool namereg_fail = false;
+ bool voice_source_routing = false;
++ pa_card_profile *virtual;
++ const char *combine;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+- pa_log("Failed to parse module argumets.");
++ pa_log("Failed to parse module arguments.");
+ goto fail;
+ }
+
++ combine = pa_modargs_get_value(ma, "combine", NULL);
++
+ struct userdata *u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+
+@@ -521,7 +828,19 @@ int pa__init(pa_module *m) {
+ u->card_data.module_id = pa_xstrdup(module_id);
+ u->card_data.userdata = u;
+
+- u->profile_set = pa_droid_profile_set_new(u->hw_module->enabled_module);
++ if (combine) {
++ char *tmp;
++ pa_strlist *o = NULL;
++
++ tmp = pa_replace(combine, ",", " ");
++ o = pa_strlist_parse(tmp);
++
++ u->profile_set = pa_droid_profile_set_combined_new(u->hw_module->enabled_module,
++ o, NULL);
++ pa_strlist_free(o);
++ pa_xfree(tmp);
++ } else
++ u->profile_set = pa_droid_profile_set_new(u->hw_module->enabled_module);
+
+ pa_card_new_data_init(&data);
+ data.driver = __FILE__;
+@@ -549,15 +868,37 @@ int pa__init(pa_module *m) {
+ goto fail;
+ }
+
+- u->call_profile.profile = add_virtual_profile(u, VOICE_CALL_PROFILE_NAME,
+- VOICE_CALL_PROFILE_DESC, data.profiles);
+- u->call_profile.mode = AUDIO_MODE_IN_CALL;
+- u->comm_profile.profile = add_virtual_profile(u, COMMUNICATION_PROFILE_NAME,
+- COMMUNICATION_PROFILE_DESC, data.profiles);
+- u->comm_profile.mode = AUDIO_MODE_IN_COMMUNICATION;
+- u->ring_profile.profile = add_virtual_profile(u, RINGTONE_PROFILE_NAME,
+- RINGTONE_PROFILE_DESC, data.profiles);
+- u->ring_profile.mode = AUDIO_MODE_RINGTONE;
++ virtual =
++ add_virtual_profile(u, VOICE_CALL_PROFILE_NAME, VOICE_CALL_PROFILE_DESC,
++ AUDIO_MODE_IN_CALL, voicecall_profile_event_cb,
++ PA_AVAILABLE_YES, NULL, data.profiles);
++ add_virtual_profile(u, VOICE_RECORD_PROFILE_NAME, VOICE_RECORD_PROFILE_DESC,
++ AUDIO_MODE_IN_CALL, voicecall_record_profile_event_cb,
++ PA_AVAILABLE_NO, virtual, data.profiles);
++ add_virtual_profile(u, COMMUNICATION_PROFILE_NAME, COMMUNICATION_PROFILE_DESC,
++ AUDIO_MODE_IN_COMMUNICATION, NULL,
++ PA_AVAILABLE_YES, NULL, data.profiles);
++ add_virtual_profile(u, RINGTONE_PROFILE_NAME, RINGTONE_PROFILE_DESC,
++ AUDIO_MODE_RINGTONE, NULL,
++ PA_AVAILABLE_YES, NULL, data.profiles);
++#ifdef DROID_AUDIO_HAL_USE_VSID
++ add_virtual_profile(u, VOICE_SESSION_VOICE1_PROFILE_NAME, VOICE_SESSION_VOICE1_PROFILE_DESC,
++ AUDIO_MODE_IN_CALL, voicecall_voice1_vsid_profile_event_cb,
++ PA_AVAILABLE_YES, NULL, data.profiles);
++ add_virtual_profile(u, VOICE_SESSION_VOICE2_PROFILE_NAME, VOICE_SESSION_VOICE2_PROFILE_DESC,
++ AUDIO_MODE_IN_CALL, voicecall_voice2_vsid_profile_event_cb,
++ PA_AVAILABLE_YES, NULL, data.profiles);
++ /* TODO: Probably enabled state needs to be determined dynamically for VOLTE and friends. */
++ add_virtual_profile(u, VOICE_SESSION_VOLTE_PROFILE_NAME, VOICE_SESSION_VOLTE_PROFILE_DESC,
++ AUDIO_MODE_IN_CALL, voicecall_volte_vsid_profile_event_cb,
++ PA_AVAILABLE_YES, NULL, data.profiles);
++ add_virtual_profile(u, VOICE_SESSION_QCHAT_PROFILE_NAME, VOICE_SESSION_QCHAT_PROFILE_DESC,
++ AUDIO_MODE_IN_CALL, voicecall_qchat_vsid_profile_event_cb,
++ PA_AVAILABLE_YES, NULL, data.profiles);
++ add_virtual_profile(u, VOICE_SESSION_VOWLAN_PROFILE_NAME, VOICE_SESSION_VOWLAN_PROFILE_DESC,
++ AUDIO_MODE_IN_CALL, voicecall_vowlan_vsid_profile_event_cb,
++ PA_AVAILABLE_YES, NULL, data.profiles);
++#endif /* DROID_AUDIO_HAL_USE_VSID */
+
+ add_disabled_profile(data.profiles);
+
+Index: overlay/src/modules/droid/module-droid-discover.c
+===================================================================
+--- /dev/null
++++ overlay/src/modules/droid/module-droid-discover.c
+@@ -0,0 +1,95 @@
++/***
++ This file is part of PulseAudio.
++
++ Copyright 2016 Simon Fels <simon.fels at canonical.com>
++
++ PulseAudio is free software; you can redistribute it and/or modify
++ it under the terms of the GNU Lesser General Public License as
++ published by the Free Software Foundation; either version 2.1 of the
++ License, or (at your option) any later version.
++
++ PulseAudio is distributed in the hope that it will be useful, but
++ WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
++***/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <pulsecore/core-util.h>
++#include <pulsecore/macro.h>
++#include <pulsecore/module.h>
++
++#include <hybris/properties/properties.h>
++
++#include "module-droid-discover-symdef.h"
++
++PA_MODULE_AUTHOR("Simon Fels");
++PA_MODULE_DESCRIPTION("Detect on which Android version we're running and load correct implementation for it");
++PA_MODULE_VERSION(PACKAGE_VERSION);
++PA_MODULE_LOAD_ONCE(true);
++
++#define ANDROID_PROPERTY_VERSION_RELEASE "ro.build.version.release"
++
++#define MODULE_DROID_CARD_PREFIX "module-droid-card-"
++
++struct userdata {
++ uint32_t module_idx;
++};
++
++int pa__init(pa_module* m) {
++ struct userdata *u;
++ pa_module *mm;
++ char version[50];
++ char *module_name = NULL;
++
++ pa_assert(m);
++
++ if (property_get(ANDROID_PROPERTY_VERSION_RELEASE, version, "") < 0 || !version) {
++ pa_log("Failed to determine Android platform version we're running on");
++ return -1;
++ }
++
++ m->userdata = u = pa_xnew0(struct userdata, 1);
++
++ if (!strncmp(version, "5.1", 3) && pa_module_exists(MODULE_DROID_CARD_PREFIX "22"))
++ module_name = MODULE_DROID_CARD_PREFIX "22";
++ else if (!strncmp(version, "4.4", 3) && pa_module_exists(MODULE_DROID_CARD_PREFIX "19"))
++ module_name = MODULE_DROID_CARD_PREFIX "19";
++ else {
++ pa_log("Unsupported Android version %s", version);
++ pa_xfree(u);
++ return -1;
++ }
++
++ mm = pa_module_load(m->core, module_name, m->argument);
++ if (mm)
++ u->module_idx = mm->index;
++
++ if (u->module_idx == PA_INVALID_INDEX) {
++ pa_xfree(u);
++ pa_log("Failed to load droid card module for Android version %s", version);
++ return -1;
++ }
++
++ return 0;
++}
++
++void pa__done(pa_module* m) {
++ struct userdata *u;
++
++ pa_assert(m);
++
++ if (!(u = m->userdata))
++ return;
++
++ if (u->module_idx != PA_INVALID_INDEX)
++ pa_module_unload_by_index(m->core, u->module_idx, true);
++
++ pa_xfree(u);
++}
+Index: overlay/src/modules/droid/module-droid-sink.c
+===================================================================
+--- overlay.orig/src/modules/droid/module-droid-sink.c
++++ overlay/src/modules/droid/module-droid-sink.c
+@@ -40,30 +40,36 @@
+ #include "droid-util.h"
+ #include "droid-sink.h"
+
+-#include "module-droid-sink-symdef.h"
++#if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 2
++#include "module-droid-sink-19-symdef.h"
++#elif ANDROID_VERSION_MAJOR == 5 && ANDROID_VERSION_MINOR == 1
++#include "module-droid-sink-22-symdef.h"
++#endif
+
+ PA_MODULE_AUTHOR("Juho Hämäläinen");
+ PA_MODULE_DESCRIPTION("Droid sink");
+ PA_MODULE_USAGE("master_sink=<sink to connect to> "
+- "sink_name=<name of created sink> "
+- "sco_fake_sink=<name of the fake sco sink used for hsp>");
++ "sink_name=<name of created sink>");
+ PA_MODULE_VERSION(PACKAGE_VERSION);
+
+ static const char* const valid_modargs[] = {
+ "rate",
++ "format",
++ "channels",
++ "channel_map",
++ "sink_rate",
++ "sink_format",
++ "sink_channel_map",
+ "flags",
+- "devices",
++ "output_devices",
+ "sink_name",
+ "module_id",
+ "mute_routing_before",
+ "mute_routing_after",
+ "sink_buffer",
+ "deferred_volume",
+- "voice_volume_call_mode",
+ "voice_property_key",
+ "voice_property_value",
+- "voice_virtual_stream",
+- "sco_fake_sink",
+ NULL,
+ };
+
+@@ -78,15 +84,24 @@ void pa__done(pa_module *m) {
+
+ int pa__init(pa_module *m) {
+ pa_modargs *ma = NULL;
++ const char *flags_str;
++ audio_output_flags_t flags = 0;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+- pa_log("Failed to parse module argumets.");
++ pa_log("Failed to parse module arguments.");
+ goto fail;
+ }
+
+- if (!(m->userdata = pa_droid_sink_new(m, ma, __FILE__, NULL, 0, NULL, NULL)))
++ if ((flags_str = pa_modargs_get_value(ma, "flags", NULL))) {
++ if (!pa_string_convert_flag_str_to_num(flags_str, &flags)) {
++ pa_log("Failed to parse flags");
++ goto fail;
++ }
++ }
++
++ if (!(m->userdata = pa_droid_sink_new(m, ma, __FILE__, NULL, flags, NULL, NULL)))
+ goto fail;
+
+ pa_modargs_free(ma);
+Index: overlay/src/modules/droid/module-droid-source.c
+===================================================================
+--- overlay.orig/src/modules/droid/module-droid-source.c
++++ overlay/src/modules/droid/module-droid-source.c
+@@ -40,7 +40,11 @@
+ #include "droid-util.h"
+ #include "droid-source.h"
+
+-#include "module-droid-source-symdef.h"
++#if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 2
++#include "module-droid-source-19-symdef.h"
++#elif ANDROID_VERSION_MAJOR == 5 && ANDROID_VERSION_MINOR == 1
++#include "module-droid-source-22-symdef.h"
++#endif
+
+ PA_MODULE_AUTHOR("Juho Hämäläinen");
+ PA_MODULE_DESCRIPTION("Droid source");
+@@ -50,8 +54,14 @@ PA_MODULE_VERSION(PACKAGE_VERSION);
+
+ static const char* const valid_modargs[] = {
+ "rate",
++ "format",
++ "channels",
++ "channel_map",
++ "source_rate",
++ "source_format",
++ "source_channel_map",
+ "flags",
+- "devices",
++ "input_devices",
+ "source_name",
+ "module_id",
+ "source_buffer",
+@@ -74,11 +84,11 @@ int pa__init(pa_module *m) {
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+- pa_log("Failed to parse module argumets.");
++ pa_log("Failed to parse module arguments.");
+ goto fail;
+ }
+
+- if (!(m->userdata = pa_droid_source_new(m, ma, __FILE__, NULL, NULL, NULL)))
++ if (!(m->userdata = pa_droid_source_new(m, ma, __FILE__, (audio_devices_t) 0, NULL, NULL, NULL)))
+ goto fail;
+
+ pa_modargs_free(ma);
+Index: overlay/src/modules/module-device-restore.c
+===================================================================
+--- overlay.orig/src/modules/module-device-restore.c
++++ overlay/src/modules/module-device-restore.c
+@@ -824,6 +824,7 @@ static pa_hook_result_t sink_port_hook_c
+ pa_assert(u);
+ pa_assert(u->restore_volume || u->restore_muted);
+
++#define MODULE_DEVICE_RESTORE_SKIP_PROPERTY "module-device-restore.skip"
+ if (pa_proplist_gets(sink->proplist, MODULE_DEVICE_RESTORE_SKIP_PROPERTY))
+ return PA_HOOK_OK;
+
diff --git a/debian/patches/0601-droid-alternative-hw-module-id.patch b/debian/patches/0601-droid-alternative-hw-module-id.patch
new file mode 100644
index 0000000..9a68453
--- /dev/null
+++ b/debian/patches/0601-droid-alternative-hw-module-id.patch
@@ -0,0 +1,34 @@
+Index: pulseaudio/src/modules/droid/droid-util.c
+===================================================================
+--- pulseaudio.orig/src/modules/droid/droid-util.c
++++ pulseaudio/src/modules/droid/droid-util.c
+@@ -1358,8 +1358,13 @@ static pa_droid_hw_module *droid_hw_modu
+
+ hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, module->name, (const hw_module_t**) &hwmod);
+ if (!hwmod) {
+- pa_log("Failed to get hw module %s.", module->name);
+- goto fail;
++ /* alternative hardware module id for MTK. */
++ pa_log("Failed to get hw module id: %s name: %s, trying alternative.", AUDIO_HARDWARE_MODULE_ID, module->name);
++ hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID2, module->name, (const hw_module_t**) &hwmod);
++ if (!hwmod) {
++ pa_log("Failed to get hw module id: %s name: %s.", AUDIO_HARDWARE_MODULE_ID2, module->name);
++ goto fail;
++ }
+ }
+
+ pa_log_info("Loaded hw module %s (HAL %d.%d.%d)", module->name,
+Index: pulseaudio/src/modules/droid/droid-util.h
+===================================================================
+--- pulseaudio.orig/src/modules/droid/droid-util.h
++++ pulseaudio/src/modules/droid/droid-util.h
+@@ -54,6 +54,9 @@
+
+ #define PA_DROID_PRIMARY_DEVICE "primary"
+
++/* Workaround for MTK */
++#define AUDIO_HARDWARE_MODULE_ID2 "libaudio"
++
+ typedef struct pa_droid_hw_module pa_droid_hw_module;
+ typedef struct pa_droid_stream pa_droid_stream;
+ typedef struct pa_droid_card_data pa_droid_card_data;
diff --git a/debian/patches/0602-droid-inputstream-config-parameters.pach b/debian/patches/0602-droid-inputstream-config-parameters.pach
new file mode 100644
index 0000000..55fdc05
--- /dev/null
+++ b/debian/patches/0602-droid-inputstream-config-parameters.pach
@@ -0,0 +1,75 @@
+Index: pulseaudio/src/modules/droid/droid-util.c
+===================================================================
+--- pulseaudio.orig/src/modules/droid/droid-util.c
++++ pulseaudio/src/modules/droid/droid-util.c
+@@ -1134,7 +1158,6 @@ static void add_i_port(pa_droid_mapping
+ static void add_i_ports(pa_droid_mapping *am) {
+ pa_droid_port *p;
+ const char *name;
+- char *desc;
+ uint32_t devices;
+ uint32_t i = 0;
+
+@@ -1697,11 +1723,27 @@ pa_droid_stream *pa_droid_open_input_str
+ &config_in,
+ &stream
+ #if DROID_HAL >= 3
+- , AUDIO_INPUT_FLAG_NONE /* Default to no input flags */
+- , NULL /* Don't define address */
+- , AUDIO_SOURCE_DEFAULT /* Default audio source */
++ , AUDIO_INPUT_FLAG_NONE /* Default to no input flags */
++ , NULL /* Don't define address */
++ , AUDIO_SOURCE_DEFAULT /* Default audio source */
+ #endif
+ );
++ /* On some devices the first call will fail if the config parameters are
++ * not supported, but it'll automatically set the right ones, expecting
++ * the caller to call it again, so let's try at least one more time */
++ if (!stream)
++ ret = module->device->open_input_stream(module->device,
++ module->stream_in_id++,
++ devices,
++ &config_in,
++ &stream
++#if DROID_HAL >= 3
++ , AUDIO_INPUT_FLAG_NONE /* Default to no input flags */
++ , NULL /* Don't define address */
++ , AUDIO_SOURCE_DEFAULT /* Default audio source */
++#endif
++ );
++
+ pa_droid_hw_module_unlock(module);
+
+ if (ret < 0 || !stream) {
+@@ -1715,6 +1757,22 @@ pa_droid_stream *pa_droid_open_input_str
+ s->channel_map = channel_map;
+ s->flags = 0;
+
++ if (s->in->common.get_channels(&s->in->common) != hal_channel_mask) {
++ pa_log_warn("Requested channel mask %u but got %u instead.", hal_channel_mask, config_in.channel_mask);
++ /* convert the channel mask back to pa_channel_map
++ * some device support only mono channel.
++ * */
++ pa_channel_position_t c;
++ if (pa_convert_input_channel(config_in.channel_mask, CONV_FROM_HAL, &c) && (c == PA_CHANNEL_POSITION_MONO)) {
++ s->channel_map.channels = 1;
++ s->channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
++ s->sample_spec.channels = 1 ;
++ } else {
++ pa_log("Failed to convert channel map.");
++ goto fail;
++ }
++ }
++
+ if ((s->sample_spec.rate = s->in->common.get_sample_rate(&s->in->common)) != spec->rate)
+ pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate);
+
+@@ -1727,7 +1785,7 @@ pa_droid_stream *pa_droid_open_input_str
+ devices,
+ s->flags,
+ s->sample_spec.rate,
+- s->sample_spec.channels, hal_channel_mask,
++ s->sample_spec.channels, config_in.channel_mask,
+ s->sample_spec.format, hal_audio_format,
+ buffer_size,
+ pa_bytes_to_usec(buffer_size, &s->sample_spec));
diff --git a/debian/patches/0603-droid-port-priority-and-availability.patch b/debian/patches/0603-droid-port-priority-and-availability.patch
new file mode 100644
index 0000000..9a03636
--- /dev/null
+++ b/debian/patches/0603-droid-port-priority-and-availability.patch
@@ -0,0 +1,103 @@
+Index: pulseaudio/src/modules/droid/droid-util.c
+===================================================================
+--- pulseaudio.orig/src/modules/droid/droid-util.c
++++ pulseaudio/src/modules/droid/droid-util.c
+@@ -110,6 +110,20 @@ static bool string_convert_str_to_num(co
+ return false;
+ }
+
++static bool check_port_availability(const char *port) {
++ pa_assert(port);
++
++ pa_log_debug("Checking availability for port '%s'", port);
++
++ for (unsigned int i = 0; port_availability[i]; i++) {
++ if (pa_streq(port_availability[i], port)) {
++ return true;
++ }
++ }
++
++ return false;
++}
++
+ static char *list_string(struct string_conversion *list, uint32_t flags) {
+ char *str = NULL;
+ char *tmp;
+@@ -1035,6 +1049,9 @@ static pa_droid_port *create_o_port(pa_d
+ if (am->profile_set->config->global_config.default_output_device & device)
+ p->priority += DEFAULT_PRIORITY;
+
++ if (check_port_availability(p->name))
++ p->priority += (DEFAULT_PRIORITY * 3);
++
+ return p;
+ }
+
+@@ -1124,6 +1141,13 @@ static void add_i_port(pa_droid_mapping
+ if (am->profile_set->config->global_config.attached_input_devices & device)
+ p->priority += DEFAULT_PRIORITY;
+
++ /* Make builtin mic the default input device */
++ if (device == AUDIO_DEVICE_IN_BUILTIN_MIC)
++ p->priority += DEFAULT_PRIORITY;
++
++ if (check_port_availability(p->name))
++ p->priority += (DEFAULT_PRIORITY * 3);
++
+ pa_hashmap_put(am->profile_set->all_ports, p->name, p);
+ } else
+ pa_log_debug(" Input port %s from cache", name);
+@@ -1297,6 +1321,9 @@ static int add_ports(pa_core *core, pa_c
+ } else
+ pa_log_debug(" Port %s from cache", p->name);
+
++ /* If port/jack detection is available, start as not available by default */
++ dp->available = check_port_availability(p->name) ? PA_AVAILABLE_NO : PA_AVAILABLE_UNKNOWN;
++
+ if (cp) {
+ if (!pa_hashmap_get(dp->profiles, cp->name))
+ pa_hashmap_put(dp->profiles, cp->name, cp);
+@@ -1749,6 +1776,9 @@ pa_droid_stream *pa_droid_open_input_str
+ if ((s->sample_spec.rate = s->in->common.get_sample_rate(&s->in->common)) != spec->rate)
+ pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate);
+
++ if (s->sample_spec.channels != spec->channels)
++ pa_log_warn("Requested chennel %u but got %u instead.", spec->channels, s->sample_spec.channels);
++
+ pa_idxset_put(module->inputs, s, NULL);
+
+ buffer_size = s->in->common.get_buffer_size(&s->in->common);
+Index: pulseaudio/src/modules/droid/droid-util-44.h
+===================================================================
+--- pulseaudio.orig/src/modules/droid/droid-util-44.h
++++ pulseaudio/src/modules/droid/droid-util-44.h
+@@ -346,4 +346,12 @@ struct string_conversion string_conversi
+ };
+ #undef STRING_ENTRY
+
++/* Ports with availability option (for port/jack detection) */
++static const char* port_availability[] = {
++ "output-wired_headset",
++ "output-wired_headphone",
++ "input-wired_headset",
++ NULL
++};
++
+ #endif
+Index: pulseaudio/src/modules/droid/droid-util-51.h
+===================================================================
+--- pulseaudio.orig/src/modules/droid/droid-util-51.h
++++ pulseaudio/src/modules/droid/droid-util-51.h
+@@ -402,4 +402,12 @@ struct string_conversion string_conversi
+ };
+ #undef STRING_ENTRY
+
++/* Ports with availability option (for port/jack detection) */
++static const char* port_availability[] = {
++ "output-wired_headset",
++ "output-wired_headphone",
++ "input-wired_headset",
++ NULL
++};
++
+ #endif
diff --git a/debian/patches/series b/debian/patches/series
index b7e614a..d852ed9 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -39,3 +39,9 @@
0508-bluetooth-bluez5-add-guards-to-prevent-HFP-and-HSP-c.patch
0509-bluetooth-bluez5-don-t-reactivate-default-profile-wh.patch
0510-Further-fixes-for-HFP-A2DP-with-BlueZ-5.x.patch
+
+# Ubuntu touch: add support for Android 5.x
+0600-droid-sync-with-upstream-for-Android-5-support-and-b.patch
+0601-droid-alternative-hw-module-id.patch
+0602-droid-inputstream-config-parameters.pach
+0603-droid-port-priority-and-availability.patch
diff --git a/debian/pulseaudio-module-droid.install b/debian/pulseaudio-module-droid.install
index 1ccb2d6..7bf8e41 100644
--- a/debian/pulseaudio-module-droid.install
+++ b/debian/pulseaudio-module-droid.install
@@ -1,6 +1,7 @@
-usr/lib/pulse-*/modules/libdroid-util.so
-usr/lib/pulse-*/modules/libdroid-sink.so
-usr/lib/pulse-*/modules/libdroid-source.so
-usr/lib/pulse-*/modules/module-droid-sink.so
-usr/lib/pulse-*/modules/module-droid-source.so
-usr/lib/pulse-*/modules/module-droid-card.so
+usr/lib/pulse-*/modules/libdroid-util-*.so
+usr/lib/pulse-*/modules/libdroid-sink-*.so
+usr/lib/pulse-*/modules/libdroid-source-*.so
+usr/lib/pulse-*/modules/module-droid-sink-*.so
+usr/lib/pulse-*/modules/module-droid-source-*.so
+usr/lib/pulse-*/modules/module-droid-card-*.so
+usr/lib/pulse-*/modules/module-droid-discover.so
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-pulseaudio/pulseaudio.git
More information about the pkg-pulseaudio-devel
mailing list