Git init
[framework/multimedia/pulseaudio.git] / 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     "hci",
63     NULL,
64 };
65
66 struct bonding {
67     struct userdata *userdata;
68     char address[18];
69
70     pid_t pid;
71     int fd;
72
73     pa_io_event *io_event;
74
75     enum {
76         UNKNOWN,
77         FOUND,
78         NOT_FOUND
79     } state;
80 };
81
82 struct userdata {
83     pa_module *module;
84     pa_dbus_connection *dbus_connection;
85
86     char *sink_name;
87     char *hci, *hci_path;
88
89     pa_hashmap *bondings;
90
91     unsigned n_found;
92     unsigned n_unknown;
93
94     pa_bool_t muted:1;
95     pa_bool_t filter_added:1;
96 };
97
98 static void update_volume(struct userdata *u) {
99     pa_assert(u);
100
101     if (u->muted && u->n_found > 0) {
102         pa_sink *s;
103
104         u->muted = FALSE;
105
106         if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
107             pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
108             return;
109         }
110
111         pa_log_info("Found %u BT devices, unmuting.", u->n_found);
112         pa_sink_set_mute(s, FALSE, FALSE);
113
114     } else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
115         pa_sink *s;
116
117         u->muted = TRUE;
118
119         if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
120             pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
121             return;
122         }
123
124         pa_log_info("No BT devices found, muting.");
125         pa_sink_set_mute(s, TRUE, FALSE);
126
127     } else
128         pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);
129 }
130
131 static void bonding_free(struct bonding *b) {
132     pa_assert(b);
133
134     if (b->state == FOUND)
135         pa_assert_se(b->userdata->n_found-- >= 1);
136
137     if (b->state == UNKNOWN)
138         pa_assert_se(b->userdata->n_unknown-- >= 1);
139
140     if (b->pid != (pid_t) -1) {
141         kill(b->pid, SIGTERM);
142         waitpid(b->pid, NULL, 0);
143     }
144
145     if (b->fd >= 0)
146         pa_close(b->fd);
147
148     if (b->io_event)
149         b->userdata->module->core->mainloop->io_free(b->io_event);
150
151     pa_xfree(b);
152 }
153
154 static void io_event_cb(
155         pa_mainloop_api*a,
156         pa_io_event* e,
157         int fd,
158         pa_io_event_flags_t events,
159         void *userdata) {
160
161     struct bonding *b = userdata;
162     char x;
163     ssize_t r;
164
165     pa_assert(b);
166
167     if ((r = read(fd, &x, 1)) <= 0) {
168         pa_log_warn("Child watching '%s' died abnormally: %s", b->address, r == 0 ? "EOF" : pa_cstrerror(errno));
169
170         pa_assert_se(pa_hashmap_remove(b->userdata->bondings, b->address) == b);
171         bonding_free(b);
172         return;
173     }
174
175     pa_assert_se(r == 1);
176
177     if (b->state == UNKNOWN)
178         pa_assert_se(b->userdata->n_unknown-- >= 1);
179
180     if (x == '+') {
181         pa_assert(b->state == UNKNOWN || b->state == NOT_FOUND);
182
183         b->state = FOUND;
184         b->userdata->n_found++;
185
186         pa_log_info("Device '%s' is alive.", b->address);
187
188     } else {
189         pa_assert(x == '-');
190         pa_assert(b->state == UNKNOWN || b->state == FOUND);
191
192         if (b->state == FOUND)
193             b->userdata->n_found--;
194
195         b->state = NOT_FOUND;
196
197         pa_log_info("Device '%s' is dead.", b->address);
198     }
199
200     update_volume(b->userdata);
201 }
202
203 static struct bonding* bonding_new(struct userdata *u, const char *a) {
204     struct bonding *b = NULL;
205     DBusMessage *m = NULL, *r = NULL;
206     DBusError e;
207     const char *class;
208
209     pa_assert(u);
210     pa_assert(a);
211
212     pa_return_val_if_fail(strlen(a) == 17, NULL);
213     pa_return_val_if_fail(!pa_hashmap_get(u->bondings, a), NULL);
214
215     dbus_error_init(&e);
216
217     pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "GetRemoteMajorClass"));
218     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID));
219     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), m, -1, &e);
220
221     if (!r) {
222         pa_log("org.bluez.Adapter.GetRemoteMajorClass(%s) failed: %s", a, e.message);
223         goto fail;
224     }
225
226     if (!(dbus_message_get_args(r, &e, DBUS_TYPE_STRING, &class, DBUS_TYPE_INVALID))) {
227         pa_log("Malformed org.bluez.Adapter.GetRemoteMajorClass signal: %s", e.message);
228         goto fail;
229     }
230
231     if (strcmp(class, "phone")) {
232         pa_log_info("Found device '%s' of class '%s', ignoring.", a, class);
233         goto fail;
234     }
235
236     b = pa_xnew(struct bonding, 1);
237     b->userdata = u;
238     pa_strlcpy(b->address, a, sizeof(b->address));
239     b->pid = (pid_t) -1;
240     b->fd = -1;
241     b->io_event = NULL;
242     b->state = UNKNOWN;
243     u->n_unknown ++;
244
245     pa_log_info("Watching device '%s' of class '%s'.", b->address, class);
246
247     if ((b->fd = pa_start_child_for_read(PA_BT_PROXIMITY_HELPER, a, &b->pid)) < 0) {
248         pa_log("Failed to start helper tool.");
249         goto fail;
250     }
251
252     b->io_event = u->module->core->mainloop->io_new(
253             u->module->core->mainloop,
254             b->fd,
255             PA_IO_EVENT_INPUT,
256             io_event_cb,
257             b);
258
259     dbus_message_unref(m);
260     dbus_message_unref(r);
261
262     pa_hashmap_put(u->bondings, b->address, b);
263
264     return b;
265
266 fail:
267     if (m)
268         dbus_message_unref(m);
269     if (r)
270         dbus_message_unref(r);
271
272     if (b)
273         bonding_free(b);
274
275     dbus_error_free(&e);
276     return NULL;
277 }
278
279 static void bonding_remove(struct userdata *u, const char *a) {
280     struct bonding *b;
281     pa_assert(u);
282
283     pa_return_if_fail((b = pa_hashmap_remove(u->bondings, a)));
284
285     pa_log_info("No longer watching device '%s'", b->address);
286     bonding_free(b);
287 }
288
289 static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *m, void *userdata) {
290     struct userdata *u = userdata;
291     DBusError e;
292
293     dbus_error_init(&e);
294
295     if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingCreated")) {
296         const char *a;
297
298         if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
299             pa_log("Malformed org.bluez.Adapter.BondingCreated signal: %s", e.message);
300             goto finish;
301         }
302
303         bonding_new(u, a);
304
305         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
306
307     } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingRemoved")) {
308
309         const char *a;
310
311         if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
312             pa_log("Malformed org.bluez.Adapter.BondingRemoved signal: %s", e.message);
313             goto finish;
314         }
315
316         bonding_remove(u, a);
317
318         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
319     }
320
321 finish:
322
323     dbus_error_free(&e);
324
325     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
326 }
327
328 static int add_matches(struct userdata *u, pa_bool_t add) {
329     char *filter1, *filter2;
330     DBusError e;
331     int r = -1;
332
333     pa_assert(u);
334     dbus_error_init(&e);
335
336     filter1 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingCreated',path='%s'", u->hci_path);
337     filter2 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingRemoved',path='%s'", u->hci_path);
338
339     if (add) {
340         dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
341
342         if (dbus_error_is_set(&e)) {
343             pa_log("dbus_bus_add_match(%s) failed: %s", filter1, e.message);
344             goto finish;
345         }
346     } else
347         dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
348
349
350     if (add) {
351         dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
352
353         if (dbus_error_is_set(&e)) {
354             pa_log("dbus_bus_add_match(%s) failed: %s", filter2, e.message);
355             dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
356             goto finish;
357         }
358     } else
359         dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
360
361     if (add) {
362         pa_assert_se(dbus_connection_add_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u, NULL));
363         u->filter_added = TRUE;
364     } else if (u->filter_added)
365         dbus_connection_remove_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u);
366
367     r = 0;
368
369 finish:
370     pa_xfree(filter1);
371     pa_xfree(filter2);
372     dbus_error_free(&e);
373
374     return r;
375 }
376
377 int pa__init(pa_module*m) {
378     pa_modargs *ma = NULL;
379     struct userdata *u;
380     DBusError e;
381     DBusMessage *msg = NULL, *r = NULL;
382     DBusMessageIter iter, sub;
383
384     pa_assert(m);
385     dbus_error_init(&e);
386
387     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
388         pa_log("Failed to parse module arguments");
389         goto fail;
390     }
391
392     m->userdata = u = pa_xnew0(struct userdata, 1);
393     u->module = m;
394     u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
395     u->hci = pa_xstrdup(pa_modargs_get_value(ma, "hci", DEFAULT_HCI));
396     u->hci_path = pa_sprintf_malloc("/org/bluez/%s", u->hci);
397     u->bondings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
398
399     if (!(u->dbus_connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &e))) {
400         pa_log("Failed to get D-Bus connection: %s", e.message);
401         goto fail;
402     }
403
404     if (add_matches(u, TRUE) < 0)
405         goto fail;
406
407     pa_assert_se(msg = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "ListBondings"));
408
409     if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), msg, -1, &e))) {
410         pa_log("org.bluez.Adapter.ListBondings failed: %s", e.message);
411         goto fail;
412     }
413
414     dbus_message_iter_init(r, &iter);
415
416     if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
417         pa_log("Malformed reply to org.bluez.Adapter.ListBondings.");
418         goto fail;
419     }
420
421     dbus_message_iter_recurse(&iter, &sub);
422
423     while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
424         const char *a = NULL;
425
426         dbus_message_iter_get_basic(&sub, &a);
427         bonding_new(u, a);
428
429         dbus_message_iter_next(&sub);
430     }
431
432     dbus_message_unref(r);
433     dbus_message_unref(msg);
434
435     pa_modargs_free(ma);
436
437     if (pa_hashmap_size(u->bondings) == 0)
438         pa_log_warn("Warning: no phone device bonded.");
439
440     update_volume(u);
441
442     return 0;
443
444 fail:
445
446     if (ma)
447         pa_modargs_free(ma);
448
449     pa__done(m);
450
451     dbus_error_free(&e);
452
453     if (msg)
454         dbus_message_unref(msg);
455
456     if (r)
457         dbus_message_unref(r);
458
459     return -1;
460 }
461
462 void pa__done(pa_module*m) {
463     struct userdata *u;
464     pa_assert(m);
465
466     if (!(u = m->userdata))
467         return;
468
469     if (u->bondings) {
470         struct bonding *b;
471
472         while ((b = pa_hashmap_steal_first(u->bondings)))
473             bonding_free(b);
474
475         pa_hashmap_free(u->bondings, NULL, NULL);
476     }
477
478     if (u->dbus_connection) {
479         add_matches(u, FALSE);
480         pa_dbus_connection_unref(u->dbus_connection);
481     }
482
483     pa_xfree(u->sink_name);
484     pa_xfree(u->hci_path);
485     pa_xfree(u->hci);
486     pa_xfree(u);
487 }