mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
add new fun module that automatically mutes your audio devices when you leave with your bluetooth phone, and unmutes when you come back
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2048 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
parent
e8092bede9
commit
5054f3623f
4 changed files with 763 additions and 1 deletions
36
configure.ac
36
configure.ac
|
|
@ -789,6 +789,36 @@ AC_SUBST(HAL_LIBS)
|
|||
AC_SUBST(HAVE_HAL)
|
||||
AM_CONDITIONAL([HAVE_HAL], [test "x$HAVE_HAL" = x1])
|
||||
|
||||
#### BlueZ support (optional) ####
|
||||
|
||||
AC_ARG_ENABLE([bluez],
|
||||
AC_HELP_STRING([--disable-bluez], [Disable optional BlueZ support]),
|
||||
[
|
||||
case "${enableval}" in
|
||||
yes) bluez=yes ;;
|
||||
no) bluez=no ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --disable-bluez) ;;
|
||||
esac
|
||||
],
|
||||
[bluez=auto])
|
||||
if test "x${bluez}" != xno ; then
|
||||
PKG_CHECK_MODULES(BLUEZ, [ bluez >= 3.0 ],
|
||||
HAVE_BLUEZ=1,
|
||||
[
|
||||
HAVE_BLUEZ=0
|
||||
if test "x$bluez" = xyes ; then
|
||||
AC_MSG_ERROR([*** BLUEZ support not found])
|
||||
fi
|
||||
])
|
||||
else
|
||||
HAVE_BLUEZ=0
|
||||
fi
|
||||
|
||||
AC_SUBST(BLUEZ_CFLAGS)
|
||||
AC_SUBST(BLUEZ_LIBS)
|
||||
AC_SUBST(HAVE_BLUEZ)
|
||||
AM_CONDITIONAL([HAVE_BLUEZ], [test "x$HAVE_BLUEZ" = x1])
|
||||
|
||||
#### D-Bus support (optional) ####
|
||||
|
||||
AC_ARG_ENABLE([dbus],
|
||||
|
|
@ -1028,6 +1058,11 @@ if test "x${HAVE_LIBSAMPLERATE}" = "x1" ; then
|
|||
ENABLE_LIBSAMPLERATE=yes
|
||||
fi
|
||||
|
||||
ENABLE_BLUEZ=no
|
||||
if test "x${HAVE_BLUEZ}" = "x1" ; then
|
||||
ENABLE_BLUEZ=yes
|
||||
fi
|
||||
|
||||
echo "
|
||||
---{ $PACKAGE_NAME $VERSION }---
|
||||
|
||||
|
|
@ -1048,6 +1083,7 @@ echo "
|
|||
Enable Async DNS: ${ENABLE_LIBASYNCNS}
|
||||
Enable LIRC: ${ENABLE_LIRC}
|
||||
Enable HAL: ${ENABLE_HAL}
|
||||
Enable BlueZ: ${ENABLE_BLUEZ}
|
||||
Enable TCP Wrappers: ${ENABLE_TCPWRAP}
|
||||
Enable libsamplerate: ${ENABLE_LIBSAMPLERATE}
|
||||
System User: ${PA_SYSTEM_USER}
|
||||
|
|
|
|||
|
|
@ -736,6 +736,7 @@ libpulsecore_la_SOURCES += \
|
|||
pulsecore/macro.h \
|
||||
pulsecore/once.c pulsecore/once.h \
|
||||
pulsecore/time-smoother.c pulsecore/time-smoother.h \
|
||||
pulsecore/start-child.c pulsecore/start-child.h \
|
||||
$(PA_THREAD_OBJS)
|
||||
|
||||
if OS_IS_WIN32
|
||||
|
|
@ -1052,11 +1053,13 @@ modlibexec_LTLIBRARIES += \
|
|||
module-jack-source.la
|
||||
endif
|
||||
|
||||
pulselibexec_PROGRAMS =
|
||||
|
||||
if HAVE_GCONF
|
||||
modlibexec_LTLIBRARIES += \
|
||||
module-gconf.la
|
||||
|
||||
pulselibexec_PROGRAMS = \
|
||||
pulselibexec_PROGRAMS += \
|
||||
gconf-helper
|
||||
endif
|
||||
|
||||
|
|
@ -1071,6 +1074,14 @@ modlibexec_LTLIBRARIES += \
|
|||
module-hal-detect.la
|
||||
endif
|
||||
|
||||
if HAVE_BLUEZ
|
||||
modlibexec_LTLIBRARIES += \
|
||||
module-bt-proximity.la
|
||||
|
||||
pulselibexec_PROGRAMS += \
|
||||
bt-proximity-helper
|
||||
endif
|
||||
|
||||
# These are generated by a M4 script
|
||||
|
||||
SYMDEF_FILES = \
|
||||
|
|
@ -1121,6 +1132,7 @@ SYMDEF_FILES = \
|
|||
modules/module-rescue-streams-symdef.h \
|
||||
modules/module-suspend-on-idle-symdef.h \
|
||||
modules/module-hal-detect-symdef.h \
|
||||
modules/module-bt-proximity-symdef.h \
|
||||
modules/gconf/module-gconf-symdef.h
|
||||
|
||||
EXTRA_DIST += $(SYMDEF_FILES)
|
||||
|
|
@ -1419,6 +1431,17 @@ gconf_helper_LDADD = $(AM_LDADD) $(GCONF_LIBS) libpulsecore.la
|
|||
gconf_helper_CFLAGS = $(AM_CFLAGS) $(GCONF_CFLAGS)
|
||||
gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
|
||||
|
||||
# Bluetooth proximity
|
||||
module_bt_proximity_la_SOURCES = modules/module-bt-proximity.c
|
||||
module_bt_proximity_la_LDFLAGS = -module -avoid-version
|
||||
module_bt_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
|
||||
module_bt_proximity_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_BT_PROXIMITY_HELPER=\"$(pulselibexecdir)/bt-proximity-helper\"
|
||||
|
||||
bt_proximity_helper_SOURCES = modules/bt-proximity-helper.c
|
||||
bt_proximity_helper_LDADD = $(AM_LDADD) $(BLUEZ_LIBS)
|
||||
bt_proximity_helper_CFLAGS = $(AM_CFLAGS) $(BLUEZ_CFLAGS)
|
||||
bt_proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
|
||||
|
||||
###################################
|
||||
# Some minor stuff #
|
||||
###################################
|
||||
|
|
@ -1455,6 +1478,7 @@ daemon.conf: daemon/daemon.conf.in Makefile
|
|||
install-exec-hook:
|
||||
chown root $(DESTDIR)$(bindir)/pulseaudio ; true
|
||||
chmod u+s $(DESTDIR)$(bindir)/pulseaudio
|
||||
chmod u+s $(DESTDIR)$(pulselibexecdir)/bt-proximity-helper
|
||||
ln -sf pacat $(DESTDIR)$(bindir)/parec
|
||||
rm -f $(DESTDIR)$(modlibexecdir)/*.a
|
||||
rm -f $(DESTDIR)$(libdir)/libpulsedsp.a
|
||||
|
|
|
|||
210
src/modules/bt-proximity-helper.c
Normal file
210
src/modules/bt-proximity-helper.c
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2007 Lennart Poettering
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
PulseAudio is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with PulseAudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
/*
|
||||
* Small SUID helper that allows us to ping a BT device. Borrows
|
||||
* heavily from bluez-utils' l2ping, which is licensed as GPL2+, too
|
||||
* and comes with a copyright like this:
|
||||
*
|
||||
* Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2007 Marcel Holtmann <marcel@holtmann.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#undef NDEBUG
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/hci_lib.h>
|
||||
#include <bluetooth/l2cap.h>
|
||||
|
||||
#define PING_STRING "PulseAudio"
|
||||
#define IDENT 200
|
||||
#define TIMEOUT 4
|
||||
#define INTERVAL 2
|
||||
|
||||
static void update_status(int found) {
|
||||
static int status = -1;
|
||||
|
||||
if (!found && status != 0)
|
||||
printf("-");
|
||||
if (found && status <= 0)
|
||||
printf("+");
|
||||
|
||||
fflush(stdout);
|
||||
status = !!found;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct sockaddr_l2 addr;
|
||||
union {
|
||||
l2cap_cmd_hdr hdr;
|
||||
uint8_t buf[L2CAP_CMD_HDR_SIZE + sizeof(PING_STRING)];
|
||||
} packet;
|
||||
int fd = -1;
|
||||
uint8_t id = IDENT;
|
||||
int connected = 0;
|
||||
|
||||
assert(argc == 2);
|
||||
|
||||
for (;;) {
|
||||
fd_set fds;
|
||||
struct timeval end;
|
||||
ssize_t r;
|
||||
|
||||
if (!connected) {
|
||||
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
if ((fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
|
||||
fprintf(stderr, "socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP) failed: %s", strerror(errno));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
bacpy(&addr.l2_bdaddr, BDADDR_ANY);
|
||||
|
||||
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
fprintf(stderr, "bind() failed: %s", strerror(errno));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
str2ba(argv[1], &addr.l2_bdaddr);
|
||||
|
||||
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
|
||||
if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
|
||||
update_status(0);
|
||||
sleep(INTERVAL);
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stderr, "connect() failed: %s", strerror(errno));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
connected = 1;
|
||||
}
|
||||
|
||||
assert(connected);
|
||||
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
strcpy((char*) packet.buf + L2CAP_CMD_HDR_SIZE, PING_STRING);
|
||||
packet.hdr.ident = id;
|
||||
packet.hdr.len = htobs(sizeof(PING_STRING));
|
||||
packet.hdr.code = L2CAP_ECHO_REQ;
|
||||
|
||||
if ((r = send(fd, &packet, sizeof(packet), 0)) < 0) {
|
||||
|
||||
if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
|
||||
update_status(0);
|
||||
connected = 0;
|
||||
sleep(INTERVAL);
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stderr, "send() failed: %s", strerror(errno));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
assert(r == sizeof(packet));
|
||||
|
||||
gettimeofday(&end, NULL);
|
||||
end.tv_sec += TIMEOUT;
|
||||
|
||||
for (;;) {
|
||||
struct timeval now, delta;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
if (timercmp(&end, &now, <=)) {
|
||||
update_status(0);
|
||||
connected = 0;
|
||||
sleep(INTERVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
timersub(&end, &now, &delta);
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
|
||||
if (select(fd+1, &fds, NULL, NULL, &delta) < 0) {
|
||||
fprintf(stderr, "select() failed: %s", strerror(errno));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = recv(fd, &packet, sizeof(packet), 0)) <= 0) {
|
||||
|
||||
if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
|
||||
update_status(0);
|
||||
connected = 0;
|
||||
sleep(INTERVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "send() failed: %s", r == 0 ? "EOF" : strerror(errno));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
assert(r >= L2CAP_CMD_HDR_SIZE);
|
||||
|
||||
if (packet.hdr.ident != id)
|
||||
continue;
|
||||
|
||||
if (packet.hdr.code == L2CAP_ECHO_RSP || packet.hdr.code == L2CAP_COMMAND_REJ) {
|
||||
|
||||
if (++id >= 0xFF)
|
||||
id = IDENT;
|
||||
|
||||
update_status(1);
|
||||
sleep(INTERVAL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
492
src/modules/module-bt-proximity.c
Normal file
492
src/modules/module-bt-proximity.c
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2005-2006 Lennart Poettering
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
PulseAudio is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with PulseAudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulsecore/module.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/namereg.h>
|
||||
#include <pulsecore/sink.h>
|
||||
#include <pulsecore/modargs.h>
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/core-error.h>
|
||||
#include <pulsecore/start-child.h>
|
||||
|
||||
#include "dbus-util.h"
|
||||
#include "module-bt-proximity-symdef.h"
|
||||
|
||||
PA_MODULE_AUTHOR("Lennart Poettering");
|
||||
PA_MODULE_DESCRIPTION("Bluetooth Proximity Volume Control");
|
||||
PA_MODULE_VERSION(PACKAGE_VERSION);
|
||||
PA_MODULE_LOAD_ONCE(TRUE);
|
||||
PA_MODULE_USAGE(
|
||||
"sink=<sink name> "
|
||||
"hci=<hci device> "
|
||||
);
|
||||
|
||||
#define DEFAULT_HCI "hci0"
|
||||
|
||||
static const char* const valid_modargs[] = {
|
||||
"sink",
|
||||
"rssi",
|
||||
"hci",
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct bonding {
|
||||
struct userdata *userdata;
|
||||
char address[18];
|
||||
|
||||
pid_t pid;
|
||||
int fd;
|
||||
|
||||
pa_io_event *io_event;
|
||||
|
||||
enum {
|
||||
UNKNOWN,
|
||||
FOUND,
|
||||
NOT_FOUND
|
||||
} state;
|
||||
};
|
||||
|
||||
struct userdata {
|
||||
pa_module *module;
|
||||
pa_dbus_connection *dbus_connection;
|
||||
|
||||
char *sink_name;
|
||||
char *hci, *hci_path;
|
||||
|
||||
pa_hashmap *bondings;
|
||||
|
||||
unsigned n_found;
|
||||
unsigned n_unknown;
|
||||
|
||||
pa_bool_t muted;
|
||||
};
|
||||
|
||||
static void update_volume(struct userdata *u) {
|
||||
pa_assert(u);
|
||||
|
||||
if (u->muted && u->n_found > 0) {
|
||||
pa_sink *s;
|
||||
|
||||
u->muted = FALSE;
|
||||
|
||||
if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
|
||||
pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
|
||||
return;
|
||||
}
|
||||
|
||||
pa_log_info("Found %u BT devices, unmuting.", u->n_found);
|
||||
pa_sink_set_mute(s, FALSE);
|
||||
|
||||
} else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
|
||||
pa_sink *s;
|
||||
|
||||
u->muted = TRUE;
|
||||
|
||||
if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
|
||||
pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
|
||||
return;
|
||||
}
|
||||
|
||||
pa_log_info("No BT devices found, muting.");
|
||||
pa_sink_set_mute(s, TRUE);
|
||||
|
||||
} else
|
||||
pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);
|
||||
}
|
||||
|
||||
static void bonding_free(struct bonding *b) {
|
||||
pa_assert(b);
|
||||
|
||||
if (b->state == FOUND)
|
||||
pa_assert_se(b->userdata->n_found-- >= 1);
|
||||
|
||||
if (b->state == UNKNOWN)
|
||||
pa_assert_se(b->userdata->n_unknown-- >= 1);
|
||||
|
||||
if (b->pid != (pid_t) -1) {
|
||||
kill(b->pid, SIGTERM);
|
||||
waitpid(b->pid, NULL, 0);
|
||||
}
|
||||
|
||||
if (b->fd >= 0)
|
||||
pa_close(b->fd);
|
||||
|
||||
if (b->io_event)
|
||||
b->userdata->module->core->mainloop->io_free(b->io_event);
|
||||
|
||||
pa_xfree(b);
|
||||
}
|
||||
|
||||
static void io_event_cb(
|
||||
pa_mainloop_api*a,
|
||||
pa_io_event* e,
|
||||
int fd,
|
||||
pa_io_event_flags_t events,
|
||||
void *userdata) {
|
||||
|
||||
struct bonding *b = userdata;
|
||||
char x;
|
||||
ssize_t r;
|
||||
|
||||
pa_assert(b);
|
||||
|
||||
if ((r = read(fd, &x, 1)) <= 0) {
|
||||
pa_log_warn("Child watching '%s' died abnormally: %s", b->address, r == 0 ? "EOF" : pa_cstrerror(errno));
|
||||
|
||||
pa_assert_se(pa_hashmap_remove(b->userdata->bondings, b->address) == b);
|
||||
bonding_free(b);
|
||||
return;
|
||||
}
|
||||
|
||||
pa_assert_se(r == 1);
|
||||
|
||||
if (b->state == UNKNOWN)
|
||||
pa_assert_se(b->userdata->n_unknown-- >= 1);
|
||||
|
||||
if (x == '+') {
|
||||
pa_assert(b->state == UNKNOWN || b->state == NOT_FOUND);
|
||||
|
||||
b->state = FOUND;
|
||||
b->userdata->n_found++;
|
||||
|
||||
pa_log_info("Device '%s' is alive.", b->address);
|
||||
|
||||
} else {
|
||||
pa_assert(x == '-');
|
||||
pa_assert(b->state == UNKNOWN || b->state == FOUND);
|
||||
|
||||
if (b->state == FOUND)
|
||||
b->userdata->n_found--;
|
||||
|
||||
b->state = NOT_FOUND;
|
||||
|
||||
pa_log_info("Device '%s' is dead.", b->address);
|
||||
}
|
||||
|
||||
update_volume(b->userdata);
|
||||
}
|
||||
|
||||
static struct bonding* bonding_new(struct userdata *u, const char *a) {
|
||||
struct bonding *b = NULL;
|
||||
DBusMessage *m = NULL, *r = NULL;
|
||||
DBusError e;
|
||||
const char *class;
|
||||
|
||||
pa_assert(u);
|
||||
pa_assert(a);
|
||||
|
||||
pa_return_val_if_fail(strlen(a) == 17, NULL);
|
||||
pa_return_val_if_fail(!pa_hashmap_get(u->bondings, a), NULL);
|
||||
|
||||
dbus_error_init(&e);
|
||||
|
||||
pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "GetRemoteMajorClass"));
|
||||
pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID));
|
||||
r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), m, -1, &e);
|
||||
|
||||
if (!r) {
|
||||
pa_log("org.bluez.Adapter.GetRemoteMajorClass(%s) failed: %s", a, e.message);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(dbus_message_get_args(r, &e, DBUS_TYPE_STRING, &class, DBUS_TYPE_INVALID))) {
|
||||
pa_log("Malformed org.bluez.Adapter.GetRemoteMajorClass signal: %s", e.message);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (strcmp(class, "phone")) {
|
||||
pa_log_info("Found device '%s' of class '%s', ignoring.", a, class);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
b = pa_xnew(struct bonding, 1);
|
||||
b->userdata = u;
|
||||
pa_strlcpy(b->address, a, sizeof(b->address));
|
||||
b->pid = (pid_t) -1;
|
||||
b->fd = -1;
|
||||
b->io_event = NULL;
|
||||
b->state = UNKNOWN;
|
||||
u->n_unknown ++;
|
||||
|
||||
pa_log_info("Watching device '%s' of class '%s'.", b->address, class);
|
||||
|
||||
if ((b->fd = pa_start_child_for_read(PA_BT_PROXIMITY_HELPER, a, &b->pid)) < 0) {
|
||||
pa_log("Failed to start helper tool.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
b->io_event = u->module->core->mainloop->io_new(
|
||||
u->module->core->mainloop,
|
||||
b->fd,
|
||||
PA_IO_EVENT_INPUT,
|
||||
io_event_cb,
|
||||
b);
|
||||
|
||||
dbus_message_unref(m);
|
||||
dbus_message_unref(r);
|
||||
|
||||
pa_hashmap_put(u->bondings, a, b);
|
||||
|
||||
return b;
|
||||
|
||||
fail:
|
||||
if (m)
|
||||
dbus_message_unref(m);
|
||||
if (r)
|
||||
dbus_message_unref(r);
|
||||
|
||||
if (b)
|
||||
bonding_free(b);
|
||||
|
||||
dbus_error_free(&e);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void bonding_remove(struct userdata *u, const char *a) {
|
||||
struct bonding *b;
|
||||
pa_assert(u);
|
||||
|
||||
pa_return_if_fail((b = pa_hashmap_remove(u->bondings, a)));
|
||||
|
||||
pa_log_info("No longer watching device '%s'", b->address);
|
||||
bonding_free(b);
|
||||
}
|
||||
|
||||
static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *m, void *userdata) {
|
||||
struct userdata *u = userdata;
|
||||
DBusError e;
|
||||
|
||||
dbus_error_init(&e);
|
||||
|
||||
if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingCreated")) {
|
||||
const char *a;
|
||||
|
||||
if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
|
||||
pa_log("Malformed org.bluez.Adapter.BondingCreated signal: %s", e.message);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
bonding_new(u, a);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
|
||||
} else if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingRemoved")) {
|
||||
|
||||
const char *a;
|
||||
|
||||
if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
|
||||
pa_log("Malformed org.bluez.Adapter.BondingRemoved signal: %s", e.message);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
bonding_remove(u, a);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
finish:
|
||||
|
||||
dbus_error_free(&e);
|
||||
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static int add_matches(struct userdata *u, pa_bool_t add) {
|
||||
char *filter1, *filter2;
|
||||
DBusError e;
|
||||
int r = -1;
|
||||
|
||||
pa_assert(u);
|
||||
dbus_error_init(&e);
|
||||
|
||||
filter1 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingCreated',path='%s'", u->hci_path);
|
||||
filter2 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingRemoved',path='%s'", u->hci_path);
|
||||
|
||||
if (add) {
|
||||
dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
|
||||
|
||||
if (dbus_error_is_set(&e)) {
|
||||
pa_log("dbus_bus_add_match(%s) failed: %s", filter1, e.message);
|
||||
goto finish;
|
||||
}
|
||||
} else
|
||||
dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
|
||||
|
||||
|
||||
if (add) {
|
||||
dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
|
||||
|
||||
if (dbus_error_is_set(&e)) {
|
||||
pa_log("dbus_bus_add_match(%s) failed: %s", filter2, e.message);
|
||||
dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
|
||||
goto finish;
|
||||
}
|
||||
} else
|
||||
dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
|
||||
|
||||
|
||||
if (add)
|
||||
pa_assert_se(dbus_connection_add_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u, NULL));
|
||||
else
|
||||
dbus_connection_remove_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u);
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
pa_xfree(filter1);
|
||||
pa_xfree(filter2);
|
||||
dbus_error_free(&e);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int pa__init(pa_module*m) {
|
||||
pa_modargs *ma = NULL;
|
||||
struct userdata *u;
|
||||
DBusError e;
|
||||
DBusMessage *msg = NULL, *r = NULL;
|
||||
DBusMessageIter iter, sub;
|
||||
|
||||
pa_assert(m);
|
||||
dbus_error_init(&e);
|
||||
|
||||
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
|
||||
pa_log("Failed to parse module arguments");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
m->userdata = u = pa_xnew0(struct userdata, 1);
|
||||
u->module = m;
|
||||
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
|
||||
u->hci = pa_xstrdup(pa_modargs_get_value(ma, "hci", DEFAULT_HCI));
|
||||
u->hci_path = pa_sprintf_malloc("/org/bluez/%s", u->hci);
|
||||
u->n_found = u->n_unknown = 0;
|
||||
u->muted = FALSE;
|
||||
|
||||
u->bondings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
|
||||
if (!(u->dbus_connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &e))) {
|
||||
pa_log("Failed to get D-Bus connection: %s", e.message);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (add_matches(u, TRUE) < 0)
|
||||
goto fail;
|
||||
|
||||
pa_assert_se(msg = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "ListBondings"));
|
||||
|
||||
if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), msg, -1, &e))) {
|
||||
pa_log("org.bluez.Adapter.ListBondings failed: %s", e.message);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dbus_message_iter_init(r, &iter);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
|
||||
pa_log("Malformed reply to org.bluez.Adapter.ListBondings.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dbus_message_iter_recurse(&iter, &sub);
|
||||
|
||||
while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
|
||||
const char *a = NULL;
|
||||
|
||||
dbus_message_iter_get_basic(&sub, &a);
|
||||
bonding_new(u, a);
|
||||
|
||||
dbus_message_iter_next(&sub);
|
||||
}
|
||||
|
||||
dbus_message_unref(r);
|
||||
dbus_message_unref(msg);
|
||||
|
||||
pa_modargs_free(ma);
|
||||
|
||||
if (pa_hashmap_size(u->bondings) == 0)
|
||||
pa_log_warn("Warning: no phone device bonded.");
|
||||
|
||||
update_volume(u);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
|
||||
if (ma)
|
||||
pa_modargs_free(ma);
|
||||
|
||||
pa__done(m);
|
||||
|
||||
dbus_error_free(&e);
|
||||
|
||||
if (msg)
|
||||
dbus_message_unref(msg);
|
||||
|
||||
if (r)
|
||||
dbus_message_unref(r);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void pa__done(pa_module*m) {
|
||||
struct userdata *u;
|
||||
pa_assert(m);
|
||||
|
||||
if (!(u = m->userdata))
|
||||
return;
|
||||
|
||||
if (u->bondings) {
|
||||
struct bonding *b;
|
||||
|
||||
while ((b = pa_hashmap_steal_first(u->bondings)))
|
||||
bonding_free(b);
|
||||
|
||||
pa_hashmap_free(u->bondings, NULL, NULL);
|
||||
}
|
||||
|
||||
if (u->dbus_connection) {
|
||||
add_matches(u, FALSE);
|
||||
pa_dbus_connection_unref(u->dbus_connection);
|
||||
}
|
||||
|
||||
pa_xfree(u->sink_name);
|
||||
pa_xfree(u->hci_path);
|
||||
pa_xfree(u->hci);
|
||||
pa_xfree(u);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue