fb976b19d0c1e03adb2172d2d3fe045849badd01
[scm/bb/meta-tizen.git] / recipes-multimedia / pulseaudio / pulseaudio_5.0 / 0038-add-policy-module-samsung.patch
1 From: "vivian,zhang" <vivian.zhang@intel.com>
2 Date: Tue, 18 Jun 2013 16:21:32 +0800
3 Subject: add policy module - samsung
4
5 Change-Id: I2111a9c4dc0a371dbea5b347cf77adbe8f930528
6 Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
7 ---
8  configure.ac                |  18 +
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
18
19 diff --git a/configure.ac b/configure.ac
20 index c0beac0..0e205d3 100644
21 --- a/configure.ac
22 +++ b/configure.ac
23 @@ -643,6 +643,24 @@ PKG_CHECK_MODULES(LIBJSON, [ json-c >= 0.11 ], [],
24  
25  PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ])
26  
27 +dnl use samsung policy module --------------------------------------------------------
28 +AC_ARG_ENABLE(samsung-policy, AC_HELP_STRING([--enable-samsung-policy], [using samsung-policy]),
29 +[
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) ;;
34 + esac
35 + ],[USE_SAMSUNG_POLICY=no])
36 +
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)
41 +fi
42 +AM_CONDITIONAL(USE_SAMSUNG_POLICY, test "x$USE_SAMSUNG_POLICY" = "xyes")
43 +dnl end --------------------------------------------------------------------
44 +
45  dnl use dlog --------------------------------------------------------------------------
46  AC_ARG_ENABLE(dlog, AC_HELP_STRING([--enable-dlog], [using dlog]),
47  [
48 diff --git a/src/Makefile.am b/src/Makefile.am
49 index 3e41300..4872dfd 100644
50 --- a/src/Makefile.am
51 +++ b/src/Makefile.am
52 @@ -149,7 +149,7 @@ pulseaudio_SOURCES = \
53                 daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
54                 daemon/main.c
55  
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 = \
62                 pulse/volume.h \
63                 pulse/xmalloc.h
64  
65 +if USE_SAMSUNG_POLICY
66 +pulseinclude_HEADERS += \
67 +               pulse/ext-policy.h
68 +endif
69 +
70  lib_LTLIBRARIES = \
71                 libpulse.la \
72                 libpulse-simple.la
73 @@ -833,6 +838,11 @@ libpulse_la_SOURCES = \
74                 pulse/volume.c pulse/volume.h \
75                 pulse/xmalloc.c pulse/xmalloc.h
76  
77 +if USE_SAMSUNG_POLICY
78 +libpulse_la_SOURCES += \
79 +        pulse/ext-policy.c pulse/ext-policy.h
80 +endif
81 +
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 += \
88                 module-console-kit.la
89 +if USE_SAMSUNG_POLICY
90 +modlibexec_LTLIBRARIES += \
91 +               module-policy.la
92 +endif
93  endif
94  
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
100 -
101 +if USE_SAMSUNG_POLICY
102 +SYMDEF_FILES += \
103 +               module-policy-symdef.h
104 +endif
105  if HAVE_ESOUND
106  SYMDEF_FILES += \
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)
111  
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)
117 +endif
118 +
119  ###################################
120  #        Some minor stuff         #
121  ###################################
122 diff --git a/src/modules/module-policy.c b/src/modules/module-policy.c
123 new file mode 100644
124 index 0000000..2172018
125 --- /dev/null
126 +++ b/src/modules/module-policy.c
127 @@ -0,0 +1,926 @@
128 +#ifdef HAVE_CONFIG_H
129 +#include <config.h>
130 +#endif
131 +
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>
140 +
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>
147 +
148 +#include <pulsecore/protocol-native.h>
149 +#include <pulsecore/pstream-util.h>
150 +#include <vconf.h> // for mono
151 +
152 +#include "module-policy-symdef.h"
153 +
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);
158 +PA_MODULE_USAGE(
159 +        "on_hotplug=<When new device becomes available, recheck streams?> ");
160 +
161 +static const char* const valid_modargs[] = {
162 +    "on_hotplug",
163 +    NULL
164 +};
165 +
166 +struct userdata {
167 +    pa_core *core;
168 +    pa_module *module;
169 +
170 +    pa_hook_slot *sink_input_new_hook_slot,*sink_put_hook_slot;
171 +
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;
176 +
177 +    bool on_hotplug:1;
178 +    int        bt_off_idx;
179 +
180 +    int is_mono;
181 +    float balance;
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;
187 +};
188 +
189 +enum {
190 +    SUBCOMMAND_TEST,
191 +    SUBCOMMAND_MONO,
192 +    SUBCOMMAND_BALANCE,
193 +};
194 +
195 +/* DEFINEs */
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
210 +
211 +/* check if this sink is bluez */
212 +static bool policy_is_bluez (pa_sink* sink)
213 +{
214 +       const char* api_name = NULL;
215 +
216 +       if (sink == NULL) {
217 +               pa_log_warn ("input param sink is null");
218 +               return false;
219 +       }
220 +
221 +    api_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_API);
222 +       if (api_name) {
223 +               if (pa_streq (api_name, BLUEZ_API)) {
224 +#ifdef DEBUG_DETAIL
225 +                       pa_log_debug("[POLICY][%s] [%s] exists and it is [%s]...true !!", __func__, PA_PROP_DEVICE_API, api_name);
226 +#endif
227 +                       return true;
228 +               } else {
229 +#ifdef DEBUG_DETAIL
230 +                       pa_log_debug("[POLICY][%s] [%s] exists, but not bluez...false !!", __func__, PA_PROP_DEVICE_API);
231 +#endif
232 +               }
233 +       } else {
234 +#ifdef DEBUG_DETAIL
235 +               pa_log_debug("[POLICY][%s] No [%s] exists...false!!", __func__, PA_PROP_DEVICE_API);
236 +#endif
237 +       }
238 +
239 +       return false;
240 +}
241 +
242 +/* check if this sink is bluez */
243 +static bool policy_is_usb_alsa (pa_sink* sink)
244 +{
245 +       const char* api_name = NULL;
246 +       const char* device_bus_name = NULL;
247 +
248 +       if (sink == NULL) {
249 +               pa_log_warn ("input param sink is null");
250 +               return false;
251 +       }
252 +
253 +    api_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_API);
254 +       if (api_name) {
255 +               if (pa_streq (api_name, ALSA_API)) {
256 +#ifdef DEBUG_DETAIL
257 +                       pa_log_debug("[POLICY][%s] [%s] exists and it is [%s]...true !!", __func__, PA_PROP_DEVICE_API, api_name);
258 +#endif
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")) {
262 +                                       return true;
263 +                               }
264 +                       }
265 +               } else {
266 +#ifdef DEBUG_DETAIL
267 +                       pa_log_debug("[POLICY][%s] [%s] exists, but not alsa...false !!", __func__, PA_PROP_DEVICE_API);
268 +#endif
269 +               }
270 +       } else {
271 +#ifdef DEBUG_DETAIL
272 +               pa_log_debug("[POLICY][%s] No [%s] exists...false!!", __func__, PA_PROP_DEVICE_API);
273 +#endif
274 +       }
275 +
276 +       return false;
277 +}
278 +
279 +/* Get sink by name */
280 +static pa_sink* policy_get_sink_by_name (pa_core *c, const char* sink_name)
281 +{
282 +    pa_sink *s = NULL;
283 +    uint32_t idx;
284 +
285 +    if (c == NULL || sink_name == NULL) {
286 +               pa_log_warn ("input param is null");
287 +               return NULL;
288 +    }
289 +
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);
293 +                       return s;
294 +               }
295 +       }
296 +       return NULL;
297 +}
298 +
299 +/* Get bt sink if available */
300 +static pa_sink* policy_get_bt_sink (pa_core *c)
301 +{
302 +    pa_sink *s = NULL;
303 +    uint32_t idx;
304 +
305 +    if (c == NULL) {
306 +               pa_log_warn ("input param is null");
307 +               return NULL;
308 +    }
309 +
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);
313 +                       return s;
314 +               }
315 +       }
316 +       return NULL;
317 +}
318 +
319 +/* Select sink for given condition */
320 +static pa_sink* policy_select_proper_sink (pa_core *c, const char* policy, int is_mono)
321 +{
322 +       pa_sink* sink = NULL;
323 +       pa_sink* bt_sink = NULL;
324 +       pa_sink* def = NULL;
325 +
326 +       if (c == NULL || policy == NULL) {
327 +               pa_log_warn ("input param is null");
328 +               return NULL;
329 +       }
330 +
331 +       pa_assert (c);
332 +
333 +       bt_sink = policy_get_bt_sink(c);
334 +       def = pa_namereg_get_default_sink(c);
335 +       if (def == NULL) {
336 +               pa_log_warn ("POLICY][%s] pa_namereg_get_default_sink() returns null", __func__);
337 +               return NULL;
338 +       }
339 +
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");
342 +
343 +       /* Select sink to */
344 +       if (pa_streq(policy, POLICY_ALL)) {
345 +               /* all */
346 +               if (bt_sink) {
347 +                       sink = policy_get_sink_by_name(c, (is_mono)? SINK_MONO_COMBINED : SINK_COMBINED);
348 +               } else {
349 +                       sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA);
350 +               }
351 +
352 +       } else if (pa_streq(policy, POLICY_PHONE)) {
353 +               /* phone */
354 +               sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA);
355 +       } else if (pa_streq(policy, POLICY_VOIP)) {
356 +               /* VOIP */
357 +               sink = policy_get_sink_by_name (c,AEC_SINK);
358 +       } else {
359 +               /* auto */
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)) {
363 +                       sink = def;
364 +               } else {
365 +                       sink = (is_mono)? policy_get_sink_by_name (c, SINK_MONO_ALSA) : def;
366 +               }
367 +       }
368 +
369 +       pa_log_debug ("[POLICY][%s] selected sink : [%s]\n", __func__, (sink)? sink->name : "null");
370 +       return sink;
371 +}
372 +
373 +static bool policy_is_filter (pa_sink_input* si)
374 +{
375 +       const char* role = NULL;
376 +
377 +       if (si == NULL) {
378 +               pa_log_warn ("input param sink-input is null");
379 +               return false;
380 +       }
381 +
382 +       if ((role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) {
383 +#ifdef DEBUG_DETAIL
384 +               pa_log_debug("[POLICY][%s] Role of sink input [%d] = %s", __func__, si->index, role);
385 +#endif
386 +               if (pa_streq(role, "filter")) {
387 +#ifdef DEBUG_DETAIL
388 +                       pa_log_debug("[POLICY] no need to change of sink for %s", role);
389 +#endif
390 +                       return true;
391 +               }
392 +       }
393 +
394 +       return false;
395 +}
396 +
397 +
398 +
399 +#define EXT_VERSION 1
400 +
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;
403 +  uint32_t command;
404 +  pa_tagstruct *reply = NULL;
405 +
406 +  pa_sink_input *si = NULL;
407 +  pa_sink *s = NULL;
408 +  uint32_t idx;
409 +  pa_sink* sink_to_move  = NULL;
410 +
411 +  pa_assert(p);
412 +  pa_assert(m);
413 +  pa_assert(c);
414 +  pa_assert(t);
415 +
416 +  u = m->userdata;
417 +
418 +  if (pa_tagstruct_getu32(t, &command) < 0)
419 +    goto fail;
420 +
421 +  reply = pa_tagstruct_new(NULL, 0);
422 +  pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
423 +  pa_tagstruct_putu32(reply, tag);
424 +
425 +  switch (command) {
426 +    case SUBCOMMAND_TEST: {
427 +               if (!pa_tagstruct_eof(t))
428 +                       goto fail;
429 +
430 +               pa_tagstruct_putu32(reply, EXT_VERSION);
431 +               break;
432 +    }
433 +
434 +    case SUBCOMMAND_MONO: {
435 +
436 +        bool enable;
437 +
438 +        if (pa_tagstruct_get_boolean(t, &enable) < 0)
439 +            goto fail;
440 +
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);
444 +                       break;
445 +        }
446 +
447 +        u->is_mono = enable;
448 +
449 +               /* Move current sink-input to proper mono sink */
450 +               PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
451 +                       const char *policy = NULL;
452 +
453 +                       /* Skip this if it is already in the process of being moved
454 +                        * anyway */
455 +                       if (!si->sink)
456 +                               continue;
457 +
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)))
462 +                               continue;
463 +
464 +                       /* Get role (if role is filter, skip it) */
465 +                       if (policy_is_filter(si))
466 +                               continue;
467 +
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;
472 +                       }
473 +                       pa_log_debug("[POLICY] Policy of sink input [%d] = %s", si->index, policy);
474 +
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);
480 +                       } else {
481 +                               pa_log_debug("[POLICY][%s] Can't move sink-input....", __func__);
482 +                       }
483 +               }
484 +        break;
485 +    }
486 +
487 +    case SUBCOMMAND_BALANCE: {
488 +               float balance;
489 +               pa_cvolume cvol;
490 +               pa_channel_map map;
491 +
492 +               if (pa_tagstruct_get_cvolume(t, &cvol) < 0)
493 +                       goto fail;
494 +
495 +               pa_channel_map_init_stereo(&map);
496 +               balance = pa_cvolume_get_balance(&cvol, &map);
497 +
498 +               pa_log_debug ("[POLICY][%s] new balance value = [%f]\n", __func__, balance);
499 +
500 +               if (balance == u->balance) {
501 +                       pa_log_debug ("[POLICY][%s] No changes in balance value = [%f]", __func__, u->balance);
502 +                       break;
503 +               }
504 +
505 +               u->balance = balance;
506 +
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);
512 +               }
513 +               break;
514 +       }
515 +
516 +    default:
517 +      goto fail;
518 +  }
519 +
520 +  pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
521 +  return 0;
522 +
523 +  fail:
524 +
525 +  if (reply)
526 +         pa_tagstruct_free(reply);
527 +
528 +  return -1;
529 +}
530 +
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)
533 +{
534 +    const char *policy = NULL;
535 +
536 +    pa_assert(c);
537 +    pa_assert(new_data);
538 +    pa_assert(u);
539 +
540 +    if (!new_data->proplist) {
541 +        pa_log_debug("[POLICY] New stream lacks property data.");
542 +        return PA_HOOK_OK;
543 +    }
544 +
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 */
548 +#ifdef DEBUG_DETAIL
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)));
551 +#endif
552 +        return PA_HOOK_OK;
553 +    }
554 +
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)));
559 +        return PA_HOOK_OK;
560 +    }
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);
563 +
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)
567 +    {
568 +        pa_sink_input_new_data_set_sink(new_data, new_sink, false);
569 +    }
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");
573 +
574 +    return PA_HOOK_OK;
575 +}
576 +
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)
579 +{
580 +    pa_sink_input *si;
581 +    pa_sink *sink_to_move;
582 +    uint32_t idx;
583 +    char *args = NULL;
584 +
585 +    bool is_bt;
586 +    bool is_usb_alsa;
587 +
588 +    pa_assert(c);
589 +    pa_assert(sink);
590 +    pa_assert(u);
591 +    pa_assert(u->on_hotplug);
592 +
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);
597 +
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);
601 +       } else {
602 +               pa_log_debug("[POLICY][%s] this sink [%s][%d] is not a bluez....return", __func__, sink->name, sink->index);
603 +               return PA_HOOK_OK;
604 +       }
605 +
606 +       if (is_bt) {
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);
610 +               pa_xfree(args);
611 +
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);
615 +               pa_xfree(args);
616 +
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);
620 +               pa_xfree(args);
621 +       }
622 +
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;
626 +
627 +        if (si->sink == sink)
628 +               continue;
629 +
630 +        /* Skip this if it is already in the process of being moved
631 +         * anyway */
632 +        if (!si->sink)
633 +            continue;
634 +
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)))
639 +            continue;
640 +
641 +               /* Get role (if role is filter, skip it) */
642 +        if (policy_is_filter(si))
643 +               continue;
644 +
645 +               /* Check policy */
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;
650 +               }
651 +
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);
656 +               } else {
657 +                       pa_log_debug("[POLICY][%s] Can't move sink-input....",__func__);
658 +               }
659 +    }
660 +
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);
665 +
666 +    return PA_HOOK_OK;
667 +}
668 +
669 +static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata)
670 +{
671 +    struct userdata *u = userdata;
672 +    pa_sink *def;
673 +    pa_sink_input *si;
674 +    uint32_t idx2;
675 +    pa_sink *sink_to_move = NULL;
676 +    pa_assert(u);
677 +
678 +    pa_log_debug("[POLICY][%s] subscribe_cb() t=[0x%x], idx=[%d]", __func__, t, idx);
679 +
680 +    /* We only handle server changes */
681 +    if (t == (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE)) {
682 +
683 +       def = pa_namereg_get_default_sink(c);
684 +               if (def == NULL) {
685 +                       pa_log_warn("[POLICY][%s] pa_namereg_get_default_sink() returns null", __func__);
686 +                       return;
687 +               }
688 +       pa_log_debug("[POLICY][%s] trying to move stream to current default sink = [%s]", __func__, def->name);
689 +
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;
693 +
694 +                       if (!si->sink)
695 +                               continue;
696 +
697 +                       /* Get role (if role is filter, skip it) */
698 +                       if (policy_is_filter(si))
699 +                               continue;
700 +
701 +                       /* Get policy */
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;
706 +                       }
707 +
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);
713 +                       }
714 +       }
715 +    }
716 +}
717 +
718 +static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
719 +    struct userdata *u = userdata;
720 +    uint32_t idx;
721 +    pa_sink *sink_to_move;
722 +    pa_sink_input      *si;
723 +
724 +    pa_assert(c);
725 +    pa_assert(sink);
726 +    pa_assert(u);
727 +
728 +     /* There's no point in doing anything if the core is shut down anyway */
729 +    if (c->state == PA_CORE_SHUTDOWN)
730 +        return PA_HOOK_OK;
731 +
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);
735 +               return PA_HOOK_OK;
736 +       }
737 +
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);
740 +
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);
743 +
744 +       /* BT sink is unloading, move sink-input to proper sink */
745 +       PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
746 +
747 +               if (!si->sink)
748 +                       continue;
749 +
750 +               /* Get role (if role is filter, skip it) */
751 +               if (policy_is_filter(si))
752 +                       continue;
753 +
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)) {
759 +
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);
765 +                       } else {
766 +                               pa_log_warn("[POLICY][%s] No sink to move", __func__);
767 +                       }
768 +               }
769 +       }
770 +
771 +       pa_log_debug ("[POLICY][%s] unload sink in dependencies", __func__);
772 +
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;
777 +    }
778 +
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;
783 +    }
784 +
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;
789 +       }
790 +
791 +    return PA_HOOK_OK;
792 +}
793 +
794 +static pa_hook_result_t sink_unlink_post_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
795 +    struct userdata *u = userdata;
796 +
797 +    pa_assert(c);
798 +    pa_assert(sink);
799 +    pa_assert(u);
800 +
801 +    pa_log_debug("[POLICY][%s] SINK unlinked POST ================================ sink [%s][%d]", __func__, sink->name, sink->index);
802 +
803 +     /* There's no point in doing anything if the core is shut down anyway */
804 +    if (c->state == PA_CORE_SHUTDOWN)
805 +        return PA_HOOK_OK;
806 +
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__);
810 +               return PA_HOOK_OK;
811 +       }
812 +
813 +    u->bt_off_idx = -1;
814 +    pa_log_debug ("[POLICY][%s] bt_off_idx is cleared to [%d]", __func__, u->bt_off_idx);
815 +
816 +    return PA_HOOK_OK;
817 +}
818 +
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);
822 +
823 +    /* There's no point in doing anything if the core is shut down anyway */
824 +   if (core->state == PA_CORE_SHUTDOWN)
825 +       return PA_HOOK_OK;
826 +
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);
830 +
831 +    return PA_HOOK_OK;
832 +}
833 +
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);
837 +
838 +    /* There's no point in doing anything if the core is shut down anyway */
839 +   if (core->state == PA_CORE_SHUTDOWN)
840 +       return PA_HOOK_OK;
841 +
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...");
845 +
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);
849 +    }
850 +
851 +    return PA_HOOK_OK;
852 +}
853 +
854 +static pa_source* policy_get_source_by_name (pa_core *c, const char* source_name)
855 +{
856 +       pa_source *s = NULL;
857 +       uint32_t idx;
858 +
859 +       if (c == NULL || source_name == NULL) {
860 +               pa_log_warn ("input param is null");
861 +               return NULL;
862 +       }
863 +
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);
867 +                       return s;
868 +               }
869 +       }
870 +       return NULL;
871 +}
872 +
873 +/* Select source for given condition */
874 +static pa_source* policy_select_proper_source (pa_core *c, const char* policy)
875 +{
876 +       pa_source* source = NULL;
877 +       pa_source* def = NULL;
878 +
879 +       if (c == NULL || policy == NULL) {
880 +               pa_log_warn ("input param is null");
881 +               return NULL;
882 +       }
883 +
884 +       pa_assert (c);
885 +       def = pa_namereg_get_default_source(c);
886 +       if (def == NULL) {
887 +               pa_log_warn ("POLICY][%s] pa_namereg_get_default_source() returns null", __func__);
888 +               return NULL;
889 +       }
890 +
891 +       /* Select source  to */
892 +       if (pa_streq(policy, POLICY_VOIP)) {
893 +               source = policy_get_source_by_name (c, AEC_SOURCE);
894 +
895 +       } else {
896 +               source = def;
897 +       }
898 +
899 +       pa_log_debug ("[POLICY][%s] selected source : [%s]\n", __func__, (source)? source->name : "null");
900 +       return source;
901 +}
902 +
903 +
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;
907 +       pa_assert(c);
908 +       pa_assert(new_data);
909 +       pa_assert(u);
910 +
911 +       if (!new_data->proplist) {
912 +               pa_log_debug("New stream lacks property data.");
913 +               return PA_HOOK_OK;
914 +       }
915 +
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)));
918 +               return PA_HOOK_OK;
919 +       }
920 +
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)));
925 +               return PA_HOOK_OK;
926 +       }
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);
929 +
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)
933 +    {
934 +        pa_source_output_new_data_set_source(new_data, new_source, false);
935 +    }
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");
939 +
940 +       return PA_HOOK_OK;
941 +}
942 +
943 +int pa__init(pa_module *m)
944 +{
945 +       pa_modargs *ma = NULL;
946 +       struct userdata *u;
947 +       bool on_hotplug = true, on_rescue = true;
948 +
949 +       pa_assert(m);
950 +
951 +       if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
952 +               pa_log("Failed to parse module arguments");
953 +               goto fail;
954 +       }
955 +
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");
959 +               goto fail;
960 +       }
961 +
962 +       m->userdata = u = pa_xnew0(struct userdata, 1);
963 +       u->core = m->core;
964 +       u->module = m;
965 +       u->on_hotplug = on_hotplug;
966 +
967 +
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);
971 +
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);
974 +
975 +       if (on_hotplug) {
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);
979 +       }
980 +
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);
984 +
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);
987 +
988 +       u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
989 +
990 +
991 +       u->bt_off_idx = -1;     /* initial bt off sink index */
992 +
993 +       u->module_mono_bt = NULL;
994 +       u->module_combined = NULL;
995 +       u->module_mono_combined = NULL;
996 +
997 +    u->protocol = pa_native_protocol_get(m->core);
998 +    pa_native_protocol_install_ext(u->protocol, m, extension_cb);
999 +
1000 +    /* Get mono key value for init */
1001 +       vconf_get_bool(MONO_KEY, &u->is_mono);
1002 +
1003 +       pa_log_info("policy module is loaded\n");
1004 +
1005 +       if (ma)
1006 +               pa_modargs_free(ma);
1007 +
1008 +       return 0;
1009 +
1010 +fail:
1011 +       if (ma)
1012 +               pa_modargs_free(ma);
1013 +
1014 +       pa__done(m);
1015 +
1016 +       return -1;
1017 +}
1018 +
1019 +void pa__done(pa_module *m)
1020 +{
1021 +    struct userdata* u;
1022 +
1023 +    pa_assert(m);
1024 +
1025 +    if (!(u = m->userdata))
1026 +        return;
1027 +
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);
1045 +    }
1046 +    if (u->source_output_new_hook_slot)
1047 +        pa_hook_slot_free(u->source_output_new_hook_slot);
1048 +
1049 +    pa_xfree(u);
1050 +
1051 +
1052 +       pa_log_info("policy module is unloaded\n");
1053 +}
1054 diff --git a/src/pulse/ext-policy.c b/src/pulse/ext-policy.c
1055 new file mode 100644
1056 index 0000000..f3a3a8c
1057 --- /dev/null
1058 +++ b/src/pulse/ext-policy.c
1059 @@ -0,0 +1,177 @@
1060 +/***
1061 +  This file is part of PulseAudio.
1062 +
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.
1067 +
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.
1072 +
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
1076 +  USA.
1077 +***/
1078 +
1079 +#ifdef HAVE_CONFIG_H
1080 +#include <config.h>
1081 +#endif
1082 +
1083 +#include <pulse/context.h>
1084 +#include <pulse/gccmacro.h>
1085 +#include <pulse/xmalloc.h>
1086 +
1087 +#include <pulsecore/macro.h>
1088 +#include <pulsecore/pstream-util.h>
1089 +
1090 +#include "internal.h"
1091 +#include "operation.h"
1092 +#include "fork-detect.h"
1093 +
1094 +#include "ext-policy.h"
1095 +
1096 +enum {
1097 +    SUBCOMMAND_TEST,
1098 +    SUBCOMMAND_MONO,
1099 +    SUBCOMMAND_BALANCE,
1100 +};
1101 +
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;
1105 +
1106 +    pa_assert(pd);
1107 +    pa_assert(o);
1108 +    pa_assert(PA_REFCNT_VALUE(o) >= 1);
1109 +
1110 +    if (!o->context)
1111 +        goto finish;
1112 +
1113 +    if (command != PA_COMMAND_REPLY) {
1114 +        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
1115 +            goto finish;
1116 +
1117 +    } else if (pa_tagstruct_getu32(t, &version) < 0 ||
1118 +               !pa_tagstruct_eof(t)) {
1119 +
1120 +        pa_context_fail(o->context, PA_ERR_PROTOCOL);
1121 +        goto finish;
1122 +    }
1123 +
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);
1127 +    }
1128 +
1129 +finish:
1130 +    pa_operation_done(o);
1131 +    pa_operation_unref(o);
1132 +}
1133 +
1134 +pa_operation *pa_ext_policy_test(
1135 +        pa_context *c,
1136 +        pa_ext_device_manager_test_cb_t cb,
1137 +        void *userdata) {
1138 +
1139 +    uint32_t tag;
1140 +    pa_operation *o;
1141 +    pa_tagstruct *t;
1142 +
1143 +    pa_assert(c);
1144 +    pa_assert(PA_REFCNT_VALUE(c) >= 1);
1145 +
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);
1149 +
1150 +    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
1151 +
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);
1158 +
1159 +    return o;
1160 +}
1161 +
1162 +pa_operation *pa_ext_policy_set_mono (
1163 +        pa_context *c,
1164 +        int enable,
1165 +        pa_context_success_cb_t cb,
1166 +        void *userdata) {
1167 +
1168 +    uint32_t tag;
1169 +    pa_operation *o = NULL;
1170 +    pa_tagstruct *t = NULL;
1171 +
1172 +    pa_assert(c);
1173 +    pa_assert(PA_REFCNT_VALUE(c) >= 1);
1174 +
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);
1178 +
1179 +    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
1180 +
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);
1186 +
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);
1189 +
1190 +    return o;
1191 +}
1192 +
1193 +pa_operation *pa_ext_policy_set_balance (
1194 +        pa_context *c,
1195 +        double *balance,
1196 +        pa_context_success_cb_t cb,
1197 +        void *userdata) {
1198 +
1199 +    uint32_t tag;
1200 +    pa_operation *o = NULL;
1201 +    pa_tagstruct *t = NULL;
1202 +    pa_cvolume cvol;
1203 +    pa_channel_map map;
1204 +
1205 +    pa_assert(c);
1206 +    pa_assert(PA_REFCNT_VALUE(c) >= 1);
1207 +
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);
1211 +
1212 +    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
1213 +
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);
1218 +
1219 +    /* Prepare cvolume for transfer */
1220 +    pa_channel_map_init_stereo(&map);
1221 +    pa_cvolume_set(&cvol, map.channels, 65535);
1222 +
1223 +    pa_log_error ("balance = %f", *balance);
1224 +
1225 +    pa_cvolume_set_balance(&cvol, &map, *balance);
1226 +
1227 +    pa_log_error ("balance get = %f", pa_cvolume_get_balance(&cvol, &map));
1228 +
1229 +    pa_tagstruct_put_cvolume(t, &cvol);
1230 +
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);
1233 +
1234 +    return o;
1235 +}
1236 +
1237 diff --git a/src/pulse/ext-policy.h b/src/pulse/ext-policy.h
1238 new file mode 100644
1239 index 0000000..ec62ead
1240 --- /dev/null
1241 +++ b/src/pulse/ext-policy.h
1242 @@ -0,0 +1,61 @@
1243 +#ifndef foopulseextpolicyhfoo
1244 +#define foopulseextpolicyhfoo
1245 +
1246 +/***
1247 +  This file is part of PulseAudio.
1248 +
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.
1253 +
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.
1258 +
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
1262 +  USA.
1263 +***/
1264 +
1265 +#include <pulse/context.h>
1266 +#include <pulse/version.h>
1267 +
1268 +/** \file
1269 + *
1270 + * Routines for controlling module-policy
1271 + */
1272 +
1273 +PA_C_DECL_BEGIN
1274 +
1275 +/** Callback prototype for pa_ext_policy_test(). \since 0.9.21 */
1276 +typedef void (*pa_ext_policy_test_cb_t)(
1277 +        pa_context *c,
1278 +        uint32_t version,
1279 +        void *userdata);
1280 +
1281 +/** Test if this extension module is available in the server. \since 0.9.21 */
1282 +pa_operation *pa_ext_policy_test(
1283 +        pa_context *c,
1284 +        pa_ext_policy_test_cb_t cb,
1285 +        void *userdata);
1286 +
1287 +/** Enable the mono mode. \since 0.9.21 */
1288 +pa_operation *pa_ext_policy_set_mono (
1289 +        pa_context *c,
1290 +        int enable,
1291 +        pa_context_success_cb_t cb,
1292 +        void *userdata);
1293 +
1294 +/** Enable the balance mode. \since 0.9.21 */
1295 +pa_operation *pa_ext_policy_set_balance (
1296 +        pa_context *c,
1297 +        double *balance,
1298 +        pa_context_success_cb_t cb,
1299 +        void *userdata);
1300 +
1301 +PA_C_DECL_END
1302 +
1303 +#endif
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"
1311  
1312 +/** For streams: logic role of this media. One of the strings "auto", "phone" */
1313 +#define PA_PROP_MEDIA_POLICY                "media.policy"
1314 +
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"
1317