Git init
[framework/multimedia/pulseaudio.git] / src / modules / module-policy.c
1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4
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>
11 #include <stdbool.h>
12 #include <strings.h>
13
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>
20
21 #include <pulsecore/protocol-native.h>
22 #include <pulsecore/pstream-util.h>
23 #include <vconf.h> // for mono
24
25 #include "module-policy-symdef.h"
26
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);
31 PA_MODULE_USAGE(
32         "on_hotplug=<When new device becomes available, recheck streams?> ");
33
34 static const char* const valid_modargs[] = {
35     "on_hotplug",
36     NULL
37 };
38
39 struct userdata {
40     pa_core *core;
41     pa_module *module;
42
43     pa_hook_slot *sink_input_new_hook_slot,*sink_put_hook_slot;
44
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;
49
50     pa_bool_t on_hotplug:1;
51     int bt_off_idx;
52
53     int is_mono;
54     pa_module* module_mono_bt;
55     pa_module* module_combined;
56     pa_module* module_mono_combined;
57     pa_native_protocol *protocol;
58 };
59
60 enum {
61     SUBCOMMAND_TEST,
62     SUBCOMMAND_MONO,
63 };
64
65 /* DEFINEs */
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"
76
77 /* check if this sink is bluez */
78 static pa_bool_t policy_is_bluez (pa_sink* sink)
79 {
80         const char* api_name = NULL;
81
82         if (sink == NULL) {
83                 pa_log_warn ("input param sink is null");
84                 return FALSE;
85         }
86
87     api_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_API);
88         if (api_name) {
89                 if (pa_streq (api_name, BLUEZ_API)) {
90 #ifdef DEBUG_DETAIL
91                         pa_log_debug("[POLICY][%s] [%s] exists and it is [%s]...TRUE !!", __func__, PA_PROP_DEVICE_API, api_name);
92 #endif
93                         return TRUE;
94                 } else {
95 #ifdef DEBUG_DETAIL
96                         pa_log_debug("[POLICY][%s] [%s] exists, but not bluez...FALSE !!", __func__, PA_PROP_DEVICE_API);
97 #endif
98                 }
99         } else {
100 #ifdef DEBUG_DETAIL
101                 pa_log_debug("[POLICY][%s] No [%s] exists...FALSE!!", __func__, PA_PROP_DEVICE_API);
102 #endif
103         }
104
105         return FALSE;
106 }
107
108 /* Get sink by name */
109 static pa_sink* policy_get_sink_by_name (pa_core *c, const char* sink_name)
110 {
111     pa_sink *s = NULL;
112     uint32_t idx;
113
114     if (c == NULL || sink_name == NULL) {
115                 pa_log_warn ("input param is null");
116                 return NULL;
117     }
118
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);
122                         return s;
123                 }
124         }
125         return NULL;
126 }
127
128 /* Get bt sink if available */
129 static pa_sink* policy_get_bt_sink (pa_core *c)
130 {
131     pa_sink *s = NULL;
132     uint32_t idx;
133
134     if (c == NULL) {
135                 pa_log_warn ("input param is null");
136                 return NULL;
137     }
138
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);
142                         return s;
143                 }
144         }
145         return NULL;
146 }
147
148 /* Select sink for given condition */
149 static pa_sink* policy_select_proper_sink (pa_core *c, const char* policy, int is_mono)
150 {
151         pa_sink* sink = NULL;
152         pa_sink* bt_sink = NULL;
153         pa_sink* def = NULL;
154
155         if (c == NULL || policy == NULL) {
156                 pa_log_warn ("input param is null");
157                 return NULL;
158         }
159
160         pa_assert (c);
161
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");
166
167         /* Select sink to */
168         if (pa_streq(policy, POLICY_ALL)) {
169                 /* all */
170                 if (bt_sink) {
171                         sink = policy_get_sink_by_name(c, (is_mono)? SINK_MONO_COMBINED : SINK_COMBINED);
172                 } else {
173                         sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA);
174                 }
175
176         } else if (pa_streq(policy, POLICY_PHONE)) {
177                 /* phone */
178                 sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA);
179         } else {
180                 /* auto */
181                 if (pa_streq (def->name, SINK_ALSA)) {
182                         sink = (is_mono)? policy_get_sink_by_name (c, SINK_MONO_ALSA) : def;
183                 } else {
184                         sink = (is_mono)? policy_get_sink_by_name (c, SINK_MONO_BT) : def;
185                 }
186         }
187
188         pa_log_debug ("[POLICY][%s] selected sink : [%s]\n", __func__, (sink)? sink->name : "null");
189         return sink;
190 }
191
192 static pa_bool_t policy_is_filter (pa_sink_input* si)
193 {
194         const char* role = NULL;
195
196         if (si == NULL) {
197                 pa_log_warn ("input param sink-input is null");
198                 return FALSE;
199         }
200
201         if ((role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) {
202 #ifdef DEBUG_DETAIL
203                 pa_log_debug("[POLICY][%s] Role of sink input [%d] = %s", __func__, si->index, role);
204 #endif
205                 if (pa_streq(role, "filter")) {
206 #ifdef DEBUG_DETAIL
207                         pa_log_debug("[POLICY] no need to change of sink for %s", role);
208 #endif
209                         return TRUE;
210                 }
211         }
212
213         return FALSE;
214 }
215
216
217
218 #define EXT_VERSION 1
219
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;
222   uint32_t command;
223   pa_tagstruct *reply = NULL;
224
225   pa_sink_input *si = NULL;
226   uint32_t idx;
227   pa_sink* sink_to_move  = NULL;
228
229   pa_assert(p);
230   pa_assert(m);
231   pa_assert(c);
232   pa_assert(t);
233
234   u = m->userdata;
235
236   if (pa_tagstruct_getu32(t, &command) < 0)
237     goto fail;
238
239   reply = pa_tagstruct_new(NULL, 0);
240   pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
241   pa_tagstruct_putu32(reply, tag);
242
243   switch (command) {
244     case SUBCOMMAND_TEST: {
245                 if (!pa_tagstruct_eof(t))
246                         goto fail;
247
248                 pa_tagstruct_putu32(reply, EXT_VERSION);
249                 break;
250     }
251
252     case SUBCOMMAND_MONO: {
253
254         pa_bool_t enable;
255
256         if (pa_tagstruct_get_boolean(t, &enable) < 0)
257             goto fail;
258
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);
262                         break;
263         }
264
265         u->is_mono = enable;
266
267                 /* Move current sink-input to proper mono sink */
268                 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
269                         const char *policy = NULL;
270
271                         /* Skip this if it is already in the process of being moved
272                          * anyway */
273                         if (!si->sink)
274                                 continue;
275
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)))
280                                 continue;
281
282                         /* Get role (if role is filter, skip it) */
283                         if (policy_is_filter(si))
284                                 continue;
285
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;
290                         }
291                         pa_log_debug("[POLICY] Policy of sink input [%d] = %s", si->index, policy);
292
293                         /* Select sink to move and move to it */
294                         sink_to_move = policy_select_proper_sink (u->core, policy, u->is_mono);
295                         if (sink_to_move) {
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);
298                         } else {
299                                 pa_log_debug("[POLICY][%s] Can't move sink-input....", __func__);
300                         }
301                 }
302         break;
303     }
304
305     default:
306       goto fail;
307   }
308
309   pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
310   return 0;
311
312   fail:
313
314   if (reply)
315           pa_tagstruct_free(reply);
316
317   return -1;
318 }
319
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)
322 {
323     const char *policy = NULL;
324
325     pa_assert(c);
326     pa_assert(new_data);
327     pa_assert(u);
328
329     if (!new_data->proplist) {
330         pa_log_debug("[POLICY] New stream lacks property data.");
331         return PA_HOOK_OK;
332     }
333
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 */
337 #ifdef DEBUG_DETAIL
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)));
340 #endif
341         return PA_HOOK_OK;
342     }
343
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)));
348         return PA_HOOK_OK;
349     }
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);
352
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");
357
358     return PA_HOOK_OK;
359 }
360
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)
363 {
364     pa_sink_input *si;
365     pa_sink *sink_to_move;
366     uint32_t idx;
367     char *args = NULL;
368
369     pa_assert(c);
370     pa_assert(sink);
371     pa_assert(u);
372     pa_assert(u->on_hotplug);
373
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);
379         } else {
380                 pa_log_debug("[POLICY][%s] this sink [%s][%d] is not a bluez....return", __func__, sink->name, sink->index);
381                 return PA_HOOK_OK;
382         }
383
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);
387         pa_xfree(args);
388
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);
392         pa_xfree(args);
393
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);
397         pa_xfree(args);
398
399
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;
403
404         if (si->sink == sink)
405                 continue;
406
407         /* Skip this if it is already in the process of being moved
408          * anyway */
409         if (!si->sink)
410             continue;
411
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)))
416             continue;
417
418                 /* Get role (if role is filter, skip it) */
419         if (policy_is_filter(si))
420                 continue;
421
422                 /* Check policy */
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;
427                 }
428
429                 sink_to_move = policy_select_proper_sink (c, policy, u->is_mono);
430                 if (sink_to_move) {
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);
433                 } else {
434                         pa_log_debug("[POLICY][%s] Can't move sink-input....",__func__);
435                 }
436     }
437
438     return PA_HOOK_OK;
439 }
440
441 static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata)
442 {
443     struct userdata *u = userdata;
444     pa_sink *def;
445     pa_sink_input *si;
446     uint32_t idx2;
447     pa_sink *sink_to_move = NULL;
448     pa_assert(u);
449
450     pa_log_debug("[POLICY][%s] subscribe_cb() t=[0x%x], idx=[%d]", __func__, t, idx);
451
452     /* We only handle server changes */
453     if (t == (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE)) {
454
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);
457
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;
461
462                         if (!si->sink)
463                                 continue;
464
465                         /* Get role (if role is filter, skip it) */
466                         if (policy_is_filter(si))
467                                 continue;
468
469                         /* Get policy */
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;
474                         }
475
476                         sink_to_move = policy_select_proper_sink (c, policy, u->is_mono);
477                         if (sink_to_move) {
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);
481                         }
482         }
483     }
484 }
485
486 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
487     struct userdata *u = userdata;
488     uint32_t idx;
489     pa_sink *sink_to_move;
490     pa_sink_input       *si;
491
492     pa_assert(c);
493     pa_assert(sink);
494     pa_assert(u);
495
496      /* There's no point in doing anything if the core is shut down anyway */
497     if (c->state == PA_CORE_SHUTDOWN)
498         return PA_HOOK_OK;
499
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);
503                 return PA_HOOK_OK;
504         }
505
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);
508
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);
511
512         /* BT sink is unloading, move sink-input to proper sink */
513         PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
514
515                 if (!si->sink)
516                         continue;
517
518                 /* Get role (if role is filter, skip it) */
519                 if (policy_is_filter(si))
520                         continue;
521
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)) {
527
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);
532                 }
533         }
534
535         pa_log_debug ("[POLICY][%s] unload sink in dependencies", __func__);
536
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;
541     }
542
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;
547     }
548
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;
553         }
554
555     return PA_HOOK_OK;
556 }
557
558 static pa_hook_result_t sink_unlink_post_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
559     struct userdata *u = userdata;
560
561     pa_assert(c);
562     pa_assert(sink);
563     pa_assert(u);
564
565     pa_log_debug("[POLICY][%s] SINK unlinked POST ================================ sink [%s][%d]", __func__, sink->name, sink->index);
566
567      /* There's no point in doing anything if the core is shut down anyway */
568     if (c->state == PA_CORE_SHUTDOWN)
569         return PA_HOOK_OK;
570
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__);
574                 return PA_HOOK_OK;
575         }
576
577     u->bt_off_idx = -1;
578     pa_log_debug ("[POLICY][%s] bt_off_idx is cleared to [%d]", __func__, u->bt_off_idx);
579
580     return PA_HOOK_OK;
581 }
582
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);
586
587     /* There's no point in doing anything if the core is shut down anyway */
588    if (core->state == PA_CORE_SHUTDOWN)
589        return PA_HOOK_OK;
590
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);
594
595     return PA_HOOK_OK;
596 }
597
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);
601
602     /* There's no point in doing anything if the core is shut down anyway */
603    if (core->state == PA_CORE_SHUTDOWN)
604        return PA_HOOK_OK;
605
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...");
609
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);
613     }
614
615     return PA_HOOK_OK;
616 }
617
618 int pa__init(pa_module *m)
619 {
620         pa_modargs *ma = NULL;
621         struct userdata *u;
622         pa_bool_t on_hotplug = TRUE, on_rescue = TRUE;
623
624         pa_assert(m);
625
626         if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
627                 pa_log("Failed to parse module arguments");
628                 goto fail;
629         }
630
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");
634                 goto fail;
635         }
636
637         m->userdata = u = pa_xnew0(struct userdata, 1);
638         u->core = m->core;
639         u->module = m;
640         u->on_hotplug = on_hotplug;
641
642
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);
646
647         if (on_hotplug) {
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);
651         }
652
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);
656
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);
659
660         u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
661
662
663         u->bt_off_idx = -1;     /* initial bt off sink index */
664
665         u->module_mono_bt = NULL;
666         u->module_combined = NULL;
667         u->module_mono_combined = NULL;
668
669     u->protocol = pa_native_protocol_get(m->core);
670     pa_native_protocol_install_ext(u->protocol, m, extension_cb);
671
672     /* Get mono key value for init */
673         vconf_get_bool(MONO_KEY, &u->is_mono);
674
675         pa_log_info("policy module is loaded\n");
676
677         return 0;
678
679 fail:
680         pa__done(m);
681
682         return -1;
683 }
684
685 void pa__done(pa_module *m)
686 {
687     struct userdata* u;
688
689     pa_assert(m);
690
691     if (!(u = m->userdata))
692         return;
693
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);
698     if (u->subscription)
699         pa_subscription_free(u->subscription);
700     if (u->protocol) {
701         pa_native_protocol_remove_ext(u->protocol, m);
702         pa_native_protocol_unref(u->protocol);
703     }
704
705     pa_xfree(u);
706
707
708         pa_log_info("policy module is unloaded\n");
709 }