1 From: "vivian,zhang" <vivian.zhang@intel.com>
2 Date: Tue, 18 Jun 2013 16:21:32 +0800
3 Subject: add policy module - samsung
5 Change-Id: I2111a9c4dc0a371dbea5b347cf77adbe8f930528
6 Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
9 src/Makefile.am | 28 +-
10 src/modules/module-policy.c | 926 ++++++++++++++++++++++++++++++++++++++++++++
11 src/pulse/ext-policy.c | 177 +++++++++
12 src/pulse/ext-policy.h | 61 +++
13 src/pulse/proplist.h | 3 +
14 6 files changed, 1211 insertions(+), 2 deletions(-)
15 create mode 100644 src/modules/module-policy.c
16 create mode 100644 src/pulse/ext-policy.c
17 create mode 100644 src/pulse/ext-policy.h
19 diff --git a/configure.ac b/configure.ac
20 index c0beac0..0e205d3 100644
23 @@ -643,6 +643,24 @@ PKG_CHECK_MODULES(LIBJSON, [ json-c >= 0.11 ], [],
25 PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ])
27 +dnl use samsung policy module --------------------------------------------------------
28 +AC_ARG_ENABLE(samsung-policy, AC_HELP_STRING([--enable-samsung-policy], [using samsung-policy]),
30 + case "${enableval}" in
31 + yes) USE_SAMSUNG_POLICY=yes ;;
32 + no) USE_SAMSUNG_POLICY=no ;;
33 + *) AC_MSG_ERROR(bad value ${enableval} for --enable-samsung_policy) ;;
35 + ],[USE_SAMSUNG_POLICY=no])
37 +if test "x$USE_SAMSUNG_POLICY" = "xyes"; then
38 + PKG_CHECK_MODULES(VCONF, vconf)
39 + AC_SUBST(VCONF_CFLAGS)
40 + AC_SUBST(VCONF_LIBS)
42 +AM_CONDITIONAL(USE_SAMSUNG_POLICY, test "x$USE_SAMSUNG_POLICY" = "xyes")
43 +dnl end --------------------------------------------------------------------
45 dnl use dlog --------------------------------------------------------------------------
46 AC_ARG_ENABLE(dlog, AC_HELP_STRING([--enable-dlog], [using dlog]),
48 diff --git a/src/Makefile.am b/src/Makefile.am
49 index 3e41300..4872dfd 100644
52 @@ -149,7 +149,7 @@ pulseaudio_SOURCES = \
53 daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
56 -pulseaudio_CFLAGS = $(AM_CFLAGS) $(CAP_CFLAGS)
57 +pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(DBUS_CFLAGS)
58 pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la $(LIBLTDL) $(CAP_LIBS)
59 # This is needed because automake doesn't properly expand the foreach below
60 pulseaudio_DEPENDENCIES = libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la $(PREOPEN_LIBS)
61 @@ -787,6 +787,11 @@ pulseinclude_HEADERS = \
65 +if USE_SAMSUNG_POLICY
66 +pulseinclude_HEADERS += \
73 @@ -833,6 +838,11 @@ libpulse_la_SOURCES = \
74 pulse/volume.c pulse/volume.h \
75 pulse/xmalloc.c pulse/xmalloc.h
77 +if USE_SAMSUNG_POLICY
78 +libpulse_la_SOURCES += \
79 + pulse/ext-policy.c pulse/ext-policy.h
82 libpulse_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(LIBJSON_CFLAGS)
83 libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBJSON_LIBS) libpulsecommon-@PA_MAJORMINOR@.la
84 libpulse_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO)
85 @@ -1088,6 +1098,10 @@ if HAVE_DBUS
86 # Serveral module (e.g. libalsa-util.la)
87 modlibexec_LTLIBRARIES += \
89 +if USE_SAMSUNG_POLICY
90 +modlibexec_LTLIBRARIES += \
95 modlibexec_LTLIBRARIES += \
96 @@ -1470,7 +1484,10 @@ SYMDEF_FILES = \
97 module-switch-on-port-available-symdef.h \
98 module-filter-apply-symdef.h \
99 module-filter-heuristics-symdef.h
101 +if USE_SAMSUNG_POLICY
103 + module-policy-symdef.h
107 module-esound-protocol-tcp-symdef.h \
108 @@ -2120,6 +2137,13 @@ module_rygel_media_server_la_LDFLAGS = $(MODULE_LDFLAGS)
109 module_rygel_media_server_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libprotocol-http.la
110 module_rygel_media_server_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
112 +if USE_SAMSUNG_POLICY
113 +module_policy_la_SOURCES = modules/module-policy.c
114 +module_policy_la_LDFLAGS = $(MODULE_LDFLAGS)
115 +module_policy_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) $(VCONF_LIBS) libprotocol-native.la libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
116 +module_policy_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(VCONF_CFLAGS)
119 ###################################
121 ###################################
122 diff --git a/src/modules/module-policy.c b/src/modules/module-policy.c
124 index 0000000..2172018
126 +++ b/src/modules/module-policy.c
128 +#ifdef HAVE_CONFIG_H
132 +#include <pulsecore/core.h>
133 +#include <pulsecore/module.h>
134 +#include <pulsecore/modargs.h>
135 +#include <pulsecore/core-rtclock.h>
136 +#include <pulsecore/core-util.h>
137 +#include <pulsecore/log.h>
138 +#include <stdbool.h>
139 +#include <strings.h>
141 +#include <pulsecore/log.h>
142 +#include <pulsecore/core-subscribe.h>
143 +#include <pulsecore/sink-input.h>
144 +#include <pulsecore/source-output.h>
145 +#include <pulsecore/namereg.h>
146 +#include <pulsecore/core-error.h>
148 +#include <pulsecore/protocol-native.h>
149 +#include <pulsecore/pstream-util.h>
150 +#include <vconf.h> // for mono
152 +#include "module-policy-symdef.h"
154 +PA_MODULE_AUTHOR("Seungbae Shin");
155 +PA_MODULE_DESCRIPTION("Media Policy module");
156 +PA_MODULE_VERSION(PACKAGE_VERSION);
157 +PA_MODULE_LOAD_ONCE(true);
159 + "on_hotplug=<When new device becomes available, recheck streams?> ");
161 +static const char* const valid_modargs[] = {
170 + pa_hook_slot *sink_input_new_hook_slot,*sink_put_hook_slot;
172 + pa_hook_slot *sink_input_unlink_slot,*sink_unlink_slot;
173 + pa_hook_slot *sink_input_unlink_post_slot, *sink_unlink_post_slot;
174 + pa_hook_slot *sink_input_move_start_slot,*sink_input_move_finish_slot;
175 + pa_subscription *subscription;
182 + pa_module* module_mono_bt;
183 + pa_module* module_combined;
184 + pa_module* module_mono_combined;
185 + pa_native_protocol *protocol;
186 + pa_hook_slot *source_output_new_hook_slot;
192 + SUBCOMMAND_BALANCE,
196 +#define AEC_SINK "alsa_output.0.analog-stereo.echo-cancel"
197 +#define AEC_SOURCE "alsa_input.0.analog-stereo.echo-cancel"
198 +#define SINK_ALSA "alsa_output.0.analog-stereo"
199 +#define SINK_MONO_ALSA "mono_alsa"
200 +#define SINK_MONO_BT "mono_bt"
201 +#define SINK_COMBINED "combined"
202 +#define SINK_MONO_COMBINED "mono_combined"
203 +#define POLICY_AUTO "auto"
204 +#define POLICY_PHONE "phone"
205 +#define POLICY_ALL "all"
206 +#define POLICY_VOIP "voip"
207 +#define BLUEZ_API "bluez"
208 +#define ALSA_API "alsa"
209 +#define MONO_KEY VCONFKEY_SETAPPL_ACCESSIBILITY_MONO_AUDIO
211 +/* check if this sink is bluez */
212 +static bool policy_is_bluez (pa_sink* sink)
214 + const char* api_name = NULL;
216 + if (sink == NULL) {
217 + pa_log_warn ("input param sink is null");
221 + api_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_API);
223 + if (pa_streq (api_name, BLUEZ_API)) {
225 + pa_log_debug("[POLICY][%s] [%s] exists and it is [%s]...true !!", __func__, PA_PROP_DEVICE_API, api_name);
230 + pa_log_debug("[POLICY][%s] [%s] exists, but not bluez...false !!", __func__, PA_PROP_DEVICE_API);
235 + pa_log_debug("[POLICY][%s] No [%s] exists...false!!", __func__, PA_PROP_DEVICE_API);
242 +/* check if this sink is bluez */
243 +static bool policy_is_usb_alsa (pa_sink* sink)
245 + const char* api_name = NULL;
246 + const char* device_bus_name = NULL;
248 + if (sink == NULL) {
249 + pa_log_warn ("input param sink is null");
253 + api_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_API);
255 + if (pa_streq (api_name, ALSA_API)) {
257 + pa_log_debug("[POLICY][%s] [%s] exists and it is [%s]...true !!", __func__, PA_PROP_DEVICE_API, api_name);
259 + device_bus_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_BUS);
260 + if (device_bus_name) {
261 + if (pa_streq (device_bus_name, "usb")) {
267 + pa_log_debug("[POLICY][%s] [%s] exists, but not alsa...false !!", __func__, PA_PROP_DEVICE_API);
272 + pa_log_debug("[POLICY][%s] No [%s] exists...false!!", __func__, PA_PROP_DEVICE_API);
279 +/* Get sink by name */
280 +static pa_sink* policy_get_sink_by_name (pa_core *c, const char* sink_name)
285 + if (c == NULL || sink_name == NULL) {
286 + pa_log_warn ("input param is null");
290 + PA_IDXSET_FOREACH(s, c->sinks, idx) {
291 + if (pa_streq (s->name, sink_name)) {
292 + pa_log_debug ("[POLICY][%s] return [%p] for [%s]\n", __func__, s, sink_name);
299 +/* Get bt sink if available */
300 +static pa_sink* policy_get_bt_sink (pa_core *c)
306 + pa_log_warn ("input param is null");
310 + PA_IDXSET_FOREACH(s, c->sinks, idx) {
311 + if (policy_is_bluez (s)) {
312 + pa_log_debug ("[POLICY][%s] return [%p] for [%s]\n", __func__, s, s->name);
319 +/* Select sink for given condition */
320 +static pa_sink* policy_select_proper_sink (pa_core *c, const char* policy, int is_mono)
322 + pa_sink* sink = NULL;
323 + pa_sink* bt_sink = NULL;
324 + pa_sink* def = NULL;
326 + if (c == NULL || policy == NULL) {
327 + pa_log_warn ("input param is null");
333 + bt_sink = policy_get_bt_sink(c);
334 + def = pa_namereg_get_default_sink(c);
336 + pa_log_warn ("POLICY][%s] pa_namereg_get_default_sink() returns null", __func__);
340 + pa_log_debug ("[POLICY][%s] policy[%s], is_mono[%d], current default[%s], bt sink[%s]\n",
341 + __func__, policy, is_mono, def->name, (bt_sink)? bt_sink->name:"null");
343 + /* Select sink to */
344 + if (pa_streq(policy, POLICY_ALL)) {
347 + sink = policy_get_sink_by_name(c, (is_mono)? SINK_MONO_COMBINED : SINK_COMBINED);
349 + sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA);
352 + } else if (pa_streq(policy, POLICY_PHONE)) {
354 + sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA);
355 + } else if (pa_streq(policy, POLICY_VOIP)) {
357 + sink = policy_get_sink_by_name (c,AEC_SINK);
360 + if (policy_is_bluez(def)) {
361 + sink = (is_mono)? policy_get_sink_by_name (c, SINK_MONO_BT) : def;
362 + } else if (policy_is_usb_alsa(def)) {
365 + sink = (is_mono)? policy_get_sink_by_name (c, SINK_MONO_ALSA) : def;
369 + pa_log_debug ("[POLICY][%s] selected sink : [%s]\n", __func__, (sink)? sink->name : "null");
373 +static bool policy_is_filter (pa_sink_input* si)
375 + const char* role = NULL;
378 + pa_log_warn ("input param sink-input is null");
382 + if ((role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) {
384 + pa_log_debug("[POLICY][%s] Role of sink input [%d] = %s", __func__, si->index, role);
386 + if (pa_streq(role, "filter")) {
388 + pa_log_debug("[POLICY] no need to change of sink for %s", role);
399 +#define EXT_VERSION 1
401 +static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
402 + struct userdata *u = NULL;
404 + pa_tagstruct *reply = NULL;
406 + pa_sink_input *si = NULL;
409 + pa_sink* sink_to_move = NULL;
418 + if (pa_tagstruct_getu32(t, &command) < 0)
421 + reply = pa_tagstruct_new(NULL, 0);
422 + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
423 + pa_tagstruct_putu32(reply, tag);
426 + case SUBCOMMAND_TEST: {
427 + if (!pa_tagstruct_eof(t))
430 + pa_tagstruct_putu32(reply, EXT_VERSION);
434 + case SUBCOMMAND_MONO: {
438 + if (pa_tagstruct_get_boolean(t, &enable) < 0)
441 + pa_log_debug ("[POLICY][%s] new mono value = %d\n", __func__, enable);
442 + if (enable == u->is_mono) {
443 + pa_log_debug ("[POLICY][%s] No changes in mono value = %d", __func__, u->is_mono);
447 + u->is_mono = enable;
449 + /* Move current sink-input to proper mono sink */
450 + PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
451 + const char *policy = NULL;
453 + /* Skip this if it is already in the process of being moved
458 + /* It might happen that a stream and a sink are set up at the
459 + same time, in which case we want to make sure we don't
460 + interfere with that */
461 + if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
464 + /* Get role (if role is filter, skip it) */
465 + if (policy_is_filter(si))
468 + /* Check policy, if no policy exists, treat as AUTO */
469 + if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) {
470 + pa_log_debug("[POLICY] set policy of sink-input[%d] from [%s] to [auto]", si->index, "null");
471 + policy = POLICY_AUTO;
473 + pa_log_debug("[POLICY] Policy of sink input [%d] = %s", si->index, policy);
475 + /* Select sink to move and move to it */
476 + sink_to_move = policy_select_proper_sink (u->core, policy, u->is_mono);
477 + if (sink_to_move) {
478 + pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
479 + pa_sink_input_move_to(si, sink_to_move, false);
481 + pa_log_debug("[POLICY][%s] Can't move sink-input....", __func__);
487 + case SUBCOMMAND_BALANCE: {
490 + pa_channel_map map;
492 + if (pa_tagstruct_get_cvolume(t, &cvol) < 0)
495 + pa_channel_map_init_stereo(&map);
496 + balance = pa_cvolume_get_balance(&cvol, &map);
498 + pa_log_debug ("[POLICY][%s] new balance value = [%f]\n", __func__, balance);
500 + if (balance == u->balance) {
501 + pa_log_debug ("[POLICY][%s] No changes in balance value = [%f]", __func__, u->balance);
505 + u->balance = balance;
507 + /* Apply balance value to each Sinks */
508 + PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
509 + pa_cvolume* cvol = pa_sink_get_volume (s, false);
510 + pa_cvolume_set_balance (cvol, &s->channel_map, u->balance);
511 + pa_sink_set_volume(s, cvol, true, true);
520 + pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
526 + pa_tagstruct_free(reply);
531 +/* Called when new sink-input is creating */
532 +static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u)
534 + const char *policy = NULL;
537 + pa_assert(new_data);
540 + if (!new_data->proplist) {
541 + pa_log_debug("[POLICY] New stream lacks property data.");
545 + /* If sink-input has already sink, skip */
546 + if (new_data->sink) {
547 + /* sink-input with filter role will be also here because sink is already set */
549 + pa_log_debug("[POLICY] Not setting device for stream [%s], because already set.",
550 + pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
555 + /* If no policy exists, skip */
556 + if (!(policy = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_POLICY))) {
557 + pa_log_debug("[POLICY][%s] Not setting device for stream [%s], because it lacks policy.",
558 + __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
561 + pa_log_debug("[POLICY][%s] Policy for stream [%s] = [%s]",
562 + __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)), policy);
564 + /* Set proper sink to sink-input */
565 + pa_sink* new_sink = policy_select_proper_sink(c, policy, u->is_mono);
566 + if(new_sink != new_data->sink)
568 + pa_sink_input_new_data_set_sink(new_data, new_sink, false);
570 + /*new_data->save_sink = false;
571 + new_data->sink = policy_select_proper_sink (c, policy, u->is_mono);*/
572 + pa_log_debug("[POLICY][%s] set sink of sink-input to [%s]", __func__, (new_data->sink)? new_data->sink->name : "null");
577 +/* Called when new sink is added while sink-input is existing */
578 +static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u)
581 + pa_sink *sink_to_move;
591 + pa_assert(u->on_hotplug);
593 + /* If connected sink is BLUETOOTH, set as default */
594 + /* we are checking with device.api property */
595 + is_bt = policy_is_bluez(sink);
596 + is_usb_alsa = policy_is_usb_alsa(sink);
598 + if (is_bt || is_usb_alsa) {
599 + pa_log_debug("[POLICY][%s] set default sink to sink[%s][%d]", __func__, sink->name, sink->index);
600 + pa_namereg_set_default_sink (c,sink);
602 + pa_log_debug("[POLICY][%s] this sink [%s][%d] is not a bluez....return", __func__, sink->name, sink->index);
607 + /* Load mono_bt sink */
608 + args = pa_sprintf_malloc("sink_name=%s master=%s channels=1", SINK_MONO_BT, sink->name);
609 + u->module_mono_bt = pa_module_load(u->module->core, "module-remap-sink", args);
612 + /* load combine sink */
613 + args = pa_sprintf_malloc("sink_name=%s slaves=\"%s,%s\"", SINK_COMBINED, sink->name, SINK_ALSA);
614 + u->module_combined = pa_module_load(u->module->core, "module-combine", args);
617 + /* load mono_combine sink */
618 + args = pa_sprintf_malloc("sink_name=%s master=%s channels=1", SINK_MONO_COMBINED, SINK_COMBINED);
619 + u->module_mono_combined = pa_module_load(u->module->core, "module-remap-sink", args);
623 + /* Iterate each sink inputs to decide whether we should move to new sink */
624 + PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
625 + const char *policy = NULL;
627 + if (si->sink == sink)
630 + /* Skip this if it is already in the process of being moved
635 + /* It might happen that a stream and a sink are set up at the
636 + same time, in which case we want to make sure we don't
637 + interfere with that */
638 + if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
641 + /* Get role (if role is filter, skip it) */
642 + if (policy_is_filter(si))
646 + if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) {
647 + /* No policy exists, this means auto */
648 + pa_log_debug("[POLICY][%s] set policy of sink-input[%d] from [%s] to [auto]", __func__, si->index, "null");
649 + policy = POLICY_AUTO;
652 + sink_to_move = policy_select_proper_sink (c, policy, u->is_mono);
653 + if (sink_to_move) {
654 + pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
655 + pa_sink_input_move_to(si, sink_to_move, false);
657 + pa_log_debug("[POLICY][%s] Can't move sink-input....",__func__);
661 + /* Reset sink volume with balance from userdata */
662 + pa_cvolume* cvol = pa_sink_get_volume(sink, false);
663 + pa_cvolume_set_balance(cvol, &sink->channel_map, u->balance);
664 + pa_sink_set_volume(sink, cvol, true, true);
669 +static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata)
671 + struct userdata *u = userdata;
675 + pa_sink *sink_to_move = NULL;
678 + pa_log_debug("[POLICY][%s] subscribe_cb() t=[0x%x], idx=[%d]", __func__, t, idx);
680 + /* We only handle server changes */
681 + if (t == (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE)) {
683 + def = pa_namereg_get_default_sink(c);
685 + pa_log_warn("[POLICY][%s] pa_namereg_get_default_sink() returns null", __func__);
688 + pa_log_debug("[POLICY][%s] trying to move stream to current default sink = [%s]", __func__, def->name);
690 + /* Iterate each sink inputs to decide whether we should move to new DEFAULT sink */
691 + PA_IDXSET_FOREACH(si, c->sink_inputs, idx2) {
692 + const char *policy = NULL;
697 + /* Get role (if role is filter, skip it) */
698 + if (policy_is_filter(si))
702 + if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) {
703 + /* No policy exists, this means auto */
704 + pa_log_debug("[POLICY][%s] set policy of sink-input[%d] from [%s] to [auto]", __func__, si->index, "null");
705 + policy = POLICY_AUTO;
708 + sink_to_move = policy_select_proper_sink (c, policy, u->is_mono);
709 + if (sink_to_move) {
710 + /* Move sink-input to new DEFAULT sink */
711 + pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
712 + pa_sink_input_move_to(si, sink_to_move, false);
718 +static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
719 + struct userdata *u = userdata;
721 + pa_sink *sink_to_move;
728 + /* There's no point in doing anything if the core is shut down anyway */
729 + if (c->state == PA_CORE_SHUTDOWN)
732 + /* if unloading sink is not bt, just return */
733 + if (!policy_is_bluez (sink)) {
734 + pa_log_debug("[POLICY][%s] sink[%s][%d] unlinked but not a bluez....return\n", __func__, sink->name, sink->index);
738 + pa_log_debug ("[POLICY][%s] SINK unlinked ================================ sink [%s][%d], bt_off_idx was [%d]",
739 + __func__, sink->name, sink->index,u->bt_off_idx);
741 + u->bt_off_idx = sink->index;
742 + pa_log_debug ("[POLICY][%s] bt_off_idx is set to [%d]", __func__, u->bt_off_idx);
744 + /* BT sink is unloading, move sink-input to proper sink */
745 + PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
750 + /* Get role (if role is filter, skip it) */
751 + if (policy_is_filter(si))
754 + /* Find who were using bt sink or bt related sink and move them to proper sink (alsa/mono_alsa) */
755 + if (pa_streq (si->sink->name, SINK_MONO_BT) ||
756 + pa_streq (si->sink->name, SINK_MONO_COMBINED) ||
757 + pa_streq (si->sink->name, SINK_COMBINED) ||
758 + policy_is_bluez (si->sink)) {
760 + /* Move sink-input to proper sink : only alsa related sink is available now */
761 + sink_to_move = policy_get_sink_by_name (c, (u->is_mono)? SINK_MONO_ALSA : SINK_ALSA);
762 + if (sink_to_move) {
763 + pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
764 + pa_sink_input_move_to(si, sink_to_move, false);
766 + pa_log_warn("[POLICY][%s] No sink to move", __func__);
771 + pa_log_debug ("[POLICY][%s] unload sink in dependencies", __func__);
773 + /* Unload mono_combine sink */
774 + if (u->module_mono_combined) {
775 + pa_module_unload(u->module->core, u->module_mono_combined, true);
776 + u->module_mono_combined = NULL;
779 + /* Unload combine sink */
780 + if (u->module_combined) {
781 + pa_module_unload(u->module->core, u->module_combined, true);
782 + u->module_combined = NULL;
785 + /* Unload mono_bt sink */
786 + if (u->module_mono_bt) {
787 + pa_module_unload(u->module->core, u->module_mono_bt, true);
788 + u->module_mono_bt = NULL;
794 +static pa_hook_result_t sink_unlink_post_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
795 + struct userdata *u = userdata;
801 + pa_log_debug("[POLICY][%s] SINK unlinked POST ================================ sink [%s][%d]", __func__, sink->name, sink->index);
803 + /* There's no point in doing anything if the core is shut down anyway */
804 + if (c->state == PA_CORE_SHUTDOWN)
807 + /* if unloading sink is not bt, just return */
808 + if (!policy_is_bluez (sink)) {
809 + pa_log_debug("[POLICY][%s] not a bluez....return\n", __func__);
813 + u->bt_off_idx = -1;
814 + pa_log_debug ("[POLICY][%s] bt_off_idx is cleared to [%d]", __func__, u->bt_off_idx);
819 +static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
820 + pa_core_assert_ref(core);
821 + pa_sink_input_assert_ref(i);
823 + /* There's no point in doing anything if the core is shut down anyway */
824 + if (core->state == PA_CORE_SHUTDOWN)
827 + pa_log_debug ("[POLICY][%s] sink_input_move_start_cb -------------------------------------- sink-input [%d] was sink [%s][%d] : Trying to mute!!!",
828 + __func__, i->index, i->sink->name, i->sink->index);
829 + pa_sink_input_set_mute(i, true, false);
834 +static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
835 + pa_core_assert_ref(core);
836 + pa_sink_input_assert_ref(i);
838 + /* There's no point in doing anything if the core is shut down anyway */
839 + if (core->state == PA_CORE_SHUTDOWN)
842 + pa_log_debug("[POLICY][%s] sink_input_move_finish_cb -------------------------------------- sink-input [%d], sink [%s][%d], bt_off_idx [%d] : %s",
843 + __func__, i->index, i->sink->name, i->sink->index, u->bt_off_idx,
844 + (u->bt_off_idx == -1)? "Trying to un-mute!!!!" : "skip un-mute...");
846 + /* If sink input move is caused by bt sink unlink, then skip un-mute operation */
847 + if (u->bt_off_idx == -1) {
848 + pa_sink_input_set_mute(i, false, false);
854 +static pa_source* policy_get_source_by_name (pa_core *c, const char* source_name)
856 + pa_source *s = NULL;
859 + if (c == NULL || source_name == NULL) {
860 + pa_log_warn ("input param is null");
864 + PA_IDXSET_FOREACH(s, c->sources, idx) {
865 + if (pa_streq (s->name, source_name)) {
866 + pa_log_debug ("[POLICY][%s] return [%p] for [%s]\n", __func__, s, source_name);
873 +/* Select source for given condition */
874 +static pa_source* policy_select_proper_source (pa_core *c, const char* policy)
876 + pa_source* source = NULL;
877 + pa_source* def = NULL;
879 + if (c == NULL || policy == NULL) {
880 + pa_log_warn ("input param is null");
885 + def = pa_namereg_get_default_source(c);
887 + pa_log_warn ("POLICY][%s] pa_namereg_get_default_source() returns null", __func__);
891 + /* Select source to */
892 + if (pa_streq(policy, POLICY_VOIP)) {
893 + source = policy_get_source_by_name (c, AEC_SOURCE);
899 + pa_log_debug ("[POLICY][%s] selected source : [%s]\n", __func__, (source)? source->name : "null");
904 +/* Called when new source-output is creating */
905 +static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
906 + const char *policy = NULL;
908 + pa_assert(new_data);
911 + if (!new_data->proplist) {
912 + pa_log_debug("New stream lacks property data.");
916 + if (new_data->source) {
917 + pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
921 + /* If no policy exists, skip */
922 + if (!(policy = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_POLICY))) {
923 + pa_log_debug("[POLICY][%s] Not setting device for stream [%s], because it lacks policy.",
924 + __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
927 + pa_log_debug("[POLICY][%s] Policy for stream [%s] = [%s]",
928 + __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)), policy);
930 + /* Set proper source to source-output */
931 + pa_source* new_source = policy_select_proper_source(c, policy);
932 + if(new_source != new_data->source)
934 + pa_source_output_new_data_set_source(new_data, new_source, false);
936 + /*new_data->save_source= false;
937 + new_data->source= policy_select_proper_source (c, policy);*/
938 + pa_log_debug("[POLICY][%s] set source of source-input to [%s]", __func__, (new_data->source)? new_data->source->name : "null");
943 +int pa__init(pa_module *m)
945 + pa_modargs *ma = NULL;
946 + struct userdata *u;
947 + bool on_hotplug = true, on_rescue = true;
951 + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
952 + pa_log("Failed to parse module arguments");
956 + if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
957 + pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
958 + pa_log("on_hotplug= and on_rescue= expect boolean arguments");
962 + m->userdata = u = pa_xnew0(struct userdata, 1);
965 + u->on_hotplug = on_hotplug;
968 + /* A little bit later than module-stream-restore */
969 + u->sink_input_new_hook_slot =
970 + pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) sink_input_new_hook_callback, u);
972 + u->source_output_new_hook_slot =
973 + pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) source_output_new_hook_callback, u);
976 + /* A little bit later than module-stream-restore */
977 + u->sink_put_hook_slot =
978 + pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_put_hook_callback, u);
981 + /* sink unlink comes before sink-input unlink */
982 + u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_hook_callback, u);
983 + u->sink_unlink_post_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_post_hook_callback, u);
985 + u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u);
986 + u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u);
988 + u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
991 + u->bt_off_idx = -1; /* initial bt off sink index */
993 + u->module_mono_bt = NULL;
994 + u->module_combined = NULL;
995 + u->module_mono_combined = NULL;
997 + u->protocol = pa_native_protocol_get(m->core);
998 + pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1000 + /* Get mono key value for init */
1001 + vconf_get_bool(MONO_KEY, &u->is_mono);
1003 + pa_log_info("policy module is loaded\n");
1006 + pa_modargs_free(ma);
1012 + pa_modargs_free(ma);
1019 +void pa__done(pa_module *m)
1021 + struct userdata* u;
1025 + if (!(u = m->userdata))
1028 + if (u->sink_input_new_hook_slot)
1029 + pa_hook_slot_free(u->sink_input_new_hook_slot);
1030 + if (u->sink_put_hook_slot)
1031 + pa_hook_slot_free(u->sink_put_hook_slot);
1032 + if (u->sink_unlink_slot)
1033 + pa_hook_slot_free(u->sink_unlink_slot);
1034 + if (u->sink_unlink_post_slot)
1035 + pa_hook_slot_free(u->sink_unlink_post_slot);
1036 + if (u->sink_input_move_start_slot)
1037 + pa_hook_slot_free(u->sink_input_move_start_slot);
1038 + if (u->sink_input_move_finish_slot)
1039 + pa_hook_slot_free(u->sink_input_move_finish_slot);
1040 + if (u->subscription)
1041 + pa_subscription_free(u->subscription);
1042 + if (u->protocol) {
1043 + pa_native_protocol_remove_ext(u->protocol, m);
1044 + pa_native_protocol_unref(u->protocol);
1046 + if (u->source_output_new_hook_slot)
1047 + pa_hook_slot_free(u->source_output_new_hook_slot);
1052 + pa_log_info("policy module is unloaded\n");
1054 diff --git a/src/pulse/ext-policy.c b/src/pulse/ext-policy.c
1055 new file mode 100644
1056 index 0000000..f3a3a8c
1058 +++ b/src/pulse/ext-policy.c
1061 + This file is part of PulseAudio.
1063 + PulseAudio is free software; you can redistribute it and/or modify
1064 + it under the terms of the GNU Lesser General Public License as published
1065 + by the Free Software Foundation; either version 2.1 of the License,
1066 + or (at your option) any later version.
1068 + PulseAudio is distributed in the hope that it will be useful, but
1069 + WITHOUT ANY WARRANTY; without even the implied warranty of
1070 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1071 + General Public License for more details.
1073 + You should have received a copy of the GNU Lesser General Public License
1074 + along with PulseAudio; if not, write to the Free Software
1075 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1079 +#ifdef HAVE_CONFIG_H
1080 +#include <config.h>
1083 +#include <pulse/context.h>
1084 +#include <pulse/gccmacro.h>
1085 +#include <pulse/xmalloc.h>
1087 +#include <pulsecore/macro.h>
1088 +#include <pulsecore/pstream-util.h>
1090 +#include "internal.h"
1091 +#include "operation.h"
1092 +#include "fork-detect.h"
1094 +#include "ext-policy.h"
1099 + SUBCOMMAND_BALANCE,
1102 +static void ext_policy_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
1103 + pa_operation *o = userdata;
1104 + uint32_t version = PA_INVALID_INDEX;
1108 + pa_assert(PA_REFCNT_VALUE(o) >= 1);
1113 + if (command != PA_COMMAND_REPLY) {
1114 + if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
1117 + } else if (pa_tagstruct_getu32(t, &version) < 0 ||
1118 + !pa_tagstruct_eof(t)) {
1120 + pa_context_fail(o->context, PA_ERR_PROTOCOL);
1124 + if (o->callback) {
1125 + pa_ext_policy_test_cb_t cb = (pa_ext_policy_test_cb_t) o->callback;
1126 + cb(o->context, version, o->userdata);
1130 + pa_operation_done(o);
1131 + pa_operation_unref(o);
1134 +pa_operation *pa_ext_policy_test(
1136 + pa_ext_device_manager_test_cb_t cb,
1144 + pa_assert(PA_REFCNT_VALUE(c) >= 1);
1146 + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
1147 + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
1148 + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
1150 + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
1152 + t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
1153 + pa_tagstruct_putu32(t, PA_INVALID_INDEX);
1154 + pa_tagstruct_puts(t, "module-policy");
1155 + pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
1156 + pa_pstream_send_tagstruct(c->pstream, t);
1157 + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_policy_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
1162 +pa_operation *pa_ext_policy_set_mono (
1165 + pa_context_success_cb_t cb,
1169 + pa_operation *o = NULL;
1170 + pa_tagstruct *t = NULL;
1173 + pa_assert(PA_REFCNT_VALUE(c) >= 1);
1175 + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
1176 + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
1177 + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
1179 + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
1181 + t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
1182 + pa_tagstruct_putu32(t, PA_INVALID_INDEX);
1183 + pa_tagstruct_puts(t, "module-policy");
1184 + pa_tagstruct_putu32(t, SUBCOMMAND_MONO);
1185 + pa_tagstruct_put_boolean(t, !!enable);
1187 + pa_pstream_send_tagstruct(c->pstream, t);
1188 + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
1193 +pa_operation *pa_ext_policy_set_balance (
1196 + pa_context_success_cb_t cb,
1200 + pa_operation *o = NULL;
1201 + pa_tagstruct *t = NULL;
1203 + pa_channel_map map;
1206 + pa_assert(PA_REFCNT_VALUE(c) >= 1);
1208 + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
1209 + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
1210 + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
1212 + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
1214 + t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
1215 + pa_tagstruct_putu32(t, PA_INVALID_INDEX);
1216 + pa_tagstruct_puts(t, "module-policy");
1217 + pa_tagstruct_putu32(t, SUBCOMMAND_BALANCE);
1219 + /* Prepare cvolume for transfer */
1220 + pa_channel_map_init_stereo(&map);
1221 + pa_cvolume_set(&cvol, map.channels, 65535);
1223 + pa_log_error ("balance = %f", *balance);
1225 + pa_cvolume_set_balance(&cvol, &map, *balance);
1227 + pa_log_error ("balance get = %f", pa_cvolume_get_balance(&cvol, &map));
1229 + pa_tagstruct_put_cvolume(t, &cvol);
1231 + pa_pstream_send_tagstruct(c->pstream, t);
1232 + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
1237 diff --git a/src/pulse/ext-policy.h b/src/pulse/ext-policy.h
1238 new file mode 100644
1239 index 0000000..ec62ead
1241 +++ b/src/pulse/ext-policy.h
1243 +#ifndef foopulseextpolicyhfoo
1244 +#define foopulseextpolicyhfoo
1247 + This file is part of PulseAudio.
1249 + PulseAudio is free software; you can redistribute it and/or modify
1250 + it under the terms of the GNU Lesser General Public License as published
1251 + by the Free Software Foundation; either version 2.1 of the License,
1252 + or (at your option) any later version.
1254 + PulseAudio is distributed in the hope that it will be useful, but
1255 + WITHOUT ANY WARRANTY; without even the implied warranty of
1256 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1257 + General Public License for more details.
1259 + You should have received a copy of the GNU Lesser General Public License
1260 + along with PulseAudio; if not, write to the Free Software
1261 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1265 +#include <pulse/context.h>
1266 +#include <pulse/version.h>
1270 + * Routines for controlling module-policy
1275 +/** Callback prototype for pa_ext_policy_test(). \since 0.9.21 */
1276 +typedef void (*pa_ext_policy_test_cb_t)(
1281 +/** Test if this extension module is available in the server. \since 0.9.21 */
1282 +pa_operation *pa_ext_policy_test(
1284 + pa_ext_policy_test_cb_t cb,
1287 +/** Enable the mono mode. \since 0.9.21 */
1288 +pa_operation *pa_ext_policy_set_mono (
1291 + pa_context_success_cb_t cb,
1294 +/** Enable the balance mode. \since 0.9.21 */
1295 +pa_operation *pa_ext_policy_set_balance (
1298 + pa_context_success_cb_t cb,
1304 diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
1305 index dc3cddc..341abaa 100644
1306 --- a/src/pulse/proplist.h
1307 +++ b/src/pulse/proplist.h
1308 @@ -65,6 +65,9 @@ PA_C_DECL_BEGIN
1309 /** For streams: logic role of this media. One of the strings "video", "music", "game", "event", "phone", "animation", "production", "a11y", "test" */
1310 #define PA_PROP_MEDIA_ROLE "media.role"
1312 +/** For streams: logic role of this media. One of the strings "auto", "phone" */
1313 +#define PA_PROP_MEDIA_POLICY "media.policy"
1315 /** For streams: the name of a filter that is desired, e.g.\ "echo-cancel" or "equalizer-sink". PulseAudio may choose to not apply the filter if it does not make sense (for example, applying echo-cancellation on a Bluetooth headset probably does not make sense. \since 1.0 */
1316 #define PA_PROP_FILTER_WANT "filter.want"