]> code.delx.au - pulseaudio/blob - src/modules/bluetooth/module-bluetooth-proximity.c
dbus: remove filter functions only if they were actually set before
[pulseaudio] / src / modules / bluetooth / module-bluetooth-proximity.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2005-2006 Lennart Poettering
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34
35 #include <pulse/xmalloc.h>
36 #include <pulsecore/module.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/namereg.h>
39 #include <pulsecore/sink.h>
40 #include <pulsecore/modargs.h>
41 #include <pulsecore/macro.h>
42 #include <pulsecore/core-util.h>
43 #include <pulsecore/core-error.h>
44 #include <pulsecore/start-child.h>
45 #include <pulsecore/dbus-shared.h>
46
47 #include "module-bluetooth-proximity-symdef.h"
48
49 PA_MODULE_AUTHOR("Lennart Poettering");
50 PA_MODULE_DESCRIPTION("Bluetooth Proximity Volume Control");
51 PA_MODULE_VERSION(PACKAGE_VERSION);
52 PA_MODULE_LOAD_ONCE(TRUE);
53 PA_MODULE_USAGE(
54 "sink=<sink name> "
55 "hci=<hci device> "
56 );
57
58 #define DEFAULT_HCI "hci0"
59
60 static const char* const valid_modargs[] = {
61 "sink",
62 "rssi",
63 "hci",
64 NULL,
65 };
66
67 struct bonding {
68 struct userdata *userdata;
69 char address[18];
70
71 pid_t pid;
72 int fd;
73
74 pa_io_event *io_event;
75
76 enum {
77 UNKNOWN,
78 FOUND,
79 NOT_FOUND
80 } state;
81 };
82
83 struct userdata {
84 pa_module *module;
85 pa_dbus_connection *dbus_connection;
86
87 char *sink_name;
88 char *hci, *hci_path;
89
90 pa_hashmap *bondings;
91
92 unsigned n_found;
93 unsigned n_unknown;
94
95 pa_bool_t muted:1;
96 pa_bool_t filter_added:1;
97 };
98
99 static void update_volume(struct userdata *u) {
100 pa_assert(u);
101
102 if (u->muted && u->n_found > 0) {
103 pa_sink *s;
104
105 u->muted = FALSE;
106
107 if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
108 pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
109 return;
110 }
111
112 pa_log_info("Found %u BT devices, unmuting.", u->n_found);
113 pa_sink_set_mute(s, FALSE, FALSE);
114
115 } else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
116 pa_sink *s;
117
118 u->muted = TRUE;
119
120 if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
121 pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
122 return;
123 }
124
125 pa_log_info("No BT devices found, muting.");
126 pa_sink_set_mute(s, TRUE, FALSE);
127
128 } else
129 pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);
130 }
131
132 static void bonding_free(struct bonding *b) {
133 pa_assert(b);
134
135 if (b->state == FOUND)
136 pa_assert_se(b->userdata->n_found-- >= 1);
137
138 if (b->state == UNKNOWN)
139 pa_assert_se(b->userdata->n_unknown-- >= 1);
140
141 if (b->pid != (pid_t) -1) {
142 kill(b->pid, SIGTERM);
143 waitpid(b->pid, NULL, 0);
144 }
145
146 if (b->fd >= 0)
147 pa_close(b->fd);
148
149 if (b->io_event)
150 b->userdata->module->core->mainloop->io_free(b->io_event);
151
152 pa_xfree(b);
153 }
154
155 static void io_event_cb(
156 pa_mainloop_api*a,
157 pa_io_event* e,
158 int fd,
159 pa_io_event_flags_t events,
160 void *userdata) {
161
162 struct bonding *b = userdata;
163 char x;
164 ssize_t r;
165
166 pa_assert(b);
167
168 if ((r = read(fd, &x, 1)) <= 0) {
169 pa_log_warn("Child watching '%s' died abnormally: %s", b->address, r == 0 ? "EOF" : pa_cstrerror(errno));
170
171 pa_assert_se(pa_hashmap_remove(b->userdata->bondings, b->address) == b);
172 bonding_free(b);
173 return;
174 }
175
176 pa_assert_se(r == 1);
177
178 if (b->state == UNKNOWN)
179 pa_assert_se(b->userdata->n_unknown-- >= 1);
180
181 if (x == '+') {
182 pa_assert(b->state == UNKNOWN || b->state == NOT_FOUND);
183
184 b->state = FOUND;
185 b->userdata->n_found++;
186
187 pa_log_info("Device '%s' is alive.", b->address);
188
189 } else {
190 pa_assert(x == '-');
191 pa_assert(b->state == UNKNOWN || b->state == FOUND);
192
193 if (b->state == FOUND)
194 b->userdata->n_found--;
195
196 b->state = NOT_FOUND;
197
198 pa_log_info("Device '%s' is dead.", b->address);
199 }
200
201 update_volume(b->userdata);
202 }
203
204 static struct bonding* bonding_new(struct userdata *u, const char *a) {
205 struct bonding *b = NULL;
206 DBusMessage *m = NULL, *r = NULL;
207 DBusError e;
208 const char *class;
209
210 pa_assert(u);
211 pa_assert(a);
212
213 pa_return_val_if_fail(strlen(a) == 17, NULL);
214 pa_return_val_if_fail(!pa_hashmap_get(u->bondings, a), NULL);
215
216 dbus_error_init(&e);
217
218 pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "GetRemoteMajorClass"));
219 pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID));
220 r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), m, -1, &e);
221
222 if (!r) {
223 pa_log("org.bluez.Adapter.GetRemoteMajorClass(%s) failed: %s", a, e.message);
224 goto fail;
225 }
226
227 if (!(dbus_message_get_args(r, &e, DBUS_TYPE_STRING, &class, DBUS_TYPE_INVALID))) {
228 pa_log("Malformed org.bluez.Adapter.GetRemoteMajorClass signal: %s", e.message);
229 goto fail;
230 }
231
232 if (strcmp(class, "phone")) {
233 pa_log_info("Found device '%s' of class '%s', ignoring.", a, class);
234 goto fail;
235 }
236
237 b = pa_xnew(struct bonding, 1);
238 b->userdata = u;
239 pa_strlcpy(b->address, a, sizeof(b->address));
240 b->pid = (pid_t) -1;
241 b->fd = -1;
242 b->io_event = NULL;
243 b->state = UNKNOWN;
244 u->n_unknown ++;
245
246 pa_log_info("Watching device '%s' of class '%s'.", b->address, class);
247
248 if ((b->fd = pa_start_child_for_read(PA_BT_PROXIMITY_HELPER, a, &b->pid)) < 0) {
249 pa_log("Failed to start helper tool.");
250 goto fail;
251 }
252
253 b->io_event = u->module->core->mainloop->io_new(
254 u->module->core->mainloop,
255 b->fd,
256 PA_IO_EVENT_INPUT,
257 io_event_cb,
258 b);
259
260 dbus_message_unref(m);
261 dbus_message_unref(r);
262
263 pa_hashmap_put(u->bondings, b->address, b);
264
265 return b;
266
267 fail:
268 if (m)
269 dbus_message_unref(m);
270 if (r)
271 dbus_message_unref(r);
272
273 if (b)
274 bonding_free(b);
275
276 dbus_error_free(&e);
277 return NULL;
278 }
279
280 static void bonding_remove(struct userdata *u, const char *a) {
281 struct bonding *b;
282 pa_assert(u);
283
284 pa_return_if_fail((b = pa_hashmap_remove(u->bondings, a)));
285
286 pa_log_info("No longer watching device '%s'", b->address);
287 bonding_free(b);
288 }
289
290 static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *m, void *userdata) {
291 struct userdata *u = userdata;
292 DBusError e;
293
294 dbus_error_init(&e);
295
296 if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingCreated")) {
297 const char *a;
298
299 if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
300 pa_log("Malformed org.bluez.Adapter.BondingCreated signal: %s", e.message);
301 goto finish;
302 }
303
304 bonding_new(u, a);
305
306 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
307
308 } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingRemoved")) {
309
310 const char *a;
311
312 if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
313 pa_log("Malformed org.bluez.Adapter.BondingRemoved signal: %s", e.message);
314 goto finish;
315 }
316
317 bonding_remove(u, a);
318
319 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
320 }
321
322 finish:
323
324 dbus_error_free(&e);
325
326 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
327 }
328
329 static int add_matches(struct userdata *u, pa_bool_t add) {
330 char *filter1, *filter2;
331 DBusError e;
332 int r = -1;
333
334 pa_assert(u);
335 dbus_error_init(&e);
336
337 filter1 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingCreated',path='%s'", u->hci_path);
338 filter2 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingRemoved',path='%s'", u->hci_path);
339
340 if (add) {
341 dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
342
343 if (dbus_error_is_set(&e)) {
344 pa_log("dbus_bus_add_match(%s) failed: %s", filter1, e.message);
345 goto finish;
346 }
347 } else
348 dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
349
350
351 if (add) {
352 dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
353
354 if (dbus_error_is_set(&e)) {
355 pa_log("dbus_bus_add_match(%s) failed: %s", filter2, e.message);
356 dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
357 goto finish;
358 }
359 } else
360 dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
361
362 if (add) {
363 pa_assert_se(dbus_connection_add_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u, NULL));
364 u->filter_added = TRUE;
365 } else if (u->filter_added)
366 dbus_connection_remove_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u);
367
368 r = 0;
369
370 finish:
371 pa_xfree(filter1);
372 pa_xfree(filter2);
373 dbus_error_free(&e);
374
375 return r;
376 }
377
378 int pa__init(pa_module*m) {
379 pa_modargs *ma = NULL;
380 struct userdata *u;
381 DBusError e;
382 DBusMessage *msg = NULL, *r = NULL;
383 DBusMessageIter iter, sub;
384
385 pa_assert(m);
386 dbus_error_init(&e);
387
388 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
389 pa_log("Failed to parse module arguments");
390 goto fail;
391 }
392
393 m->userdata = u = pa_xnew0(struct userdata, 1);
394 u->module = m;
395 u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
396 u->hci = pa_xstrdup(pa_modargs_get_value(ma, "hci", DEFAULT_HCI));
397 u->hci_path = pa_sprintf_malloc("/org/bluez/%s", u->hci);
398 u->bondings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
399
400 if (!(u->dbus_connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &e))) {
401 pa_log("Failed to get D-Bus connection: %s", e.message);
402 goto fail;
403 }
404
405 if (add_matches(u, TRUE) < 0)
406 goto fail;
407
408 pa_assert_se(msg = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "ListBondings"));
409
410 if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), msg, -1, &e))) {
411 pa_log("org.bluez.Adapter.ListBondings failed: %s", e.message);
412 goto fail;
413 }
414
415 dbus_message_iter_init(r, &iter);
416
417 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
418 pa_log("Malformed reply to org.bluez.Adapter.ListBondings.");
419 goto fail;
420 }
421
422 dbus_message_iter_recurse(&iter, &sub);
423
424 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
425 const char *a = NULL;
426
427 dbus_message_iter_get_basic(&sub, &a);
428 bonding_new(u, a);
429
430 dbus_message_iter_next(&sub);
431 }
432
433 dbus_message_unref(r);
434 dbus_message_unref(msg);
435
436 pa_modargs_free(ma);
437
438 if (pa_hashmap_size(u->bondings) == 0)
439 pa_log_warn("Warning: no phone device bonded.");
440
441 update_volume(u);
442
443 return 0;
444
445 fail:
446
447 if (ma)
448 pa_modargs_free(ma);
449
450 pa__done(m);
451
452 dbus_error_free(&e);
453
454 if (msg)
455 dbus_message_unref(msg);
456
457 if (r)
458 dbus_message_unref(r);
459
460 return -1;
461 }
462
463 void pa__done(pa_module*m) {
464 struct userdata *u;
465 pa_assert(m);
466
467 if (!(u = m->userdata))
468 return;
469
470 if (u->bondings) {
471 struct bonding *b;
472
473 while ((b = pa_hashmap_steal_first(u->bondings)))
474 bonding_free(b);
475
476 pa_hashmap_free(u->bondings, NULL, NULL);
477 }
478
479 if (u->dbus_connection) {
480 add_matches(u, FALSE);
481 pa_dbus_connection_unref(u->dbus_connection);
482 }
483
484 pa_xfree(u->sink_name);
485 pa_xfree(u->hci_path);
486 pa_xfree(u->hci);
487 pa_xfree(u);
488 }