5 #include <pulsecore/core.h>
6 #include <pulsecore/module.h>
7 #include <pulsecore/modargs.h>
8 #include <pulsecore/core-rtclock.h>
9 #include <pulsecore/core-util.h>
10 #include <pulsecore/log.h>
14 #include <pulsecore/log.h>
15 #include <pulsecore/core-subscribe.h>
16 #include <pulsecore/sink-input.h>
17 #include <pulsecore/source-output.h>
18 #include <pulsecore/namereg.h>
19 #include <pulsecore/core-error.h>
21 #include <pulsecore/protocol-native.h>
22 #include <pulsecore/pstream-util.h>
23 #include <vconf.h> // for mono
25 #include "module-policy-symdef.h"
27 PA_MODULE_AUTHOR("Seungbae Shin");
28 PA_MODULE_DESCRIPTION("Media Policy module");
29 PA_MODULE_VERSION(PACKAGE_VERSION);
30 PA_MODULE_LOAD_ONCE(TRUE);
32 "on_hotplug=<When new device becomes available, recheck streams?> ");
34 static const char* const valid_modargs[] = {
43 pa_hook_slot *sink_input_new_hook_slot,*sink_put_hook_slot;
45 pa_hook_slot *sink_input_unlink_slot,*sink_unlink_slot;
46 pa_hook_slot *sink_input_unlink_post_slot, *sink_unlink_post_slot;
47 pa_hook_slot *sink_input_move_start_slot,*sink_input_move_finish_slot;
48 pa_subscription *subscription;
50 pa_bool_t on_hotplug:1;
54 pa_module* module_mono_bt;
55 pa_module* module_combined;
56 pa_module* module_mono_combined;
57 pa_native_protocol *protocol;
66 #define SINK_ALSA "alsa_output.0.analog-stereo"
67 #define SINK_MONO_ALSA "mono_alsa"
68 #define SINK_MONO_BT "mono_bt"
69 #define SINK_COMBINED "combined"
70 #define SINK_MONO_COMBINED "mono_combined"
71 #define POLICY_AUTO "auto"
72 #define POLICY_PHONE "phone"
73 #define POLICY_ALL "all"
74 #define BLUEZ_API "bluez"
75 #define MONO_KEY "db/setting/accessibility/mono_audio"
77 /* check if this sink is bluez */
78 static pa_bool_t policy_is_bluez (pa_sink* sink)
80 const char* api_name = NULL;
83 pa_log_warn ("input param sink is null");
87 api_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_API);
89 if (pa_streq (api_name, BLUEZ_API)) {
91 pa_log_debug("[POLICY][%s] [%s] exists and it is [%s]...TRUE !!", __func__, PA_PROP_DEVICE_API, api_name);
96 pa_log_debug("[POLICY][%s] [%s] exists, but not bluez...FALSE !!", __func__, PA_PROP_DEVICE_API);
101 pa_log_debug("[POLICY][%s] No [%s] exists...FALSE!!", __func__, PA_PROP_DEVICE_API);
108 /* Get sink by name */
109 static pa_sink* policy_get_sink_by_name (pa_core *c, const char* sink_name)
114 if (c == NULL || sink_name == NULL) {
115 pa_log_warn ("input param is null");
119 PA_IDXSET_FOREACH(s, c->sinks, idx) {
120 if (pa_streq (s->name, sink_name)) {
121 pa_log_debug ("[POLICY][%s] return [%p] for [%s]\n", __func__, s, sink_name);
128 /* Get bt sink if available */
129 static pa_sink* policy_get_bt_sink (pa_core *c)
135 pa_log_warn ("input param is null");
139 PA_IDXSET_FOREACH(s, c->sinks, idx) {
140 if (policy_is_bluez (s)) {
141 pa_log_debug ("[POLICY][%s] return [%p] for [%s]\n", __func__, s, s->name);
148 /* Select sink for given condition */
149 static pa_sink* policy_select_proper_sink (pa_core *c, const char* policy, int is_mono)
151 pa_sink* sink = NULL;
152 pa_sink* bt_sink = NULL;
155 if (c == NULL || policy == NULL) {
156 pa_log_warn ("input param is null");
162 bt_sink = policy_get_bt_sink(c);
163 def = pa_namereg_get_default_sink(c);
164 pa_log_debug ("[POLICY][%s] policy[%s], is_mono[%d], current default[%s], bt sink[%s]\n",
165 __func__, policy, is_mono, def->name, (bt_sink)? bt_sink->name:"null");
168 if (pa_streq(policy, POLICY_ALL)) {
171 sink = policy_get_sink_by_name(c, (is_mono)? SINK_MONO_COMBINED : SINK_COMBINED);
173 sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA);
176 } else if (pa_streq(policy, POLICY_PHONE)) {
178 sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA);
181 if (pa_streq (def->name, SINK_ALSA)) {
182 sink = (is_mono)? policy_get_sink_by_name (c, SINK_MONO_ALSA) : def;
184 sink = (is_mono)? policy_get_sink_by_name (c, SINK_MONO_BT) : def;
188 pa_log_debug ("[POLICY][%s] selected sink : [%s]\n", __func__, (sink)? sink->name : "null");
192 static pa_bool_t policy_is_filter (pa_sink_input* si)
194 const char* role = NULL;
197 pa_log_warn ("input param sink-input is null");
201 if ((role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) {
203 pa_log_debug("[POLICY][%s] Role of sink input [%d] = %s", __func__, si->index, role);
205 if (pa_streq(role, "filter")) {
207 pa_log_debug("[POLICY] no need to change of sink for %s", role);
218 #define EXT_VERSION 1
220 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
221 struct userdata *u = NULL;
223 pa_tagstruct *reply = NULL;
225 pa_sink_input *si = NULL;
227 pa_sink* sink_to_move = NULL;
236 if (pa_tagstruct_getu32(t, &command) < 0)
239 reply = pa_tagstruct_new(NULL, 0);
240 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
241 pa_tagstruct_putu32(reply, tag);
244 case SUBCOMMAND_TEST: {
245 if (!pa_tagstruct_eof(t))
248 pa_tagstruct_putu32(reply, EXT_VERSION);
252 case SUBCOMMAND_MONO: {
256 if (pa_tagstruct_get_boolean(t, &enable) < 0)
259 pa_log_debug ("[POLICY][%s] new mono value = %d\n", __func__, enable);
260 if (enable == u->is_mono) {
261 pa_log_debug ("[POLICY][%s] No changes in mono value = %d", __func__, u->is_mono);
267 /* Move current sink-input to proper mono sink */
268 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
269 const char *policy = NULL;
271 /* Skip this if it is already in the process of being moved
276 /* It might happen that a stream and a sink are set up at the
277 same time, in which case we want to make sure we don't
278 interfere with that */
279 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
282 /* Get role (if role is filter, skip it) */
283 if (policy_is_filter(si))
286 /* Check policy, if no policy exists, treat as AUTO */
287 if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) {
288 pa_log_debug("[POLICY] set policy of sink-input[%d] from [%s] to [auto]", si->index, "null");
289 policy = POLICY_AUTO;
291 pa_log_debug("[POLICY] Policy of sink input [%d] = %s", si->index, policy);
293 /* Select sink to move and move to it */
294 sink_to_move = policy_select_proper_sink (u->core, policy, u->is_mono);
296 pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
297 pa_sink_input_move_to(si, sink_to_move, FALSE);
299 pa_log_debug("[POLICY][%s] Can't move sink-input....", __func__);
309 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
315 pa_tagstruct_free(reply);
320 /* Called when new sink-input is creating */
321 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u)
323 const char *policy = NULL;
329 if (!new_data->proplist) {
330 pa_log_debug("[POLICY] New stream lacks property data.");
334 /* If sink-input has already sink, skip */
335 if (new_data->sink) {
336 /* sink-input with filter role will be also here because sink is already set */
338 pa_log_debug("[POLICY] Not setting device for stream [%s], because already set.",
339 pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
344 /* If no policy exists, skip */
345 if (!(policy = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_POLICY))) {
346 pa_log_debug("[POLICY][%s] Not setting device for stream [%s], because it lacks policy.",
347 __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
350 pa_log_debug("[POLICY][%s] Policy for stream [%s] = [%s]",
351 __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)), policy);
353 /* Set proper sink to sink-input */
354 new_data->save_sink = FALSE;
355 new_data->sink = policy_select_proper_sink (c, policy, u->is_mono);
356 pa_log_debug("[POLICY][%s] set sink of sink-input to [%s]", __func__, (new_data->sink)? new_data->sink->name : "null");
361 /* Called when new sink is added while sink-input is existing */
362 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u)
365 pa_sink *sink_to_move;
372 pa_assert(u->on_hotplug);
374 /* If connected sink is BLUETOOTH, set as default */
375 /* we are checking with device.api property */
376 if (policy_is_bluez(sink)) {
377 pa_log_debug("[POLICY][%s] set default sink to sink[%s][%d]", __func__, sink->name, sink->index);
378 pa_namereg_set_default_sink (c,sink);
380 pa_log_debug("[POLICY][%s] this sink [%s][%d] is not a bluez....return", __func__, sink->name, sink->index);
384 /* Load mono_bt sink */
385 args = pa_sprintf_malloc("sink_name=%s master=%s channels=1", SINK_MONO_BT, sink->name);
386 u->module_mono_bt = pa_module_load(u->module->core, "module-remap-sink", args);
389 /* load combine sink */
390 args = pa_sprintf_malloc("sink_name=%s slaves=\"%s,%s\"", SINK_COMBINED, sink->name, SINK_ALSA);
391 u->module_combined = pa_module_load(u->module->core, "module-combine", args);
394 /* load mono_combine sink */
395 args = pa_sprintf_malloc("sink_name=%s master=%s channels=1", SINK_MONO_COMBINED, SINK_COMBINED);
396 u->module_mono_combined = pa_module_load(u->module->core, "module-remap-sink", args);
400 /* Iterate each sink inputs to decide whether we should move to new sink */
401 PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
402 const char *policy = NULL;
404 if (si->sink == sink)
407 /* Skip this if it is already in the process of being moved
412 /* It might happen that a stream and a sink are set up at the
413 same time, in which case we want to make sure we don't
414 interfere with that */
415 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
418 /* Get role (if role is filter, skip it) */
419 if (policy_is_filter(si))
423 if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) {
424 /* No policy exists, this means auto */
425 pa_log_debug("[POLICY][%s] set policy of sink-input[%d] from [%s] to [auto]", __func__, si->index, "null");
426 policy = POLICY_AUTO;
429 sink_to_move = policy_select_proper_sink (c, policy, u->is_mono);
431 pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
432 pa_sink_input_move_to(si, sink_to_move, FALSE);
434 pa_log_debug("[POLICY][%s] Can't move sink-input....",__func__);
441 static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata)
443 struct userdata *u = userdata;
447 pa_sink *sink_to_move = NULL;
450 pa_log_debug("[POLICY][%s] subscribe_cb() t=[0x%x], idx=[%d]", __func__, t, idx);
452 /* We only handle server changes */
453 if (t == (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE)) {
455 def = pa_namereg_get_default_sink(c);
456 pa_log_debug("[POLICY][%s] trying to move stream to current default sink = [%s]", __func__, def->name);
458 /* Iterate each sink inputs to decide whether we should move to new DEFAULT sink */
459 PA_IDXSET_FOREACH(si, c->sink_inputs, idx2) {
460 const char *policy = NULL;
465 /* Get role (if role is filter, skip it) */
466 if (policy_is_filter(si))
470 if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) {
471 /* No policy exists, this means auto */
472 pa_log_debug("[POLICY][%s] set policy of sink-input[%d] from [%s] to [auto]", __func__, si->index, "null");
473 policy = POLICY_AUTO;
476 sink_to_move = policy_select_proper_sink (c, policy, u->is_mono);
478 /* Move sink-input to new DEFAULT sink */
479 pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
480 pa_sink_input_move_to(si, sink_to_move, FALSE);
486 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
487 struct userdata *u = userdata;
489 pa_sink *sink_to_move;
496 /* There's no point in doing anything if the core is shut down anyway */
497 if (c->state == PA_CORE_SHUTDOWN)
500 /* if unloading sink is not bt, just return */
501 if (!policy_is_bluez (sink)) {
502 pa_log_debug("[POLICY][%s] sink[%s][%d] unlinked but not a bluez....return\n", __func__, sink->name, sink->index);
506 pa_log_debug ("[POLICY][%s] SINK unlinked ================================ sink [%s][%d], bt_off_idx was [%d]",
507 __func__, sink->name, sink->index,u->bt_off_idx);
509 u->bt_off_idx = sink->index;
510 pa_log_debug ("[POLICY][%s] bt_off_idx is set to [%d]", __func__, u->bt_off_idx);
512 /* BT sink is unloading, move sink-input to proper sink */
513 PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
518 /* Get role (if role is filter, skip it) */
519 if (policy_is_filter(si))
522 /* Find who were using bt sink or bt related sink and move them to proper sink (alsa/mono_alsa) */
523 if (pa_streq (si->sink->name, SINK_MONO_BT) ||
524 pa_streq (si->sink->name, SINK_MONO_COMBINED) ||
525 pa_streq (si->sink->name, SINK_COMBINED) ||
526 policy_is_bluez (si->sink)) {
528 /* Move sink-input to proper sink : only alsa related sink is available now */
529 sink_to_move = policy_get_sink_by_name (c, (u->is_mono)? SINK_MONO_ALSA : SINK_ALSA);
530 pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name);
531 pa_sink_input_move_to(si, sink_to_move, FALSE);
535 pa_log_debug ("[POLICY][%s] unload sink in dependencies", __func__);
537 /* Unload mono_combine sink */
538 if (u->module_mono_combined) {
539 pa_module_unload(u->module->core, u->module_mono_combined, TRUE);
540 u->module_mono_combined = NULL;
543 /* Unload combine sink */
544 if (u->module_combined) {
545 pa_module_unload(u->module->core, u->module_combined, TRUE);
546 u->module_combined = NULL;
549 /* Unload mono_bt sink */
550 if (u->module_mono_bt) {
551 pa_module_unload(u->module->core, u->module_mono_bt, TRUE);
552 u->module_mono_bt = NULL;
558 static pa_hook_result_t sink_unlink_post_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
559 struct userdata *u = userdata;
565 pa_log_debug("[POLICY][%s] SINK unlinked POST ================================ sink [%s][%d]", __func__, sink->name, sink->index);
567 /* There's no point in doing anything if the core is shut down anyway */
568 if (c->state == PA_CORE_SHUTDOWN)
571 /* if unloading sink is not bt, just return */
572 if (!policy_is_bluez (sink)) {
573 pa_log_debug("[POLICY][%s] not a bluez....return\n", __func__);
578 pa_log_debug ("[POLICY][%s] bt_off_idx is cleared to [%d]", __func__, u->bt_off_idx);
583 static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
584 pa_core_assert_ref(core);
585 pa_sink_input_assert_ref(i);
587 /* There's no point in doing anything if the core is shut down anyway */
588 if (core->state == PA_CORE_SHUTDOWN)
591 pa_log_debug ("[POLICY][%s] sink_input_move_start_cb -------------------------------------- sink-input [%d] was sink [%s][%d] : Trying to mute!!!",
592 __func__, i->index, i->sink->name, i->sink->index);
593 pa_sink_input_set_mute(i, TRUE, FALSE);
598 static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
599 pa_core_assert_ref(core);
600 pa_sink_input_assert_ref(i);
602 /* There's no point in doing anything if the core is shut down anyway */
603 if (core->state == PA_CORE_SHUTDOWN)
606 pa_log_debug("[POLICY][%s] sink_input_move_finish_cb -------------------------------------- sink-input [%d], sink [%s][%d], bt_off_idx [%d] : %s",
607 __func__, i->index, i->sink->name, i->sink->index, u->bt_off_idx,
608 (u->bt_off_idx == -1)? "Trying to un-mute!!!!" : "skip un-mute...");
610 /* If sink input move is caused by bt sink unlink, then skip un-mute operation */
611 if (u->bt_off_idx == -1) {
612 pa_sink_input_set_mute(i, FALSE, FALSE);
618 int pa__init(pa_module *m)
620 pa_modargs *ma = NULL;
622 pa_bool_t on_hotplug = TRUE, on_rescue = TRUE;
626 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
627 pa_log("Failed to parse module arguments");
631 if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
632 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
633 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
637 m->userdata = u = pa_xnew0(struct userdata, 1);
640 u->on_hotplug = on_hotplug;
643 /* A little bit later than module-stream-restore */
644 u->sink_input_new_hook_slot =
645 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);
648 /* A little bit later than module-stream-restore */
649 u->sink_put_hook_slot =
650 pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_put_hook_callback, u);
653 /* sink unlink comes before sink-input unlink */
654 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);
655 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);
657 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);
658 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);
660 u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
663 u->bt_off_idx = -1; /* initial bt off sink index */
665 u->module_mono_bt = NULL;
666 u->module_combined = NULL;
667 u->module_mono_combined = NULL;
669 u->protocol = pa_native_protocol_get(m->core);
670 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
672 /* Get mono key value for init */
673 vconf_get_bool(MONO_KEY, &u->is_mono);
675 pa_log_info("policy module is loaded\n");
685 void pa__done(pa_module *m)
691 if (!(u = m->userdata))
694 if (u->sink_input_new_hook_slot)
695 pa_hook_slot_free(u->sink_input_new_hook_slot);
696 if (u->sink_put_hook_slot)
697 pa_hook_slot_free(u->sink_put_hook_slot);
699 pa_subscription_free(u->subscription);
701 pa_native_protocol_remove_ext(u->protocol, m);
702 pa_native_protocol_unref(u->protocol);
708 pa_log_info("policy module is unloaded\n");