Hi Marcel, Johan,
The attached the patch implement the HFP plugin for oFono(ofono.org).
Your comments are welcome.
Thanks,
Forrest
---
audio/Makefile.am | 3 +-
audio/telephony-oFono.c | 1140 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1142 insertions(+), 1 deletions(-)
create mode 100644 audio/telephony-oFono.c
diff --git a/audio/Makefile.am b/audio/Makefile.am
index 67c75ba..01e8910 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..53d93e0
--- /dev/null
+++ b/audio/telephony-oFono.c
@@ -0,0 +1,1140 @@
+/*
+ *
+ * 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, ...);
+
+
+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);
+}
+
+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);
+}
+
+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 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;
+}
+
+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
>
> please call the file telephony-ofono.c since the oFono writing if you
> use it withing written text references. Filenames and variables should
> write if ofono.
OK. A revised patch will be sent out soon.
> Once merged you are responsible for making sure the compilation always
> works. I am only testing the dummy code during release engineering.
Yes, I'll take this responsibility.
Forrest
Hi Forrest,
> The attached the patch implement the HFP plugin for oFono(ofono.org).
> Your comments are welcome.
please call the file telephony-ofono.c since the oFono writing if you
use it withing written text references. Filenames and variables should
write if ofono.
Once merged you are responsible for making sure the compilation always
works. I am only testing the dummy code during release engineering.
Regards
Marcel