[pulseaudio] 05/07: Merge branch 'ubuntu-pulse8-ppa' into ubuntu

Luke Yelavich themuso-guest at moszumanska.debian.org
Wed Feb 10 05:29:48 UTC 2016


This is an automated email from the git hooks/post-receive script.

themuso-guest pushed a commit to branch ubuntu
in repository pulseaudio.

commit 3f0acc369ace4afd76238eb477a6364f05e37126
Merge: 01f35c2 756d4ba
Author: Luke Yelavich <themuso at ubuntu.com>
Date:   Wed Feb 10 16:15:51 2016 +1100

    Merge branch 'ubuntu-pulse8-ppa' into ubuntu

 .gitignore                                         |    2 +
 .tarball-version                                   |    2 +-
 .version                                           |    2 +-
 Makefile.am                                        |    4 +
 Makefile.in                                        |    6 +-
 NEWS                                               |   57 +-
 bootstrap.sh                                       |    4 +-
 config.h.in                                        |   19 +-
 configure                                          |  318 +-
 configure.ac                                       |   39 +-
 debian/changelog                                   |   64 +-
 debian/control                                     |  194 +-
 debian/libpulse0.symbols                           |    3 +
 debian/libpulsedsp.lintian-overrides               |    3 -
 .../0005-dont-load-cork-music-on-phone.patch       |    2 +-
 ...rapper-Quit-daemon-if-pid-file-is-removed.patch |    4 +-
 ...nd_mixer_poll_descriptors_count_when_zero.patch |    4 +-
 ...onf-parser-add-support-for-.d-directories.patch |  184 -
 ...nt-conf-daemon-conf-enable-.d-directories.patch |  127 -
 debian/patches/0202-dont-probe-ucm.patch           |    2 +-
 ...0203-card-Add-hook-before-profile-changes.patch |    4 +-
 debian/patches/0207-Enable-pulseaudio-droid.patch  |   12 +-
 ...tooth-device-Allow-leaving-transport-runn.patch |   16 +-
 .../0407-access-Add-access-control-hooks.patch     |    6 +-
 debian/patches/0409-Trust-store-patch.patch        |  105 +-
 ...-thread-to-activate-trust-store-interface.patch |    4 +-
 .../0417-increase-timeout-check-apparmor.patch     |   14 +-
 ...with-upstream-for-Android-5-support-and-b.patch |   98 +-
 debian/patches/series                              |    4 -
 debian/pulseaudio-dev.install                      |    2 -
 debian/pulseaudio-esound-compat.lintian-overrides  |    1 -
 .../pulseaudio-module-bluetooth.lintian-overrides  |    1 -
 debian/pulseaudio-module-raop.lintian-overrides    |    2 -
 debian/pulseaudio-module-x11.lintian-overrides     |    2 -
 .../pulseaudio-module-zeroconf.lintian-overrides   |    1 -
 debian/pulseaudio.install                          |    2 +-
 debian/pulseaudio.lintian-overrides                |    4 -
 debian/rules                                       |    2 +
 debian/shlibs.local                                |    4 +-
 doxygen/Makefile.in                                |    2 +-
 git-version-gen                                    |    5 +-
 man/Makefile.in                                    |    2 +-
 man/default.pa.5                                   |    6 +-
 man/default.pa.5.xml.in                            |   19 +-
 man/pulse-client.conf.5                            |    6 +-
 man/pulse-client.conf.5.xml.in                     |   19 +-
 man/pulse-daemon.conf.5                            |    8 +-
 man/pulse-daemon.conf.5.xml.in                     |   25 +-
 po/uk.po                                           |  341 +-
 po/zh_CN.po                                        | 3660 +++++++++++---------
 scripts/benchmark_memory_usage.sh                  |  133 +
 scripts/benchmarks/.gitignore                      |   10 +
 scripts/benchmarks/README                          |    4 +
 scripts/plot_memory_usage.gp                       |   63 +
 src/Makefile.am                                    |   50 +-
 src/Makefile.in                                    |  234 +-
 src/daemon/cmdline.c                               |    2 +-
 src/daemon/daemon-conf.c                           |    4 +-
 src/daemon/default.pa.in                           |    4 +
 src/daemon/main.c                                  |    6 +-
 src/daemon/system.pa.in                            |    4 +
 src/map-file                                       |    3 +
 src/modules/alsa/alsa-mixer.c                      |   25 +-
 src/modules/alsa/alsa-mixer.h                      |    3 +
 src/modules/alsa/alsa-sink.c                       |   17 +-
 src/modules/alsa/alsa-source.c                     |    3 +-
 src/modules/alsa/alsa-util.c                       |   21 +-
 .../alsa/mixer/paths/analog-input-headset-mic.conf |    3 +
 .../mixer/profile-sets/sb-omni-surround-5.1.conf   |   21 +-
 src/modules/alsa/module-alsa-card.c                |   33 +-
 src/modules/bluetooth/module-bluetooth-policy.c    |    5 +-
 src/modules/bluetooth/module-bluez4-device.c       |   11 +-
 src/modules/bluetooth/module-bluez5-device.c       |    2 +-
 src/modules/bluetooth/module-bluez5-discover.c     |    3 +-
 src/modules/dbus/iface-card-profile.c              |   19 +
 src/modules/dbus/iface-card.c                      |   51 +-
 src/modules/dbus/iface-core.c                      |    4 +-
 src/modules/dbus/iface-device-port.c               |   65 +-
 src/modules/echo-cancel/module-echo-cancel.c       |   18 +-
 src/modules/macosx/module-coreaudio-device.c       |   50 +-
 src/modules/module-always-sink.c                   |    9 +-
 src/modules/module-augment-properties.c            |    2 +-
 src/modules/module-card-restore.c                  |   58 +-
 src/modules/module-loopback.c                      |    5 +
 src/modules/module-rygel-media-server.c            |    2 +-
 src/modules/module-switch-on-port-available.c      |  179 +-
 src/modules/module-tunnel.c                        |    4 +-
 src/modules/module-udev-detect.c                   |    2 +-
 src/modules/module-zeroconf-publish.c              |    2 +-
 src/modules/rtp/headerlist.c                       |    2 +-
 src/modules/rtp/rtsp_client.c                      |    6 +-
 src/modules/rtp/sdp.c                              |    7 +-
 src/modules/x11/module-x11-publish.c               |    2 +-
 src/pulse/channelmap.c                             |   13 +
 src/pulse/channelmap.h                             |    5 +
 src/pulse/client-conf.c                            |    2 +-
 src/pulse/context.c                                |    6 +-
 src/pulse/proplist.c                               |   14 +-
 src/pulse/pulseaudio.h                             |    9 +
 src/pulse/stream.h                                 |    9 +-
 src/pulse/thread-mainloop.c                        |   16 +-
 src/pulse/version.h                                |    6 +-
 src/pulse/volume.c                                 |  149 +-
 src/pulse/volume.h                                 |   18 +
 src/pulsecore/card.c                               |   46 +-
 src/pulsecore/card.h                               |   15 +-
 src/pulsecore/cli-text.c                           |   18 +-
 src/pulsecore/cli.c                                |    2 +-
 src/pulsecore/client.c                             |    6 +-
 src/pulsecore/client.h                             |    3 +-
 src/pulsecore/conf-parser.c                        |   42 +-
 src/pulsecore/conf-parser.h                        |    8 +-
 src/pulsecore/core-rtclock.h                       |    2 +
 src/pulsecore/core-util.c                          |   52 +-
 src/pulsecore/core.c                               |    2 +-
 src/pulsecore/core.h                               |    3 +-
 src/pulsecore/device-port.c                        |   11 +
 src/pulsecore/device-port.h                        |    5 +-
 src/pulsecore/log.c                                |    9 +
 src/pulsecore/ltdl-helper.c                        |    2 +-
 src/pulsecore/modargs.c                            |    8 +-
 src/pulsecore/module.c                             |    7 +-
 src/pulsecore/module.h                             |    2 +-
 src/pulsecore/packet.c                             |    4 +-
 src/pulsecore/protocol-dbus.c                      |    4 +-
 src/pulsecore/protocol-http.c                      |    2 +-
 src/pulsecore/resampler.c                          |    7 +-
 src/pulsecore/resampler/soxr.c                     |   15 +-
 src/pulsecore/rtpoll.c                             |    2 +-
 src/pulsecore/sample-util.h                        |   10 +-
 src/pulsecore/sink-input.c                         |    2 +
 src/pulsecore/sink-input.h                         |    3 +-
 src/pulsecore/sink.c                               |   38 +-
 src/pulsecore/sink.h                               |    4 +-
 src/pulsecore/source-output.c                      |    2 +
 src/pulsecore/source-output.h                      |    3 +-
 src/pulsecore/source.c                             |   18 +-
 src/pulsecore/source.h                             |    3 +-
 src/pulsecore/strbuf.c                             |    8 +-
 src/pulsecore/strbuf.h                             |    4 +-
 src/pulsecore/strlist.c                            |    4 +-
 src/pulsecore/strlist.h                            |    2 +-
 src/pulsecore/{strbuf.h => typedefs.h}             |   33 +-
 src/tests/extended-test.c                          |    2 +
 src/tests/memblockq-test.c                         |    4 +-
 src/tests/mult-s16-test.c                          |    8 +-
 src/tests/strlist-test.c                           |    6 +-
 src/tests/sync-playback.c                          |    3 +-
 src/utils/pacmd.c                                  |    2 +-
 149 files changed, 4151 insertions(+), 3100 deletions(-)

diff --cc debian/changelog
index c8b08a5,d160619..ee8e1d0
--- a/debian/changelog
+++ b/debian/changelog
@@@ -1,37 -1,64 +1,99 @@@
 -pulseaudio (1:7.99.2-1ubuntu1~xenial1) UNRELEASED; urgency=medium
++pulseaudio (1:8.0-0ubuntu1) UNRELEASED; urgency=medium
+ 
++  * New upstream release
+   * Merge from Debian experimental, remaining changes:
+     - epoch (my stupid fault :S)
+     - Don't ship the consolekit module, and explicitly depend on the PAM
+       systemd module
+     - Add a module to allow pulseaudio to use the Android audio HAL
+     - Adjust gbp.conf file to point to the ubuntu branch for package builds
+     - Disable module-role-cork by default
+     - Load module-x11-bell in the start-pulseaudio-x11 script
+     - Hide pulseaudio from startup apps dialog
+     - Quit daemon if pid file is removed
+     - Avoid abort when poll descriptor is 0, such as when using it with the
+       audioflinger bridge, like done in ubuntu touch
+     - change default rlimit_rttime value to 200 ms
+     - Don't probe UCM profiles, trust that the profile writer knows what they
+       are doing
+     - Add card hook to allow modules to do something before a profile becomes
+       active
+     - Add an option to the bluez4 discovery module to set the profile
+     - Allow leaving bluetooth transport running while sink and source are
+       suspended.
+     - Add option to module-switch-on-connect to skip abstract devices
+     - Add property to module-device-restore to skip store/restore
+     - Add missing fields for sink_input/source_output info struct to the vala
+       bindings
+     - Add example upstart job to run pulseaudio in system mode
+     - Add a build/run autopkgtest for libpulse-dev
+     - Drop libsoxr-dev build dep, its in universe
+   * Refreshed patches, and dropped those that were applied upstream.
+ 
+  -- Luke Yelavich <themuso at ubuntu.com>  Tue, 05 Jan 2016 10:04:43 +1100
+ 
+ pulseaudio (7.99.2-1) experimental; urgency=medium
+ 
+   * New upstream release candidate
+   * Drop build-sys-Fix-install-order-of-libpulsecore.patch, applied
+     upstream
+ 
+  -- Felipe Sateler <fsateler at debian.org>  Fri, 15 Jan 2016 18:21:14 -0300
+ 
+ pulseaudio (7.99.1-1) experimental; urgency=medium
+ 
+   * New upstream release candidate
+     - Update local shlibs file for 7.99
+     - libpulsecore is now installed in the package-private directory
+     - Update symbols file to add new balance functions
+   * Drop pulsecore related lintian overrides
+   * Drop stale pulseaudio-dev.install file
+   * Drop stale lintian overrides
+   * Add patch to fix install order of libpulsecore
+ 
+  -- Felipe Sateler <fsateler at debian.org>  Mon, 28 Dec 2015 17:34:22 -0300
+ 
+ pulseaudio (7.1-2) unstable; urgency=medium
+ 
+   * Add build-depends on libsoxr-dev to enable soxr resamplers.
+      Closes: #804212
+ 
+  -- Felipe Sateler <fsateler at debian.org>  Fri, 06 Nov 2015 17:15:54 -0300
+ 
 +pulseaudio (1:7.1-1ubuntu7) xenial; urgency=medium
 +
 +  * Keep the libpulsecore library in the multiarch libdir.
 +
 + -- Matthias Klose <doko at ubuntu.com>  Fri, 05 Feb 2016 13:10:43 +0100
 +
 +pulseaudio (1:7.1-1ubuntu6) xenial; urgency=medium
 +
 +  * debian/patches/60*.patch:
 +    + Add proper dep3 patch header to all patches.
 +  * debian/control:
 +    + Only depend on android-headers for i386, amd64 and armhf as no
 +      other platform actually uses it or provides hybris support.
 +
 + -- Simon Fels <simon.fels at canonical.com>  Mon, 01 Feb 2016 15:31:25 +0100
 +
 +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 --cc debian/patches/0005-dont-load-cork-music-on-phone.patch
index b625444,b625444..2894175
--- a/debian/patches/0005-dont-load-cork-music-on-phone.patch
+++ b/debian/patches/0005-dont-load-cork-music-on-phone.patch
@@@ -2,7 -2,7 +2,7 @@@ Index: pulseaudio/src/daemon/default.pa
  ===================================================================
  --- pulseaudio.orig/src/daemon/default.pa.in
  +++ pulseaudio/src/daemon/default.pa.in
--@@ -160,7 +160,7 @@ load-module module-systemd-login
++@@ -164,7 +164,7 @@ load-module module-systemd-login
   load-module module-position-event-sounds
   
   ### Cork music/video streams when a phone stream is active
diff --cc debian/patches/0207-Enable-pulseaudio-droid.patch
index 585b9a1,373c7d8..3d2f530
--- a/debian/patches/0207-Enable-pulseaudio-droid.patch
+++ b/debian/patches/0207-Enable-pulseaudio-droid.patch
@@@ -2,7 -2,7 +2,7 @@@ Index: pulseaudio/configure.a
  ===================================================================
  --- pulseaudio.orig/configure.ac
  +++ pulseaudio/configure.ac
- @@ -807,6 +807,21 @@ AM_CONDITIONAL([HAVE_ALSA], [test "x$HAV
 -@@ -808,6 +808,21 @@ AM_CONDITIONAL([HAVE_ALSA], [test "x$HAV
++@@ -822,6 +822,21 @@ AM_CONDITIONAL([HAVE_ALSA], [test "x$HAV
   AS_IF([test "x$HAVE_ALSA" = "x1"], AC_DEFINE([HAVE_ALSA], 1, [Have ALSA?]))
   AS_IF([test "x$HAVE_ALSA_UCM" = "x1"], AC_DEFINE([HAVE_ALSA_UCM], 1, [Have ALSA UCM?]))
   
@@@ -24,7 -24,7 +24,7 @@@
   #### EsounD support (optional) ####
   
   AC_ARG_ENABLE([esound],
- @@ -1521,6 +1536,7 @@ AS_IF([test "x$HAVE_X11" = "x1"], ENABLE
 -@@ -1522,6 +1537,7 @@ AS_IF([test "x$HAVE_X11" = "x1"], ENABLE
++@@ -1536,6 +1551,7 @@ AS_IF([test "x$HAVE_X11" = "x1"], ENABLE
   AS_IF([test "x$HAVE_OSS_OUTPUT" = "x1"], ENABLE_OSS_OUTPUT=yes, ENABLE_OSS_OUTPUT=no)
   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)
@@@ -32,7 -32,7 +32,7 @@@
   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)
- @@ -1583,6 +1599,7 @@ echo "
 -@@ -1584,6 +1600,7 @@ echo "
++@@ -1598,6 +1614,7 @@ echo "
       Enable OSS Wrapper:            ${ENABLE_OSS_WRAPPER}
       Enable EsounD:                 ${ENABLE_ESOUND}
       Enable Alsa:                   ${ENABLE_ALSA}
diff --cc debian/patches/0409-Trust-store-patch.patch
index 539891d,958095e..f3c3958
--- a/debian/patches/0409-Trust-store-patch.patch
+++ b/debian/patches/0409-Trust-store-patch.patch
@@@ -26,9 -25,9 +25,9 @@@ Signed-off-by: David Henningsson <david
  
  Index: pulseaudio/configure.ac
  ===================================================================
- --- pulseaudio.orig/configure.ac	2015-11-30 16:38:21.788316586 +0100
- +++ pulseaudio/configure.ac	2015-11-30 16:38:21.776316368 +0100
- @@ -1399,6 +1399,19 @@
+ --- pulseaudio.orig/configure.ac
+ +++ pulseaudio/configure.ac
 -@@ -1400,6 +1400,19 @@ AS_IF([test "x$enable_adrian_aec" != "xn
++@@ -1414,6 +1414,19 @@ AS_IF([test "x$enable_adrian_aec" != "xn
       [HAVE_ADRIAN_EC=1])
   AM_CONDITIONAL([HAVE_ADRIAN_EC], [test "x$HAVE_ADRIAN_EC" = "x1"])
   
@@@ -48,7 -47,7 +47,7 @@@
   
   
   ###################################
- @@ -1568,6 +1581,7 @@
 -@@ -1569,6 +1582,7 @@ AS_IF([test "x$HAVE_ADRIAN_EC" = "x1"],
++@@ -1583,6 +1596,7 @@ AS_IF([test "x$HAVE_ADRIAN_EC" = "x1"],
   AS_IF([test "x$HAVE_SPEEX" = "x1"], ENABLE_SPEEX=yes, ENABLE_SPEEX=no)
   AS_IF([test "x$HAVE_SOXR" = "x1"], ENABLE_SOXR=yes, ENABLE_SOXR=no)
   AS_IF([test "x$HAVE_WEBRTC" = "x1"], ENABLE_WEBRTC=yes, ENABLE_WEBRTC=no)
@@@ -56,7 -55,7 +55,7 @@@
   AS_IF([test "x$HAVE_TDB" = "x1"], ENABLE_TDB=yes, ENABLE_TDB=no)
   AS_IF([test "x$HAVE_GDBM" = "x1"], ENABLE_GDBM=yes, ENABLE_GDBM=no)
   AS_IF([test "x$HAVE_SIMPLEDB" = "x1"], ENABLE_SIMPLEDB=yes, ENABLE_SIMPLEDB=no)
- @@ -1632,6 +1646,7 @@
 -@@ -1633,6 +1647,7 @@ echo "
++@@ -1647,6 +1661,7 @@ echo "
       Enable speex (resampler, AEC): ${ENABLE_SPEEX}
       Enable soxr (resampler):       ${ENABLE_SOXR}
       Enable WebRTC echo canceller:  ${ENABLE_WEBRTC}
diff --cc debian/patches/0600-droid-sync-with-upstream-for-Android-5-support-and-b.patch
index bd61fbd,0000000..582111f
mode 100644,000000..100644
--- 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
@@@ -1,5249 -1,0 +1,5249 @@@
 +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
 +
 +Origin: upstream, https://github.com/mer-hybris/pulseaudio-modules-droid
 +Forwarded: not-needed
 +---
 + 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
++Index: pulseaudio/configure.ac
 +===================================================================
- --- overlay.orig/configure.ac
- +++ overlay/configure.ac
- @@ -822,6 +822,23 @@ AS_IF([test "x$enable_android_hal" = "xy
++--- pulseaudio.orig/configure.ac
+++++ pulseaudio/configure.ac
++@@ -837,6 +837,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"],
++@@ -1565,6 +1582,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 "
++@@ -1629,6 +1648,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
++Index: pulseaudio/src/Makefile.am
 +===================================================================
- --- overlay.orig/src/Makefile.am
- +++ overlay/src/Makefile.am
- @@ -1272,12 +1272,25 @@ modlibexec_LTLIBRARIES += \
++--- pulseaudio.orig/src/Makefile.am
+++++ pulseaudio/src/Makefile.am
++@@ -1285,12 +1285,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 = \
++@@ -1566,9 +1579,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)
++@@ -1881,42 +1904,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
++Index: pulseaudio/src/modules/droid/droid-sink.c
 +===================================================================
- --- overlay.orig/src/modules/droid/droid-sink.c
- +++ overlay/src/modules/droid/droid-sink.c
++--- pulseaudio.orig/src/modules/droid/droid-sink.c
+++++ pulseaudio/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
++Index: pulseaudio/src/modules/droid/droid-source.c
 +===================================================================
- --- overlay.orig/src/modules/droid/droid-source.c
- +++ overlay/src/modules/droid/droid-source.c
++--- pulseaudio.orig/src/modules/droid/droid-source.c
+++++ pulseaudio/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
++Index: pulseaudio/src/modules/droid/droid-source.h
 +===================================================================
- --- overlay.orig/src/modules/droid/droid-source.h
- +++ overlay/src/modules/droid/droid-source.h
++--- pulseaudio.orig/src/modules/droid/droid-source.h
+++++ pulseaudio/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
++Index: pulseaudio/src/modules/droid/droid-util-42.h
 +===================================================================
- --- overlay.orig/src/modules/droid/droid-util-42.h
- +++ overlay/src/modules/droid/droid-util-42.h
++--- pulseaudio.orig/src/modules/droid/droid-util-42.h
+++++ pulseaudio/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
++Index: pulseaudio/src/modules/droid/droid-util-44.h
 +===================================================================
 +--- /dev/null
- +++ overlay/src/modules/droid/droid-util-44.h
+++++ pulseaudio/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
++Index: pulseaudio/src/modules/droid/droid-util-51.h
 +===================================================================
 +--- /dev/null
- +++ overlay/src/modules/droid/droid-util-51.h
+++++ pulseaudio/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
++Index: pulseaudio/src/modules/droid/droid-util.c
 +===================================================================
- --- overlay.orig/src/modules/droid/droid-util.c
- +++ overlay/src/modules/droid/droid-util.c
++--- pulseaudio.orig/src/modules/droid/droid-util.c
+++++ pulseaudio/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
++Index: pulseaudio/src/modules/droid/droid-util.h
 +===================================================================
- --- overlay.orig/src/modules/droid/droid-util.h
- +++ overlay/src/modules/droid/droid-util.h
++--- pulseaudio.orig/src/modules/droid/droid-util.h
+++++ pulseaudio/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
++Index: pulseaudio/src/modules/droid/module-droid-card.c
 +===================================================================
- --- overlay.orig/src/modules/droid/module-droid-card.c
- +++ overlay/src/modules/droid/module-droid-card.c
++--- pulseaudio.orig/src/modules/droid/module-droid-card.c
+++++ pulseaudio/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
++Index: pulseaudio/src/modules/droid/module-droid-discover.c
 +===================================================================
 +--- /dev/null
- +++ overlay/src/modules/droid/module-droid-discover.c
+++++ pulseaudio/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
++Index: pulseaudio/src/modules/droid/module-droid-sink.c
 +===================================================================
- --- overlay.orig/src/modules/droid/module-droid-sink.c
- +++ overlay/src/modules/droid/module-droid-sink.c
++--- pulseaudio.orig/src/modules/droid/module-droid-sink.c
+++++ pulseaudio/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
++Index: pulseaudio/src/modules/droid/module-droid-source.c
 +===================================================================
- --- overlay.orig/src/modules/droid/module-droid-source.c
- +++ overlay/src/modules/droid/module-droid-source.c
++--- pulseaudio.orig/src/modules/droid/module-droid-source.c
+++++ pulseaudio/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
++Index: pulseaudio/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
++--- pulseaudio.orig/src/modules/module-device-restore.c
+++++ pulseaudio/src/modules/module-device-restore.c
++@@ -815,6 +815,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;
 + 

-- 
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