All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] add support for HFP plugin for oFono(ofono.org)
@ 2009-05-13  2:55 Forrest Zhao
  2009-05-13  6:49 ` Johan Hedberg
  0 siblings, 1 reply; 5+ messages in thread
From: Forrest Zhao @ 2009-05-13  2:55 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: forrest.zhao, Forrest Zhao

Compared with last version, the file name is changed from
telephony-oFono.c to telephony-ofono.c.

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..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..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 <marcel@holtmann.org>
+ *
+ *
+ *  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


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] add support for HFP plugin for oFono(ofono.org)
  2009-05-13  2:55 [PATCH] add support for HFP plugin for oFono(ofono.org) Forrest Zhao
@ 2009-05-13  6:49 ` Johan Hedberg
  2009-05-13  7:02   ` Zhao Forrest
  0 siblings, 1 reply; 5+ messages in thread
From: Johan Hedberg @ 2009-05-13  6:49 UTC (permalink / raw)
  To: Forrest Zhao; +Cc: linux-bluetooth, forrest.zhao

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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] add support for HFP plugin for oFono(ofono.org)
  2009-05-13  6:49 ` Johan Hedberg
@ 2009-05-13  7:02   ` Zhao Forrest
  0 siblings, 0 replies; 5+ messages in thread
From: Zhao Forrest @ 2009-05-13  7:02 UTC (permalink / raw)
  To: Forrest Zhao, linux-bluetooth, forrest.zhao

>
> 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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] add support for HFP plugin for oFono(ofono.org)
  2009-05-13  7:20 Forrest Zhao
@ 2009-05-13  7:40 ` Marcel Holtmann
  0 siblings, 0 replies; 5+ messages in thread
From: Marcel Holtmann @ 2009-05-13  7:40 UTC (permalink / raw)
  To: Forrest Zhao; +Cc: linux-bluetooth

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



^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH] add support for HFP plugin for oFono(ofono.org)
@ 2009-05-13  7:20 Forrest Zhao
  2009-05-13  7:40 ` Marcel Holtmann
  0 siblings, 1 reply; 5+ messages in thread
From: Forrest Zhao @ 2009-05-13  7:20 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Forrest Zhao

---
 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 <marcel@holtmann.org>
+ *
+ *
+ *  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


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2009-05-13  7:40 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-05-13  2:55 [PATCH] add support for HFP plugin for oFono(ofono.org) Forrest Zhao
2009-05-13  6:49 ` Johan Hedberg
2009-05-13  7:02   ` Zhao Forrest
2009-05-13  7:20 Forrest Zhao
2009-05-13  7:40 ` Marcel Holtmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.