[pulseaudio-discuss] [PATCH v2] Added module-ofono-switch-on-voicecall
eu at felipetonello.com
eu at felipetonello.com
Thu Jun 5 10:45:36 PDT 2014
From: "Felipe F. Tonello" <eu at felipetonello.com>
This module is used to change a card profile when a voice call is received
from oFono.
Signed-off-by: Felipe F. Tonello <eu at felipetonello.com>
---
src/Makefile.am | 12 +-
src/modules/module-ofono-switch-on-voicecall.c | 480 +++++++++++++++++++++++++
2 files changed, 490 insertions(+), 2 deletions(-)
create mode 100644 src/modules/module-ofono-switch-on-voicecall.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 1ac8a16..add00fb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1326,7 +1326,8 @@ endif
if HAVE_DBUS
modlibexec_LTLIBRARIES += \
module-rygel-media-server.la \
- module-dbus-protocol.la
+ module-dbus-protocol.la \
+ module-ofono-switch-on-voicecall.la
endif
if HAVE_BLUEZ
@@ -1457,7 +1458,8 @@ SYMDEF_FILES = \
module-switch-on-connect-symdef.h \
module-switch-on-port-available-symdef.h \
module-filter-apply-symdef.h \
- module-filter-heuristics-symdef.h
+ module-filter-heuristics-symdef.h \
+ module-ofono-switch-on-voicecall-symdef.h
if HAVE_ESOUND
SYMDEF_FILES += \
@@ -2103,6 +2105,12 @@ module_rygel_media_server_la_LDFLAGS = $(MODULE_LDFLAGS)
module_rygel_media_server_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libprotocol-http.la
module_rygel_media_server_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+# oFono VoiceCall switcher
+module_ofono_switch_on_voicecall_la_SOURCES = modules/module-ofono-switch-on-voicecall.c
+module_ofono_switch_on_voicecall_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_ofono_switch_on_voicecall_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
+module_ofono_switch_on_voicecall_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
###################################
# Some minor stuff #
###################################
diff --git a/src/modules/module-ofono-switch-on-voicecall.c b/src/modules/module-ofono-switch-on-voicecall.c
new file mode 100644
index 0000000..786aedb
--- /dev/null
+++ b/src/modules/module-ofono-switch-on-voicecall.c
@@ -0,0 +1,480 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2013 Felipe F. Tonello <eu at felipetonello.com>
+
+ 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.1 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 <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-shared.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/hashmap.h>
+
+#include "module-ofono-switch-on-voicecall-symdef.h"
+
+PA_MODULE_AUTHOR("Felipe F. Tonello");
+PA_MODULE_DESCRIPTION("Card profile switcher while a call detected by oFono");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(false);
+PA_MODULE_USAGE("card=<card to switch profile> profile=<profile to set during a call>");
+
+static const char* const valid_modargs[] = {
+ "card",
+ "profile",
+ NULL,
+};
+
+struct userdata {
+ pa_card *card;
+ pa_card_profile *profile;
+ pa_card_profile *old_profile;
+ pa_dbus_connection *bus;
+ bool filter_added;
+ pa_hashmap *modems_hash;
+ char *voicecall_path;
+};
+
+static void modems_hash_free_value(void *p) {
+ pa_strlist_free((pa_strlist *)p);
+}
+
+static const char *check_variant_property(DBusMessageIter *i) {
+ const char *key;
+
+ pa_assert(i);
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+ pa_log("Property name not a string.");
+ return NULL;
+ }
+
+ dbus_message_iter_get_basic(i, &key);
+
+ if (!dbus_message_iter_next(i)) {
+ pa_log("Property value missing");
+ return NULL;
+ }
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+ pa_log("Property value not a variant.");
+ return NULL;
+ }
+
+ return key;
+}
+
+static inline bool update_profile(const struct userdata *u, pa_card_profile *p) {
+ pa_assert(u);
+ pa_assert(p);
+
+ /* only change profile if the new profile is different */
+ if (u->card->active_profile == p)
+ return false;
+
+ if (pa_card_set_profile(u->card, p, false) < 0) {
+ pa_log_error("Couldn't set profile '%s' for card '%s'", p->name, u->card->name);
+ return false;
+ }
+
+ pa_log_info("Set profile '%s' for card '%s'", p->name, u->card->name);
+
+ return true;
+}
+
+static bool switch_profile(const struct userdata *u) {
+ return update_profile(u, u->profile);
+}
+
+static bool switch_back_profile(const struct userdata *u) {
+ return update_profile(u, u->old_profile);
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
+ struct userdata *u = userdata;
+ DBusError error;
+
+ pa_assert(bus);
+ pa_assert(message);
+ pa_assert(u);
+
+ dbus_error_init(&error);
+
+ pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
+ dbus_message_get_interface(message),
+ dbus_message_get_path(message),
+ dbus_message_get_member(message));
+
+ if (dbus_message_is_signal(message, "org.ofono.Manager", "ModemAdded")) {
+ /* create new listener for the modem */
+
+ const char *path;
+ pa_strlist *filter_list = NULL;
+ char *call_added, *call_removed;
+
+ if (!dbus_message_get_args(message, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+ pa_log_error("Failed to parse ModemAdded message: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ pa_log_debug("ModemAdded(object path, dict properties): path=%s", path);
+
+ if (pa_hashmap_get(u->modems_hash, path)) {
+ pa_log_info("Modem already loaded: %s", path);
+ goto finish;
+ }
+
+ call_added = pa_sprintf_malloc("type='signal',sender='org.ofono',"
+ "interface='org.ofono.VoiceCallManager',"
+ "path='%s',member='CallAdded'", path);
+ call_removed = pa_sprintf_malloc("type='signal',sender='org.ofono',"
+ "interface='org.ofono.VoiceCallManager',"
+ "path='%s',member='CallRemoved'", path);
+
+ if (pa_dbus_add_matches(pa_dbus_connection_get(u->bus), &error,
+ call_added, call_removed, NULL) < 0) {
+ pa_log_error("Unable to subscribe to oFono VoiceCallManager signals: %s: %s",
+ error.name, error.message);
+ pa_xfree(call_added);
+ pa_xfree(call_removed);
+ goto finish;
+ }
+
+ filter_list = pa_strlist_prepend(filter_list, call_added);
+ filter_list = pa_strlist_prepend(filter_list, call_removed);
+ pa_hashmap_put(u->modems_hash, (char *)path, filter_list);
+
+ } else if (dbus_message_is_signal(message, "org.ofono.Manager", "ModemRemoved")) {
+ /* remove that listener */
+ const char *path;
+ pa_strlist *filter_list = NULL, *l;
+ char *s;
+
+ if (!dbus_message_get_args(message, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+ pa_log_error("Failed to parse ModemAdded message: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ pa_log("ModemRemoved(object path): path=%s", path);
+
+ if (!(filter_list = pa_hashmap_remove(u->modems_hash, path))) {
+ pa_log_info("Modem '%s' not loaded", path);
+ goto finish;
+ }
+
+ l = filter_list;
+ while (l) {
+ l = pa_strlist_pop(l, &s);
+ pa_dbus_remove_matches(pa_dbus_connection_get(u->bus), s, NULL);
+ pa_xfree(s);
+ }
+
+ } else if (dbus_message_is_signal(message, "org.ofono.VoiceCallManager", "CallAdded")) {
+ /* check call properties if "state" is "dialing" or "incoming" */
+ DBusMessageIter arg_i;
+
+ if (u->voicecall_path) {
+ pa_log_error("VoiceCall was already loaded: '%s'", u->voicecall_path);
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(message, &arg_i)) {
+ pa_log_error("Failed to parse ModemAdded: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ while (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_INVALID) {
+ const char *path;
+
+ switch (dbus_message_iter_get_arg_type(&arg_i)) {
+ case DBUS_TYPE_OBJECT_PATH: {
+ dbus_message_iter_get_basic(&arg_i, &path);
+ break;
+ }
+
+ case DBUS_TYPE_ARRAY: {
+ DBusMessageIter array_i;
+ dbus_message_iter_recurse(&arg_i, &array_i);
+ while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) {
+ DBusMessageIter dict_i;
+
+ dbus_message_iter_recurse(&array_i, &dict_i);
+ while (dbus_message_iter_get_arg_type(&dict_i) != DBUS_TYPE_INVALID) {
+ const char *key;
+
+ key = check_variant_property(&dict_i);
+ if (key) {
+ DBusMessageIter variant_i;
+
+ dbus_message_iter_recurse(&dict_i, &variant_i);
+
+ if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_STRING &&
+ pa_streq(key, "State")) {
+ const char *state;
+ dbus_message_iter_get_basic(&variant_i, &state);
+
+ if (pa_streq(state, "dialing") || pa_streq(state, "incoming")) {
+ char *filter;
+
+ pa_log_debug("CallAdded(object path, dict properties): path=%s "
+ "state=%s", path, state);
+
+ u->voicecall_path = pa_xstrdup(path);
+ filter = pa_sprintf_malloc("type='signal',sender='org.ofono',"
+ "interface='org.ofono.VoiceCall',"
+ "path='%s',member='PropertyChanged'",
+ u->voicecall_path);
+
+ if (pa_dbus_add_matches(pa_dbus_connection_get(u->bus), &error,
+ filter, NULL) < 0) {
+ pa_log_error("Unable to subscribe to oFono VoiceCallManager signals: %s: %s",
+ error.name, error.message);
+ pa_xfree(filter);
+ goto finish;
+ }
+ pa_xfree(filter);
+ }
+ }
+ }
+ dbus_message_iter_next(&dict_i);
+ }
+
+ dbus_message_iter_next(&array_i);
+ }
+ break;
+ }
+ }
+ dbus_message_iter_next(&arg_i);
+ }
+ } else if (dbus_message_is_signal(message, "org.ofono.VoiceCallManager", "CallRemoved")) {
+ const char *path;
+ char *filter;
+
+ if (!dbus_message_get_args(message, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+ pa_log_error("Failed to parse ModemAdded message: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ pa_log_debug("CallRemoved(object path, dict properties): path=%s", path);
+
+ if (!(u->voicecall_path && pa_streq(path, u->voicecall_path))) {
+ pa_log_error("VoiceCall '%s' not loaded", path);
+ goto finish;
+ }
+
+ switch_back_profile(u);
+
+ filter = pa_sprintf_malloc("type='signal',sender='org.ofono',"
+ "interface='org.ofono.VoiceCall',"
+ "path='%s',member='PropertyChanged'",
+ u->voicecall_path);
+
+ pa_dbus_remove_matches(pa_dbus_connection_get(u->bus), filter, NULL);
+
+ pa_xfree(filter);
+ pa_xfree(u->voicecall_path);
+ u->voicecall_path = NULL;
+
+ } else if (dbus_message_is_signal(message, "org.ofono.VoiceCall", "PropertyChanged")) {
+ const char *path, *key;
+ DBusMessageIter arg_i, variant_i;
+
+ path = dbus_message_get_path(message);
+
+ if (!(u->voicecall_path && pa_streq(path, u->voicecall_path))) {
+ pa_log_error("VoiceCall '%s' not loaded", path);
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(message, &arg_i)) {
+ pa_log_error("Failed to parse PropertyChange: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ key = check_variant_property(&arg_i);
+ if (!key)
+ goto finish;
+
+ dbus_message_iter_recurse(&arg_i, &variant_i);
+
+ if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_STRING &&
+ pa_streq(key, "State")) {
+ const char *state;
+
+ dbus_message_iter_get_basic(&variant_i, &state);
+ if (pa_streq(state, "alerting") || pa_streq(state, "active")) {
+
+ pa_log_debug("CallAdded(object path, dict properties): path=%s "
+ "state=%s", path, state);
+
+ switch_profile(u);
+ }
+ }
+ }
+
+finish:
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int pa__init(pa_module *m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u = NULL;
+ char *card_name = NULL;
+ char *profile_name = NULL;
+ pa_card *card = NULL;
+ pa_card_profile *profile = NULL;
+ void *state;
+ DBusError error;
+
+ pa_assert(m);
+
+ dbus_error_init(&error);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log_error("Failed to parse module arguments");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->modems_hash = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
+ NULL, modems_hash_free_value);
+
+ if (!(card_name = pa_xstrdup(pa_modargs_get_value(ma, "card", NULL)))) {
+ pa_log_error("Missing card name");
+ goto fail;
+ }
+
+ if (!(profile_name = pa_xstrdup(pa_modargs_get_value(ma, "profile", NULL)))) {
+ pa_log_error("Missing profile name");
+ goto fail;
+ }
+
+ if (!(card = pa_namereg_get(m->core, card_name, PA_NAMEREG_CARD))) {
+ pa_log_error("No such card (%s)", card_name);
+ goto fail;
+ }
+
+ u->card = card;
+
+ PA_HASHMAP_FOREACH(profile, card->profiles, state)
+ if (profile && pa_streq(profile->name, profile_name)) {
+ u->profile = profile;
+ break;
+ }
+
+ if (!profile) {
+ pa_log_error("No such profile (%s) associated with card (%s)", profile_name, card_name);
+ goto fail;
+ }
+
+ u->old_profile = card->active_profile;
+
+ if (!(u->bus = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error))) {
+ pa_log_error("Failed to get system bus connection: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(u->bus), filter_cb, u, NULL)) {
+ pa_log_error("Failed to add D-Bus filter function");
+ goto fail;
+ }
+
+ u->filter_added = true;
+
+ if (pa_dbus_add_matches(pa_dbus_connection_get(u->bus), &error,
+ "type='signal',sender='org.ofono',"
+ "interface='org.ofono.Manager',"
+ "path='/'",
+ NULL) < 0) {
+ pa_log_error("Unable to subscribe to oFono signals: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ pa_modargs_free(ma);
+ pa_xfree(card_name);
+ pa_xfree(profile_name);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa_xfree(card_name);
+ pa_xfree(profile_name);
+
+ /* Make sure we don't change anything in pa__done() */
+ if (u) {
+ u->card = NULL;
+ u->old_profile = NULL;
+ }
+
+ dbus_error_free(&error);
+
+ pa__done(m);
+ return -1;
+}
+
+void pa__done(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->modems_hash)
+ pa_hashmap_free(u->modems_hash);
+
+ if (u->card && u->old_profile)
+ pa_card_set_profile(u->card, u->old_profile, true);
+
+ if (u->bus) {
+
+ if (u->voicecall_path) {
+ char *filter = pa_sprintf_malloc("type='signal',sender='org.ofono',"
+ "interface='org.ofono.VoiceCall',"
+ "path='%s',member='PropertyChanged'",
+ u->voicecall_path);
+
+ pa_dbus_remove_matches(pa_dbus_connection_get(u->bus), filter, NULL);
+ pa_xfree(filter);
+ pa_xfree(u->voicecall_path);
+ }
+
+ pa_dbus_remove_matches(pa_dbus_connection_get(u->bus),
+ "type='signal',sender='org.ofono',"
+ "interface='org.ofono.Manager',"
+ "path='/'",
+ NULL);
+
+ if (u->filter_added)
+ dbus_connection_remove_filter(pa_dbus_connection_get(u->bus), filter_cb, u);
+
+ pa_dbus_connection_unref(u->bus);
+ }
+
+ pa_xfree(u);
+}
--
1.9.3
More information about the pulseaudio-discuss
mailing list