---
audio/Makefile.am | 3 +-
audio/telephony-ofono.c | 1116 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1118 insertions(+), 1 deletions(-)
create mode 100644 audio/telephony-ofono.c
diff --git a/audio/Makefile.am b/audio/Makefile.am
index 67c75ba..cbdad9f 100644
--- a/audio/Makefile.am
+++ b/audio/Makefile.am
@@ -77,7 +77,8 @@ CLEANFILES = $(BUILT_SOURCES)
INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/src
-EXTRA_DIST = audio.conf telephony-dummy.c telephony-maemo.c bluetooth.conf
+EXTRA_DIST = audio.conf telephony-dummy.c telephony-maemo.c telephony-ofono.c \
+ bluetooth.conf
MAINTAINERCLEANFILES = Makefile.in
diff --git a/audio/telephony-ofono.c b/audio/telephony-ofono.c
new file mode 100644
index 0000000..a942d97
--- /dev/null
+++ b/audio/telephony-ofono.c
@@ -0,0 +1,1116 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2009 Intel Corporation
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 Marcel Holtmann <[email protected]>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "logging.h"
+#include "telephony.h"
+
+enum net_registration_status {
+ NETWORK_REG_STATUS_HOME = 0x00,
+ NETWORK_REG_STATUS_ROAM,
+ NETWORK_REG_STATUS_NOSERV
+};
+
+struct voice_call {
+ char *obj_path;
+ int status;
+ gboolean originating;
+ char *number;
+};
+
+static DBusConnection *connection = NULL;
+static char *modem_obj_path = NULL;
+static char *last_dialed_number = NULL;
+static GSList *calls = NULL;
+
+#define OFONO_BUS_NAME "org.ofono"
+#define OFONO_PATH "/"
+#define OFONO_MANAGER_INTERFACE "org.ofono.ModemManager"
+#define OFONO_NETWORKREG_INTERFACE "org.ofono.NetworkRegistration"
+#define OFONO_VCMANAGER_INTERFACE "org.ofono.VoiceCallManager"
+#define OFONO_VC_INTERFACE "org.ofono.VoiceCall"
+
+/* HAL battery namespace key values */
+static int battchg_cur = -1; /* "battery.charge_level.current" */
+static int battchg_last = -1; /* "battery.charge_level.last_full" */
+static int battchg_design = -1; /* "battery.charge_level.design" */
+
+static struct {
+ uint8_t status;
+ uint32_t signals_bar;
+ char *operator_name;
+} net = {
+ .status = NETWORK_REG_STATUS_NOSERV,
+ .signals_bar = 0,
+ .operator_name = NULL,
+};
+
+static const char *chld_str = "0,1,1x,2,2x,3,4";
+static char *subscriber_number = NULL;
+
+static gboolean events_enabled = FALSE;
+
+/* Response and hold state
+ * -1 = none
+ * 0 = incoming call is put on hold in the AG
+ * 1 = held incoming call is accepted in the AG
+ * 2 = held incoming call is rejected in the AG
+ */
+static int response_and_hold = -1;
+
+static struct indicator ofono_indicators[] =
+{
+ { "battchg", "0-5", 5 },
+ { "signal", "0-5", 5 },
+ { "service", "0,1", 1 },
+ { "call", "0,1", 0 },
+ { "callsetup", "0-3", 0 },
+ { "callheld", "0-2", 0 },
+ { "roam", "0,1", 0 },
+ { NULL }
+};
+
+static struct voice_call *find_vc(const char *path)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct voice_call *vc = l->data;
+
+ if (g_str_equal(vc->obj_path, path))
+ return vc;
+ }
+
+ return NULL;
+}
+
+static struct voice_call *find_vc_with_status(int status)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct voice_call *vc = l->data;
+
+ if (vc->status == status)
+ return vc;
+ }
+
+ return NULL;
+}
+
+static inline DBusMessage *invalid_args(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
+ "Invalid arguments in method call");
+}
+
+void telephony_device_connected(void *telephony_device)
+{
+ debug("telephony-ofono: device %p connected", telephony_device);
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+ debug("telephony-ofono: device %p disconnected", telephony_device);
+ events_enabled = FALSE;
+}
+
+void telephony_event_reporting_req(void *telephony_device, int ind)
+{
+ events_enabled = ind == 1 ? TRUE : FALSE;
+
+ telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_response_and_hold_req(void *telephony_device, int rh)
+{
+ response_and_hold = rh;
+
+ telephony_response_and_hold_ind(response_and_hold);
+
+ telephony_response_and_hold_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_last_dialed_number_req(void *telephony_device)
+{
+ debug("telephony-ofono: last dialed number request");
+
+ if (last_dialed_number)
+ telephony_dial_number_req(telephony_device, last_dialed_number);
+ else
+ telephony_last_dialed_number_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+}
+
+static int send_method_call(const char *dest, const char *path,
+ const char *interface, const char *method,
+ DBusPendingCallNotifyFunction cb,
+ void *user_data, int type, ...)
+{
+ DBusMessage *msg;
+ DBusPendingCall *call;
+ va_list args;
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ error("Unable to allocate new D-Bus %s message", method);
+ return -ENOMEM;
+ }
+
+ va_start(args, type);
+
+ if (!dbus_message_append_args_valist(msg, type, args)) {
+ dbus_message_unref(msg);
+ va_end(args);
+ return -EIO;
+ }
+
+ va_end(args);
+
+ if (!cb) {
+ g_dbus_send_message(connection, msg);
+ return 0;
+ }
+
+ if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+ error("Sending %s failed", method);
+ dbus_message_unref(msg);
+ return -EIO;
+ }
+
+ dbus_pending_call_set_notify(call, cb, user_data, NULL);
+ dbus_pending_call_unref(call);
+ dbus_message_unref(msg);
+
+ return 0;
+}
+
+void telephony_terminate_call_req(void *telephony_device)
+{
+ struct voice_call *vc;
+ int ret;
+
+ if ((vc = find_vc_with_status(CALL_STATUS_ACTIVE)))
+ ;
+ else if ((vc = find_vc_with_status(CALL_STATUS_DIALING)))
+ ;
+ else if ((vc = find_vc_with_status(CALL_STATUS_ALERTING)))
+ ;
+ else if ((vc = find_vc_with_status(CALL_STATUS_INCOMING)))
+ ;
+
+ if (!vc) {
+ error("in telephony_terminate_call_req, no active call");
+ telephony_terminate_call_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+ return;
+ }
+
+ ret = send_method_call(OFONO_BUS_NAME, vc->obj_path,
+ OFONO_VC_INTERFACE,
+ "Hangup", NULL,
+ NULL, DBUS_TYPE_INVALID);
+
+ if (ret < 0) {
+ telephony_answer_call_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_answer_call_req(void *telephony_device)
+{
+ struct voice_call *vc = find_vc_with_status(CALL_STATUS_INCOMING);
+ int ret;
+
+ if (!vc) {
+ telephony_answer_call_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+ return;
+ }
+
+ ret = send_method_call(OFONO_BUS_NAME, vc->obj_path,
+ OFONO_VC_INTERFACE,
+ "Answer", NULL,
+ NULL, DBUS_TYPE_INVALID);
+
+ if (ret < 0) {
+ telephony_answer_call_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_dial_number_req(void *telephony_device, const char *number)
+{
+ char *clir = "default";
+ int ret;
+
+ debug("telephony-ofono: dial request to %s", number);
+
+ if (!strncmp(number, "*31#", 4)) {
+ number += 4;
+ clir = g_strdup("enabled");
+ } else if (!strncmp(number, "#31#", 4)) {
+ number += 4;
+ clir = g_strdup("disabled");
+ }
+
+ ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "Dial", NULL, NULL,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_STRING, &clir,
+ DBUS_TYPE_INVALID);
+
+ if (ret < 0)
+ telephony_dial_number_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_transmit_dtmf_req(void *telephony_device, char tone)
+{
+ char *tone_string;
+ int ret;
+
+ debug("telephony-ofono: transmit dtmf: %c", tone);
+
+ tone_string = g_strdup_printf("%c", tone);
+ ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "SendTones", NULL, NULL,
+ DBUS_TYPE_STRING, &tone_string,
+ DBUS_TYPE_INVALID);
+ g_free(tone_string);
+
+ if (ret < 0)
+ telephony_transmit_dtmf_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_subscriber_number_req(void *telephony_device)
+{
+ debug("telephony-ofono: subscriber number request");
+
+ if (subscriber_number)
+ telephony_subscriber_number_ind(subscriber_number,
+ NUMBER_TYPE_TELEPHONY,
+ SUBSCRIBER_SERVICE_VOICE);
+ telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_list_current_calls_req(void *telephony_device)
+{
+ GSList *l;
+ int i;
+
+ debug("telephony-ofono: list current calls request");
+
+ for (l = calls, i = 1; l != NULL; l = l->next, i++) {
+ struct voice_call *vc = l->data;
+ int direction;
+
+ direction = vc->originating ?
+ CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
+
+ telephony_list_current_call_ind(i, direction, vc->status,
+ CALL_MODE_VOICE, CALL_MULTIPARTY_NO,
+ vc->number, NUMBER_TYPE_TELEPHONY);
+ }
+ telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_operator_selection_req(void *telephony_device)
+{
+ debug("telephony-ofono: operator selection request");
+
+ telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
+ net.operator_name ? net.operator_name : "");
+ telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_call_hold_req(void *telephony_device, const char *cmd)
+{
+ debug("telephony-ofono: got call hold request %s", cmd);
+ telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
+{
+ debug("telephony-ofono: got %s NR and EC request",
+ enable ? "enable" : "disable");
+
+ telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_key_press_req(void *telephony_device, const char *keys)
+{
+ debug("telephony-ofono: got key press request for %s", keys);
+ telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static gboolean iter_get_basic_args(DBusMessageIter *iter,
+ int first_arg_type, ...)
+{
+ int type;
+ va_list ap;
+
+ va_start(ap, first_arg_type);
+
+ for (type = first_arg_type; type != DBUS_TYPE_INVALID;
+ type = va_arg(ap, int)) {
+ void *value = va_arg(ap, void *);
+ int real_type = dbus_message_iter_get_arg_type(iter);
+
+ if (real_type != type) {
+ error("iter_get_basic_args: expected %c but got %c",
+ (char) type, (char) real_type);
+ break;
+ }
+
+ dbus_message_iter_get_basic(iter, value);
+ dbus_message_iter_next(iter);
+ }
+
+ va_end(ap);
+
+ return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
+}
+
+void handle_registration_property(const char *property, DBusMessageIter sub)
+{
+ char *status, *operator;
+ unsigned int signals_bar;
+
+ if (g_str_equal(property, "Status")) {
+ dbus_message_iter_get_basic(&sub, &status);
+ debug("Status is %s", status);
+ if (g_str_equal(status, "registered")) {
+ net.status = NETWORK_REG_STATUS_HOME;
+ telephony_update_indicator(ofono_indicators,
+ "roam", EV_ROAM_INACTIVE);
+ telephony_update_indicator(ofono_indicators,
+ "service", EV_SERVICE_PRESENT);
+ } else if (g_str_equal(status, "roaming")) {
+ net.status = NETWORK_REG_STATUS_ROAM;
+ telephony_update_indicator(ofono_indicators,
+ "roam", EV_ROAM_ACTIVE);
+ telephony_update_indicator(ofono_indicators,
+ "service", EV_SERVICE_PRESENT);
+ } else {
+ net.status = NETWORK_REG_STATUS_NOSERV;
+ telephony_update_indicator(ofono_indicators,
+ "roam", EV_ROAM_INACTIVE);
+ telephony_update_indicator(ofono_indicators,
+ "service", EV_SERVICE_NONE);
+ }
+ } else if (g_str_equal(property, "Operator")) {
+ dbus_message_iter_get_basic(&sub, &operator);
+ debug("Operator is %s", operator);
+ g_free(net.operator_name);
+ net.operator_name = g_strdup(operator);
+ } else if (g_str_equal(property, "SignalStrength")) {
+ dbus_message_iter_get_basic(&sub, &signals_bar);
+ debug("SignalStrength is %d", signals_bar);
+ net.signals_bar = signals_bar;
+ telephony_update_indicator(ofono_indicators, "signal",
+ (signals_bar + 20) / 21);
+ }
+}
+
+static void get_registration_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, iter_entry;
+ uint32_t features = AG_FEATURE_EC_ANDOR_NR |
+ AG_FEATURE_REJECT_A_CALL |
+ AG_FEATURE_ENHANCED_CALL_STATUS |
+ AG_FEATURE_EXTENDED_ERROR_RESULT_CODES;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("ofono replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ /* ARRAY -> ENTRY -> VARIANT*/
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in GetProperties return");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &iter_entry);
+
+ if (dbus_message_iter_get_arg_type(&iter_entry)
+ != DBUS_TYPE_DICT_ENTRY) {
+ error("Unexpected signature in GetProperties return");
+ goto done;
+ }
+
+ while (dbus_message_iter_get_arg_type(&iter_entry)
+ != DBUS_TYPE_INVALID) {
+ DBusMessageIter iter_property, sub;
+ char *property;
+
+ dbus_message_iter_recurse(&iter_entry, &iter_property);
+ if (dbus_message_iter_get_arg_type(&iter_property)
+ != DBUS_TYPE_STRING) {
+ error("Unexpected signature in GetProperties return");
+ goto done;
+ }
+
+ dbus_message_iter_get_basic(&iter_property, &property);
+
+ dbus_message_iter_next(&iter_property);
+ dbus_message_iter_recurse(&iter_property, &sub);
+
+ handle_registration_property(property, sub);
+
+ dbus_message_iter_next(&iter_entry);
+ }
+
+ telephony_ready_ind(features, ofono_indicators,
+ response_and_hold, chld_str);
+
+done:
+ dbus_message_unref(reply);
+}
+
+static int get_registration_and_signal_status()
+{
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_NETWORKREG_INTERFACE,
+ "GetProperties", get_registration_reply,
+ NULL, DBUS_TYPE_INVALID);
+}
+
+static void list_modem_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, iter_entry, iter_property, iter_arrary, sub;
+ char *property, *modem_obj_path_local;
+ int ret;
+
+ debug("list_modem_reply is called\n");
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("ofono replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in ListModems return");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &iter_entry);
+
+ if (dbus_message_iter_get_arg_type(&iter_entry)
+ != DBUS_TYPE_DICT_ENTRY) {
+ error("Unexpected signature in ListModems return 2, %c",
+ dbus_message_iter_get_arg_type(&iter_entry));
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter_entry, &iter_property);
+
+ dbus_message_iter_get_basic(&iter_property, &property);
+
+ dbus_message_iter_next(&iter_property);
+ dbus_message_iter_recurse(&iter_property, &iter_arrary);
+ dbus_message_iter_recurse(&iter_arrary, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+
+ dbus_message_iter_get_basic(&sub, &modem_obj_path_local);
+ modem_obj_path = g_strdup(modem_obj_path_local);
+ debug("modem_obj_path is %p, %s\n", modem_obj_path,
+ modem_obj_path);
+ dbus_message_iter_next(&sub);
+ }
+
+ ret = get_registration_and_signal_status();
+ if (ret < 0)
+ error("get_registration_and_signal_status() failed(%d)", ret);
+done:
+ dbus_message_unref(reply);
+}
+
+static void handle_networkregistration_property_changed(DBusMessage *msg,
+ const char *call_path)
+{
+ DBusMessageIter iter, sub;
+ const char *property;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+ error("Unexpected signature in networkregistration"
+ " PropertyChanged signal");
+ return;
+ }
+ dbus_message_iter_get_basic(&iter, &property);
+ debug("in handle_networkregistration_property_changed(),"
+ " the property is %s", property);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &sub);
+
+ handle_registration_property(property, sub);
+}
+
+static void vc_getproperties_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply;
+ DBusError err;
+ DBusMessageIter iter, iter_entry;
+ const char *path = user_data;
+ struct voice_call *vc;
+
+ debug("in vc_getproperties_reply");
+
+ reply = dbus_pending_call_steal_reply(call);
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("ofono replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ vc = find_vc(path);
+ if (!vc) {
+ error("in vc_getproperties_reply, vc is NULL");
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in vc_getproperties_reply()");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &iter_entry);
+
+ if (dbus_message_iter_get_arg_type(&iter_entry)
+ != DBUS_TYPE_DICT_ENTRY) {
+ error("Unexpected signature in vc_getproperties_reply()");
+ goto done;
+ }
+
+ while (dbus_message_iter_get_arg_type(&iter_entry)
+ != DBUS_TYPE_INVALID) {
+ DBusMessageIter iter_property, sub;
+ char *property, *cli, *state;
+
+ dbus_message_iter_recurse(&iter_entry, &iter_property);
+ if (dbus_message_iter_get_arg_type(&iter_property)
+ != DBUS_TYPE_STRING) {
+ error("Unexpected signature in"
+ " vc_getproperties_reply()");
+ goto done;
+ }
+
+ dbus_message_iter_get_basic(&iter_property, &property);
+
+ dbus_message_iter_next(&iter_property);
+ dbus_message_iter_recurse(&iter_property, &sub);
+ if (g_str_equal(property, "LineIdentification")) {
+ dbus_message_iter_get_basic(&sub, &cli);
+ debug("in vc_getproperties_reply(), cli is %s", cli);
+ vc->number = g_strdup(cli);
+ } else if (g_str_equal(property, "State")) {
+ dbus_message_iter_get_basic(&sub, &state);
+ debug("in vc_getproperties_reply(),"
+ " state is %s", state);
+ if (g_str_equal(state, "incoming"))
+ vc->status = CALL_STATUS_INCOMING;
+ else if (g_str_equal(state, "dialing"))
+ vc->status = CALL_STATUS_DIALING;
+ else if (g_str_equal(state, "alerting"))
+ vc->status = CALL_STATUS_ALERTING;
+ else if (g_str_equal(state, "waiting"))
+ vc->status = CALL_STATUS_WAITING;
+ }
+
+ dbus_message_iter_next(&iter_entry);
+ }
+
+ switch (vc->status) {
+ case CALL_STATUS_INCOMING:
+ printf("in CALL_STATUS_INCOMING: case\n");
+ vc->originating = FALSE;
+ telephony_update_indicator(ofono_indicators, "callsetup",
+ EV_CALLSETUP_INCOMING);
+ telephony_incoming_call_ind(vc->number, NUMBER_TYPE_TELEPHONY);
+ break;
+ case CALL_STATUS_DIALING:
+ printf("in CALL_STATUS_DIALING: case\n");
+ vc->originating = TRUE;
+ g_free(last_dialed_number);
+ last_dialed_number = g_strdup(vc->number);
+ telephony_update_indicator(ofono_indicators, "callsetup",
+ EV_CALLSETUP_OUTGOING);
+ break;
+ case CALL_STATUS_ALERTING:
+ printf("in CALL_STATUS_ALERTING: case\n");
+ vc->originating = TRUE;
+ g_free(last_dialed_number);
+ last_dialed_number = g_strdup(vc->number);
+ telephony_update_indicator(ofono_indicators, "callsetup",
+ EV_CALLSETUP_ALERTING);
+ break;
+ case CALL_STATUS_WAITING:
+ debug("in CALL_STATUS_WAITING: case");
+ vc->originating = FALSE;
+ telephony_update_indicator(ofono_indicators, "callsetup",
+ EV_CALLSETUP_INCOMING);
+ telephony_call_waiting_ind(vc->number, NUMBER_TYPE_TELEPHONY);
+ break;
+ }
+done:
+ dbus_message_unref(reply);
+}
+
+static void handle_vcmanager_property_changed(DBusMessage *msg,
+ const char *obj_path)
+{
+ DBusMessageIter iter, sub, array;
+ const char *property, *vc_obj_path = NULL;
+ struct voice_call *vc = NULL, *vc_new = NULL;
+
+ debug("in handle_vcmanager_property_changed");
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+ error("Unexpected signature in vcmanager"
+ " PropertyChanged signal");
+ return;
+ }
+
+ dbus_message_iter_get_basic(&iter, &property);
+ debug("in handle_vcmanager_property_changed(),"
+ " the property is %s", property);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &sub);
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in vcmanager"
+ " PropertyChanged signal");
+ return;
+ }
+ dbus_message_iter_recurse(&sub, &array);
+ while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
+ dbus_message_iter_get_basic(&array, &vc_obj_path);
+ vc = find_vc(vc_obj_path);
+ if (vc) {
+ debug("in handle_vcmanager_property_changed,"
+ " found an existing vc");
+ } else {
+ vc_new = g_new0(struct voice_call, 1);
+ vc_new->obj_path = g_strdup(vc_obj_path);
+ calls = g_slist_append(calls, vc_new);
+ }
+ dbus_message_iter_next(&array);
+ }
+
+ if (!vc_new)
+ return;
+
+ send_method_call(OFONO_BUS_NAME, vc_new->obj_path,
+ OFONO_VC_INTERFACE,
+ "GetProperties", vc_getproperties_reply,
+ vc_new->obj_path, DBUS_TYPE_INVALID);
+}
+
+static void vc_free(struct voice_call *vc)
+{
+ if (!vc)
+ return;
+
+ g_free(vc->obj_path);
+ g_free(vc->number);
+ g_free(vc);
+}
+
+static void handle_vc_property_changed(DBusMessage *msg, const char *obj_path)
+{
+ DBusMessageIter iter, sub;
+ const char *property, *state;
+ struct voice_call *vc = NULL;
+
+ debug("in handle_vc_property_changed, obj_path is %s", obj_path);
+
+ vc = find_vc(obj_path);
+
+ if (!vc)
+ return;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+ error("Unexpected signature in vc PropertyChanged signal");
+ return;
+ }
+
+ dbus_message_iter_get_basic(&iter, &property);
+ debug("in handle_vc_property_changed(), the property is %s", property);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &sub);
+ if (g_str_equal(property, "State")) {
+ dbus_message_iter_get_basic(&sub, &state);
+ debug("in handle_vc_property_changed(), State is %s", state);
+ if (g_str_equal(state, "disconnected")) {
+ printf("in disconnected case\n");
+ if (vc->status == CALL_STATUS_ACTIVE)
+ telephony_update_indicator(ofono_indicators,
+ "call", EV_CALL_INACTIVE);
+ else
+ telephony_update_indicator(ofono_indicators,
+ "callsetup", EV_CALLSETUP_INACTIVE);
+ if (vc->status == CALL_STATUS_INCOMING)
+ telephony_calling_stopped_ind();
+ calls = g_slist_remove(calls, vc);
+ vc_free(vc);
+ } else if (g_str_equal(state, "active")) {
+ telephony_update_indicator(ofono_indicators,
+ "call", EV_CALL_ACTIVE);
+ telephony_update_indicator(ofono_indicators,
+ "callsetup",
+ EV_CALLSETUP_INACTIVE);
+ if (vc->status == CALL_STATUS_INCOMING) {
+ telephony_calling_stopped_ind();
+ }
+ vc->status = CALL_STATUS_ACTIVE;
+ debug("vc status is CALL_STATUS_ACTIVE");
+ } else if (g_str_equal(state, "alerting")) {
+ telephony_update_indicator(ofono_indicators,
+ "callsetup", EV_CALLSETUP_ALERTING);
+ vc->status = CALL_STATUS_ALERTING;
+ debug("vc status is CALL_STATUS_ALERTING");
+ } else if (g_str_equal(state, "incoming")) {
+ /* state change from waiting to incoming */
+ telephony_update_indicator(ofono_indicators,
+ "callsetup", EV_CALLSETUP_INCOMING);
+ telephony_incoming_call_ind(vc->number,
+ NUMBER_TYPE_TELEPHONY);
+ vc->status = CALL_STATUS_INCOMING;
+ debug("vc status is CALL_STATUS_INCOMING");
+ }
+ }
+}
+
+static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply;
+ DBusError err;
+ dbus_int32_t level;
+ int *value = user_data;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("hald replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_INT32, &level,
+ DBUS_TYPE_INVALID);
+
+ *value = (int) level;
+
+ if (value == &battchg_last)
+ debug("telephony-ofono: battery.charge_level.last_full"
+ " is %d", *value);
+ else if (value == &battchg_design)
+ debug("telephony-ofono: battery.charge_level.design"
+ " is %d", *value);
+ else
+ debug("telephony-ofono: battery.charge_level.current"
+ " is %d", *value);
+
+ if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
+ int new, cur, max;
+
+ if (battchg_last > 0)
+ max = battchg_last;
+ else
+ max = battchg_design;
+
+ cur = telephony_get_indicator(ofono_indicators, "battchg");
+ new = battchg_cur * 5 / max;
+
+ if (new != cur)
+ telephony_update_indicator(ofono_indicators,
+ "battchg", new);
+ }
+done:
+ dbus_message_unref(reply);
+}
+
+static void hal_get_integer(const char *path, const char *key, void *user_data)
+{
+ send_method_call("org.freedesktop.Hal", path,
+ "org.freedesktop.Hal.Device",
+ "GetPropertyInteger",
+ hal_battery_level_reply, user_data,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_INVALID);
+}
+
+static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply;
+ DBusError err;
+ DBusMessageIter iter, sub;
+ int type;
+ const char *path;
+ char match_string[256];
+
+ debug("begin of hal_find_device_reply()");
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("hald replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in hal_find_device_reply()");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ type = dbus_message_iter_get_arg_type(&sub);
+
+ if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
+ error("No hal device with battery capability found");
+ goto done;
+ }
+
+ dbus_message_iter_get_basic(&sub, &path);
+
+ debug("telephony-ofono: found battery device at %s", path);
+
+ snprintf(match_string, sizeof(match_string),
+ "type='signal',"
+ "path='%s',"
+ "interface='org.freedesktop.Hal.Device',"
+ "member='PropertyModified'", path);
+ dbus_bus_add_match(connection, match_string, NULL);
+
+ hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
+ hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
+ hal_get_integer(path, "battery.charge_level.design", &battchg_design);
+done:
+ dbus_message_unref(reply);
+}
+
+static void handle_hal_property_modified(DBusMessage *msg)
+{
+ const char *path;
+ DBusMessageIter iter, array;
+ dbus_int32_t num_changes;
+
+ path = dbus_message_get_path(msg);
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
+ error("Unexpected signature in hal PropertyModified signal");
+ return;
+ }
+
+ dbus_message_iter_get_basic(&iter, &num_changes);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in hal PropertyModified signal");
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+
+ while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
+ DBusMessageIter prop;
+ const char *name;
+ dbus_bool_t added, removed;
+
+ dbus_message_iter_recurse(&array, &prop);
+
+ if (!iter_get_basic_args(&prop,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_BOOLEAN, &added,
+ DBUS_TYPE_BOOLEAN, &removed,
+ DBUS_TYPE_INVALID)) {
+ error("Invalid hal PropertyModified parameters");
+ break;
+ }
+
+ if (g_str_equal(name, "battery.charge_level.last_full"))
+ hal_get_integer(path, name, &battchg_last);
+ else if (g_str_equal(name, "battery.charge_level.current"))
+ hal_get_integer(path, name, &battchg_cur);
+ else if (g_str_equal(name, "battery.charge_level.design"))
+ hal_get_integer(path, name, &battchg_design);
+
+ dbus_message_iter_next(&array);
+ }
+}
+
+static DBusHandlerResult signal_filter(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *path = dbus_message_get_path(msg);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (dbus_message_is_signal(msg, OFONO_NETWORKREG_INTERFACE,
+ "PropertyChanged"))
+ handle_networkregistration_property_changed(msg, path);
+ else if (dbus_message_is_signal(msg, OFONO_VCMANAGER_INTERFACE,
+ "PropertyChanged"))
+ handle_vcmanager_property_changed(msg, path);
+ else if (dbus_message_is_signal(msg, OFONO_VC_INTERFACE,
+ "PropertyChanged"))
+ handle_vc_property_changed(msg, path);
+ else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device",
+ "PropertyModified"))
+ handle_hal_property_modified(msg);
+
+ debug("signal_filter is called, path is %s\n", path);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int telephony_init(void)
+{
+ const char *battery_cap = "battery";
+ char match_string[128];
+ int ret;
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+ if (!dbus_connection_add_filter(connection, signal_filter,
+ NULL, NULL)) {
+ error("telephony-ofono: Can't add signal filter");
+ return -EIO;
+ }
+
+ snprintf(match_string, sizeof(match_string), "type=signal,interface=%s",
+ OFONO_NETWORKREG_INTERFACE);
+ dbus_bus_add_match(connection, match_string, NULL);
+
+ snprintf(match_string, sizeof(match_string), "type=signal,interface=%s",
+ OFONO_VCMANAGER_INTERFACE);
+ dbus_bus_add_match(connection, match_string, NULL);
+
+ snprintf(match_string, sizeof(match_string), "type=signal,interface=%s",
+ OFONO_VC_INTERFACE);
+ dbus_bus_add_match(connection, match_string, NULL);
+
+ ret = send_method_call(OFONO_BUS_NAME, OFONO_PATH,
+ OFONO_MANAGER_INTERFACE, "GetProperties",
+ list_modem_reply, NULL, DBUS_TYPE_INVALID);
+ if (ret < 0)
+ return ret;
+
+ ret = send_method_call("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/Manager",
+ "org.freedesktop.Hal.Manager",
+ "FindDeviceByCapability",
+ hal_find_device_reply, NULL,
+ DBUS_TYPE_STRING, &battery_cap,
+ DBUS_TYPE_INVALID);
+ if (ret < 0)
+ return ret;
+
+ debug("telephony_init() successfully");
+
+ return ret;
+}
+
+void telephony_exit(void)
+{
+ g_free(net.operator_name);
+
+ g_free(modem_obj_path);
+ g_free(last_dialed_number);
+
+ g_slist_foreach(calls, (GFunc) vc_free, NULL);
+ g_slist_free(calls);
+ calls = NULL;
+
+ dbus_connection_remove_filter(connection, signal_filter, NULL);
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
--
1.5.4.5
Hi Forrest,
> ---
> audio/Makefile.am | 3 +-
> audio/telephony-ofono.c | 1116 +++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 1118 insertions(+), 1 deletions(-)
> create mode 100644 audio/telephony-ofono.c
patch has been applied, but as mentioned on IRC, you need to use
./bootstrap-configure --with-telephony=ofono
to ensure that no GCC warnings are present anymore :)
Regards
Marcel
>
> On Wed, May 13, 2009, Forrest Zhao wrote:
>> +static int send_method_call(const char *dest, const char *path,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?const char *interface, const char *method,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?DBusPendingCallNotifyFunction cb,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?void *user_data, int type, ...);
>
> I don't think there should be any need for this forward declaration. Just
> move the function higher up in the file.
OK.
>> +static void answer_reply(DBusPendingCall *call, void *user_data)
>> +{
>> + ? ? DBusMessage *reply;
>> + ? ? DBusError err;
>> +
>> + ? ? reply = dbus_pending_call_steal_reply(call);
>> + ? ? dbus_error_init(&err);
>> + ? ? if (dbus_set_error_from_message(&err, reply)) {
>> + ? ? ? ? ? ? error("oFono answer incoming call failed: %s, %s",
>> + ? ? ? ? ? ? ? ? ? ? err.name, err.message);
>> + ? ? ? ? ? ? dbus_error_free(&err);
>> + ? ? ? ? ? ? goto done;
>> + ? ? }
>> +
>> +done:
>> + ? ? dbus_message_unref(reply);
>> +}
>> +
>> +void telephony_answer_call_req(void *telephony_device)
>> +{
>> + ? ? struct voice_call *vc = find_vc_with_status(CALL_STATUS_INCOMING);
>> + ? ? int ret;
>> +
>> + ? ? if (!vc) {
>> + ? ? ? ? ? ? telephony_answer_call_rsp(telephony_device,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CME_ERROR_NOT_ALLOWED);
>> + ? ? ? ? ? ? return;
>> + ? ? }
>> +
>> + ? ? ret = send_method_call(OFONO_BUS_NAME, vc->obj_path,
>> + ? ? ? ? ? ? ? ? ? ? OFONO_VC_INTERFACE,
>> + ? ? ? ? ? ? ? ? ? ? "Answer", answer_reply,
>> + ? ? ? ? ? ? ? ? ? ? NULL, DBUS_TYPE_INVALID);
>> +
>> + ? ? if (ret < 0) {
>> + ? ? ? ? ? ? telephony_answer_call_rsp(telephony_device,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CME_ERROR_AG_FAILURE);
>> + ? ? ? ? ? ? return;
>> + ? ? }
>> +
>> + ? ? telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
>> +}
>
> If you're actually going to handle the D-Bus reply to the Answer method
> call (which the maemo driver doesn't do -- it relies on the state signals
> instead), doesn't it make sense to call telephony_answer_call_rsp in the
> reply handler (answer_reply) instead of here?
After reviewing the patch I think I should remove answer_reply() because
it also depends on the state signals to know if "answer" actually succeed.
Thanks,
Forrest
Hi Forrest,
In general the patch looks ok'ish (which it should since it seems to be
largely based on the maemo driver), though I do have a couple of comments:
On Wed, May 13, 2009, Forrest Zhao wrote:
> +static int send_method_call(const char *dest, const char *path,
> + const char *interface, const char *method,
> + DBusPendingCallNotifyFunction cb,
> + void *user_data, int type, ...);
I don't think there should be any need for this forward declaration. Just
move the function higher up in the file.
> +static void answer_reply(DBusPendingCall *call, void *user_data)
> +{
> + DBusMessage *reply;
> + DBusError err;
> +
> + reply = dbus_pending_call_steal_reply(call);
> + dbus_error_init(&err);
> + if (dbus_set_error_from_message(&err, reply)) {
> + error("oFono answer incoming call failed: %s, %s",
> + err.name, err.message);
> + dbus_error_free(&err);
> + goto done;
> + }
> +
> +done:
> + dbus_message_unref(reply);
> +}
> +
> +void telephony_answer_call_req(void *telephony_device)
> +{
> + struct voice_call *vc = find_vc_with_status(CALL_STATUS_INCOMING);
> + int ret;
> +
> + if (!vc) {
> + telephony_answer_call_rsp(telephony_device,
> + CME_ERROR_NOT_ALLOWED);
> + return;
> + }
> +
> + ret = send_method_call(OFONO_BUS_NAME, vc->obj_path,
> + OFONO_VC_INTERFACE,
> + "Answer", answer_reply,
> + NULL, DBUS_TYPE_INVALID);
> +
> + if (ret < 0) {
> + telephony_answer_call_rsp(telephony_device,
> + CME_ERROR_AG_FAILURE);
> + return;
> + }
> +
> + telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
> +}
If you're actually going to handle the D-Bus reply to the Answer method
call (which the maemo driver doesn't do -- it relies on the state signals
instead), doesn't it make sense to call telephony_answer_call_rsp in the
reply handler (answer_reply) instead of here?
Johan