From: "Felipe F. Tonello" <[email protected]>

This module is used to change a card profile when a voice call is received
from oFono.

Signed-off-by: Felipe F. Tonello <[email protected]>
---
 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 <[email protected]>
+
+  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

_______________________________________________
pulseaudio-discuss mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss

Reply via email to