summaryrefslogtreecommitdiff
path: root/audio/telephony-ofono.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/telephony-ofono.c')
-rw-r--r--audio/telephony-ofono.c1627
1 files changed, 1627 insertions, 0 deletions
diff --git a/audio/telephony-ofono.c b/audio/telephony-ofono.c
new file mode 100644
index 0000000..6f5685b
--- /dev/null
+++ b/audio/telephony-ofono.c
@@ -0,0 +1,1627 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2009-2010 Intel Corporation
+ * Copyright (C) 2006-2009 Nokia Corporation
+ * Copyright (C) 2004-2010 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 "log.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;
+ gboolean conference;
+ char *number;
+ guint watch;
+};
+
+static DBusConnection *connection = NULL;
+static char *modem_obj_path = NULL;
+static char *last_dialed_number = NULL;
+static GSList *calls = NULL;
+static GSList *watches = NULL;
+static GSList *pending = NULL;
+
+#define OFONO_BUS_NAME "org.ofono"
+#define OFONO_PATH "/"
+#define OFONO_MODEM_INTERFACE "org.ofono.Modem"
+#define OFONO_MANAGER_INTERFACE "org.ofono.Manager"
+#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;
+
+static struct indicator ofono_indicators[] =
+{
+ { "battchg", "0-5", 5, TRUE },
+ { "signal", "0-5", 5, TRUE },
+ { "service", "0,1", 1, TRUE },
+ { "call", "0,1", 0, TRUE },
+ { "callsetup", "0-3", 0, TRUE },
+ { "callheld", "0-2", 0, FALSE },
+ { "roam", "0,1", 0, TRUE },
+ { 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 struct voice_call *find_vc_without_status(int status)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct voice_call *call = l->data;
+
+ if (call->status != status)
+ return call;
+ }
+
+ return NULL;
+}
+
+static int number_type(const char *number)
+{
+ if (number == NULL)
+ return NUMBER_TYPE_TELEPHONY;
+
+ if (number[0] == '+' || strncmp(number, "00", 2) == 0)
+ return NUMBER_TYPE_INTERNATIONAL;
+
+ return NUMBER_TYPE_TELEPHONY;
+}
+
+void telephony_device_connected(void *telephony_device)
+{
+ struct voice_call *coming;
+
+ DBG("telephony-ofono: device %p connected", telephony_device);
+
+ coming = find_vc_with_status(CALL_STATUS_ALERTING);
+ if (coming) {
+ if (find_vc_with_status(CALL_STATUS_ACTIVE))
+ telephony_call_waiting_ind(coming->number,
+ number_type(coming->number));
+ else
+ telephony_incoming_call_ind(coming->number,
+ number_type(coming->number));
+ }
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+ DBG("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)
+{
+ telephony_response_and_hold_rsp(telephony_device,
+ CME_ERROR_NOT_SUPPORTED);
+}
+
+void telephony_last_dialed_number_req(void *telephony_device)
+{
+ DBG("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);
+ pending = g_slist_prepend(pending, call);
+ dbus_message_unref(msg);
+
+ return 0;
+}
+
+static int answer_call(struct voice_call *vc)
+{
+ DBG("%s", vc->number);
+ return send_method_call(OFONO_BUS_NAME, vc->obj_path,
+ OFONO_VC_INTERFACE, "Answer",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int release_call(struct voice_call *vc)
+{
+ DBG("%s", vc->number);
+ return send_method_call(OFONO_BUS_NAME, vc->obj_path,
+ OFONO_VC_INTERFACE, "Hangup",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int release_answer_calls()
+{
+ DBG("");
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "ReleaseAndAnswer",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int split_call(struct voice_call *call)
+{
+ DBG("%s", call->number);
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "PrivateChat",
+ NULL, NULL,
+ DBUS_TYPE_OBJECT_PATH,
+ call->obj_path,
+ DBUS_TYPE_INVALID);
+ return -1;
+}
+
+static int swap_calls(void)
+{
+ DBG("");
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "SwapCalls",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int create_conference(void)
+{
+ DBG("");
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "CreateMultiparty",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int release_conference(void)
+{
+ DBG("");
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "HangupMultiparty",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int call_transfer(void)
+{
+ DBG("");
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "Transfer",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+void telephony_terminate_call_req(void *telephony_device)
+{
+ struct voice_call *call;
+ struct voice_call *alerting;
+ int err;
+
+ call = find_vc_with_status(CALL_STATUS_ACTIVE);
+ if (!call)
+ call = calls->data;
+
+ if (!call) {
+ error("No active call");
+ telephony_terminate_call_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+ return;
+ }
+
+ alerting = find_vc_with_status(CALL_STATUS_ALERTING);
+ if (call->status == CALL_STATUS_HELD && alerting)
+ err = release_call(alerting);
+ else if (call->conference)
+ err = release_conference();
+ else
+ err = release_call(call);
+
+ if (err < 0)
+ telephony_terminate_call_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_answer_call_req(void *telephony_device)
+{
+ struct voice_call *vc;
+ int ret;
+
+ vc = find_vc_with_status(CALL_STATUS_INCOMING);
+ if (!vc)
+ vc = find_vc_with_status(CALL_STATUS_ALERTING);
+
+ if (!vc)
+ vc = find_vc_with_status(CALL_STATUS_WAITING);
+
+ if (!vc) {
+ telephony_answer_call_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+ return;
+ }
+
+ ret = answer_call(vc);
+ 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)
+{
+ const char *clir;
+ int ret;
+
+ DBG("telephony-ofono: dial request to %s", number);
+
+ if (!modem_obj_path) {
+ telephony_dial_number_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ if (!strncmp(number, "*31#", 4)) {
+ number += 4;
+ clir = "enabled";
+ } else if (!strncmp(number, "#31#", 4)) {
+ number += 4;
+ clir = "disabled";
+ } else
+ clir = "default";
+
+ 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;
+
+ DBG("telephony-ofono: transmit dtmf: %c", tone);
+
+ if (!modem_obj_path) {
+ telephony_transmit_dtmf_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ 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)
+{
+ DBG("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;
+
+ DBG("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, multiparty;
+
+ direction = vc->originating ?
+ CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
+
+ multiparty = vc->conference ?
+ CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
+
+ DBG("call %s direction %d multiparty %d", vc->number,
+ direction, multiparty);
+
+ telephony_list_current_call_ind(i, direction, vc->status,
+ CALL_MODE_VOICE, multiparty,
+ vc->number, number_type(vc->number));
+ }
+
+ telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_operator_selection_req(void *telephony_device)
+{
+ DBG("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);
+}
+
+static void foreach_vc_with_status(int status,
+ int (*func)(struct voice_call *vc))
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct voice_call *call = l->data;
+
+ if (call->status == status)
+ func(call);
+ }
+}
+
+void telephony_call_hold_req(void *telephony_device, const char *cmd)
+{
+ const char *idx;
+ struct voice_call *call;
+ int err = 0;
+
+ DBG("telephony-ofono: got call hold request %s", cmd);
+
+ if (strlen(cmd) > 1)
+ idx = &cmd[1];
+ else
+ idx = NULL;
+
+ if (idx)
+ call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
+ else
+ call = NULL;
+
+ switch (cmd[0]) {
+ case '0':
+ if (find_vc_with_status(CALL_STATUS_WAITING))
+ foreach_vc_with_status(CALL_STATUS_WAITING,
+ release_call);
+ else
+ foreach_vc_with_status(CALL_STATUS_HELD, release_call);
+ break;
+ case '1':
+ if (idx) {
+ if (call)
+ err = release_call(call);
+ break;
+ }
+ err = release_answer_calls();
+ break;
+ case '2':
+ if (idx) {
+ if (call)
+ err = split_call(call);
+ } else {
+ call = find_vc_with_status(CALL_STATUS_WAITING);
+
+ if (call)
+ err = answer_call(call);
+ else
+ err = swap_calls();
+ }
+ break;
+ case '3':
+ if (find_vc_with_status(CALL_STATUS_HELD) ||
+ find_vc_with_status(CALL_STATUS_WAITING))
+ err = create_conference();
+ break;
+ case '4':
+ err = call_transfer();
+ break;
+ default:
+ DBG("Unknown call hold request");
+ break;
+ }
+
+ if (err)
+ telephony_call_hold_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
+{
+ DBG("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)
+{
+ struct voice_call *active, *incoming;
+ int err;
+
+ DBG("telephony-ofono: got key press request for %s", keys);
+
+ incoming = find_vc_with_status(CALL_STATUS_INCOMING);
+
+ active = find_vc_with_status(CALL_STATUS_ACTIVE);
+
+ if (incoming)
+ err = answer_call(incoming);
+ else if (active)
+ err = release_call(active);
+ else
+ err = 0;
+
+ if (err < 0)
+ telephony_key_press_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_voice_dial_req(void *telephony_device, gboolean enable)
+{
+ DBG("telephony-ofono: got %s voice dial request",
+ enable ? "enable" : "disable");
+
+ telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
+}
+
+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;
+}
+
+static void call_free(struct voice_call *vc)
+{
+ DBG("%s", vc->obj_path);
+
+ 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();
+
+ g_dbus_remove_watch(connection, vc->watch);
+ g_free(vc->obj_path);
+ g_free(vc->number);
+ g_free(vc);
+}
+
+static gboolean handle_vc_property_changed(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct voice_call *vc = data;
+ const char *obj_path = dbus_message_get_path(msg);
+ DBusMessageIter iter, sub;
+ const char *property, *state;
+
+ DBG("path %s", obj_path);
+
+ 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 TRUE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &property);
+ DBG("property %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);
+ DBG("State %s", state);
+ if (g_str_equal(state, "disconnected")) {
+ calls = g_slist_remove(calls, vc);
+ call_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;
+ } else if (g_str_equal(state, "alerting")) {
+ telephony_update_indicator(ofono_indicators,
+ "callsetup", EV_CALLSETUP_ALERTING);
+ vc->status = CALL_STATUS_ALERTING;
+ vc->originating = TRUE;
+ } 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;
+ vc->originating = FALSE;
+ } else if (g_str_equal(state, "held")) {
+ vc->status = CALL_STATUS_HELD;
+ if (find_vc_without_status(CALL_STATUS_HELD))
+ telephony_update_indicator(ofono_indicators,
+ "callheld",
+ EV_CALLHELD_MULTIPLE);
+ else
+ telephony_update_indicator(ofono_indicators,
+ "callheld",
+ EV_CALLHELD_ON_HOLD);
+ }
+ } else if (g_str_equal(property, "Multiparty")) {
+ dbus_bool_t multiparty;
+
+ dbus_message_iter_get_basic(&sub, &multiparty);
+ DBG("Multiparty %s", multiparty ? "True" : "False");
+ vc->conference = multiparty;
+ }
+
+ return TRUE;
+}
+
+static struct voice_call *call_new(const char *path, DBusMessageIter *properties)
+{
+ struct voice_call *vc;
+
+ DBG("%s", path);
+
+ vc = g_new0(struct voice_call, 1);
+ vc->obj_path = g_strdup(path);
+ vc->watch = g_dbus_add_signal_watch(connection, NULL, path,
+ OFONO_VC_INTERFACE, "PropertyChanged",
+ handle_vc_property_changed, vc, NULL);
+
+ while (dbus_message_iter_get_arg_type(properties)
+ == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value;
+ const char *property, *cli, *state;
+ dbus_bool_t multiparty;
+
+ dbus_message_iter_recurse(properties, &entry);
+ dbus_message_iter_get_basic(&entry, &property);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (g_str_equal(property, "LineIdentification")) {
+ dbus_message_iter_get_basic(&value, &cli);
+ DBG("cli %s", cli);
+ vc->number = g_strdup(cli);
+ } else if (g_str_equal(property, "State")) {
+ dbus_message_iter_get_basic(&value, &state);
+ DBG("state %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;
+ else if (g_str_equal(state, "held"))
+ vc->status = CALL_STATUS_HELD;
+ } else if (g_str_equal(property, "Multiparty")) {
+ dbus_message_iter_get_basic(&value, &multiparty);
+ DBG("Multipary %s", multiparty ? "True" : "False");
+ vc->conference = multiparty;
+ }
+
+ dbus_message_iter_next(properties);
+ }
+
+ switch (vc->status) {
+ case CALL_STATUS_INCOMING:
+ DBG("CALL_STATUS_INCOMING");
+ 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:
+ DBG("CALL_STATUS_DIALING");
+ 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:
+ DBG("CALL_STATUS_ALERTING");
+ 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:
+ DBG("CALL_STATUS_WAITING");
+ vc->originating = FALSE;
+ telephony_update_indicator(ofono_indicators, "callsetup",
+ EV_CALLSETUP_INCOMING);
+ telephony_call_waiting_ind(vc->number, NUMBER_TYPE_TELEPHONY);
+ break;
+ }
+
+ return vc;
+}
+
+static void remove_pending(DBusPendingCall *call)
+{
+ pending = g_slist_remove(pending, call);
+ dbus_pending_call_unref(call);
+}
+
+static void call_added(const char *path, DBusMessageIter *properties)
+{
+ struct voice_call *vc;
+
+ DBG("%s", path);
+
+ vc = find_vc(path);
+ if (vc)
+ return;
+
+ vc = call_new(path, properties);
+ calls = g_slist_prepend(calls, vc);
+}
+
+static void get_calls_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, entry;
+
+ DBG("");
+ 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");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &entry);
+
+ while (dbus_message_iter_get_arg_type(&entry)
+ == DBUS_TYPE_STRUCT) {
+ const char *path;
+ DBusMessageIter value, properties;
+
+ dbus_message_iter_recurse(&entry, &value);
+ dbus_message_iter_get_basic(&value, &path);
+
+ dbus_message_iter_next(&value);
+ dbus_message_iter_recurse(&value, &properties);
+
+ call_added(path, &properties);
+
+ dbus_message_iter_next(&entry);
+ }
+
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static void handle_network_property(const char *property, DBusMessageIter *variant)
+{
+ const char *status, *operator;
+ unsigned int signals_bar;
+
+ if (g_str_equal(property, "Status")) {
+ dbus_message_iter_get_basic(variant, &status);
+ DBG("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, "Name")) {
+ dbus_message_iter_get_basic(variant, &operator);
+ DBG("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(variant, &signals_bar);
+ DBG("SignalStrength is %d", signals_bar);
+ net.signals_bar = signals_bar;
+ telephony_update_indicator(ofono_indicators, "signal",
+ (signals_bar + 20) / 21);
+ }
+}
+
+static int parse_network_properties(DBusMessageIter *properties)
+{
+ uint32_t features = AG_FEATURE_EC_ANDOR_NR |
+ AG_FEATURE_INBAND_RINGTONE |
+ AG_FEATURE_REJECT_A_CALL |
+ AG_FEATURE_ENHANCED_CALL_STATUS |
+ AG_FEATURE_ENHANCED_CALL_CONTROL |
+ AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
+ AG_FEATURE_THREE_WAY_CALLING;
+ int i;
+
+ /* Reset indicators */
+ for (i = 0; ofono_indicators[i].desc != NULL; i++) {
+ if (g_str_equal(ofono_indicators[i].desc, "battchg"))
+ ofono_indicators[i].val = 5;
+ else
+ ofono_indicators[i].val = 0;
+ }
+
+ while (dbus_message_iter_get_arg_type(properties)
+ == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+
+ dbus_message_iter_recurse(properties, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ handle_network_property(key, &value);
+
+ dbus_message_iter_next(properties);
+ }
+
+ telephony_ready_ind(features, ofono_indicators, BTRH_NOT_SUPPORTED,
+ chld_str);
+
+ return 0;
+}
+
+static void get_properties_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, properties;
+ int ret = 0;
+
+ DBG("");
+ 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");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &properties);
+
+ ret = parse_network_properties(&properties);
+ if (ret < 0) {
+ error("Unable to parse %s.GetProperty reply",
+ OFONO_NETWORKREG_INTERFACE);
+ goto done;
+ }
+
+ ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE, "GetCalls",
+ get_calls_reply, NULL, DBUS_TYPE_INVALID);
+ if (ret < 0)
+ error("Unable to send %s.GetCalls",
+ OFONO_VCMANAGER_INTERFACE);
+
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static void network_found(const char *path)
+{
+ int ret;
+
+ DBG("%s", path);
+
+ modem_obj_path = g_strdup(path);
+
+ ret = send_method_call(OFONO_BUS_NAME, path,
+ OFONO_NETWORKREG_INTERFACE, "GetProperties",
+ get_properties_reply, NULL, DBUS_TYPE_INVALID);
+ if (ret < 0)
+ error("Unable to send %s.GetProperties",
+ OFONO_NETWORKREG_INTERFACE);
+}
+
+static void modem_removed(const char *path)
+{
+ if (g_strcmp0(modem_obj_path, path) != 0)
+ return;
+
+ DBG("%s", path);
+
+ g_slist_foreach(calls, (GFunc) call_free, NULL);
+ g_slist_free(calls);
+ calls = NULL;
+
+ g_free(net.operator_name);
+ net.operator_name = NULL;
+ net.status = NETWORK_REG_STATUS_NOSERV;
+ net.signals_bar = 0;
+
+ g_free(modem_obj_path);
+ modem_obj_path = NULL;
+}
+
+static void parse_modem_interfaces(const char *path, DBusMessageIter *ifaces)
+{
+ DBG("%s", path);
+
+ while (dbus_message_iter_get_arg_type(ifaces) == DBUS_TYPE_STRING) {
+ const char *iface;
+
+ dbus_message_iter_get_basic(ifaces, &iface);
+
+ if (g_str_equal(iface, OFONO_NETWORKREG_INTERFACE)) {
+ network_found(path);
+ return;
+ }
+
+ dbus_message_iter_next(ifaces);
+ }
+
+ modem_removed(path);
+}
+
+static void modem_added(const char *path, DBusMessageIter *properties)
+{
+ if (modem_obj_path != NULL) {
+ DBG("Ignoring, modem already exist");
+ return;
+ }
+
+ DBG("%s", path);
+
+ while (dbus_message_iter_get_arg_type(properties)
+ == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter interfaces, value, entry;
+
+ dbus_message_iter_recurse(properties, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (strcasecmp(key, "Interfaces") != 0)
+ goto next;
+
+ if (dbus_message_iter_get_arg_type(&value)
+ != DBUS_TYPE_ARRAY) {
+ error("Invalid Signature");
+ return;
+ }
+
+ dbus_message_iter_recurse(&value, &interfaces);
+
+ parse_modem_interfaces(path, &interfaces);
+
+ if (modem_obj_path != NULL)
+ return;
+
+ next:
+ dbus_message_iter_next(properties);
+ }
+}
+
+static void get_modems_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, entry;
+
+ DBG("");
+ 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;
+ }
+
+ /* Skip modem selection if a modem already exist */
+ if (modem_obj_path != NULL)
+ goto done;
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &entry);
+
+ while (dbus_message_iter_get_arg_type(&entry)
+ == DBUS_TYPE_STRUCT) {
+ const char *path;
+ DBusMessageIter item, properties;
+
+ dbus_message_iter_recurse(&entry, &item);
+ dbus_message_iter_get_basic(&item, &path);
+
+ dbus_message_iter_next(&item);
+ dbus_message_iter_recurse(&item, &properties);
+
+ modem_added(path, &properties);
+ if (modem_obj_path != NULL)
+ break;
+
+ dbus_message_iter_next(&entry);
+ }
+
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static gboolean handle_network_property_changed(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter, variant;
+ 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 TRUE;
+ }
+ dbus_message_iter_get_basic(&iter, &property);
+ DBG("in handle_registration_property_changed(),"
+ " the property is %s", property);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &variant);
+
+ handle_network_property(property, &variant);
+
+ return TRUE;
+}
+
+static void handle_modem_property(const char *path, const char *property,
+ DBusMessageIter *variant)
+{
+ DBG("%s", property);
+
+ if (g_str_equal(property, "Interfaces")) {
+ DBusMessageIter interfaces;
+
+ if (dbus_message_iter_get_arg_type(variant)
+ != DBUS_TYPE_ARRAY) {
+ error("Invalid signature");
+ return;
+ }
+
+ dbus_message_iter_recurse(variant, &interfaces);
+ parse_modem_interfaces(path, &interfaces);
+ }
+}
+
+static gboolean handle_modem_property_changed(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter, variant;
+ const char *property, *path;
+
+ path = dbus_message_get_path(msg);
+
+ /* Ignore if modem already exist and paths doesn't match */
+ if (modem_obj_path != NULL &&
+ g_str_equal(path, modem_obj_path) == FALSE)
+ return TRUE;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+ error("Unexpected signature in %s.%s PropertyChanged signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ return TRUE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &property);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &variant);
+
+ handle_modem_property(path, property, &variant);
+
+ return TRUE;
+}
+
+static gboolean handle_vcmanager_call_added(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter, properties;
+ const char *path = dbus_message_get_path(msg);
+
+ /* Ignore call if modem path doesn't math */
+ if (g_strcmp0(modem_obj_path, path) != 0)
+ return TRUE;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter)
+ != DBUS_TYPE_OBJECT_PATH) {
+ error("Unexpected signature in %s.%s signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ return TRUE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &path);
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &properties);
+
+ call_added(path, &properties);
+
+ return TRUE;
+}
+
+static void call_removed(const char *path)
+{
+ struct voice_call *vc;
+
+ DBG("%s", path);
+
+ vc = find_vc(path);
+ if (vc == NULL)
+ return;
+
+ calls = g_slist_remove(calls, vc);
+ call_free(vc);
+}
+
+static gboolean handle_vcmanager_call_removed(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *path = dbus_message_get_path(msg);
+
+ /* Ignore call if modem path doesn't math */
+ if (g_strcmp0(modem_obj_path, path) != 0)
+ return TRUE;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected signature in %s.%s signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ return TRUE;
+ }
+
+ call_removed(path);
+
+ return TRUE;
+}
+
+static gboolean handle_manager_modem_added(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter, properties;
+ const char *path;
+
+ if (modem_obj_path != NULL)
+ return TRUE;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter)
+ != DBUS_TYPE_OBJECT_PATH) {
+ error("Unexpected signature in %s.%s signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ return TRUE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &path);
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &properties);
+
+ modem_added(path, &properties);
+
+ return TRUE;
+}
+
+static gboolean handle_manager_modem_removed(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *path;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected signature in %s.%s signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ return TRUE;
+ }
+
+ modem_removed(path);
+
+ return TRUE;
+}
+
+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_error_init(&err);
+ if (dbus_message_get_args(reply, &err,
+ DBUS_TYPE_INT32, &level,
+ DBUS_TYPE_INVALID) == FALSE) {
+ error("Unable to parse GetPropertyInteger reply: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ *value = (int) level;
+
+ if (value == &battchg_last)
+ DBG("telephony-ofono: battery.charge_level.last_full"
+ " is %d", *value);
+ else if (value == &battchg_design)
+ DBG("telephony-ofono: battery.charge_level.design"
+ " is %d", *value);
+ else
+ DBG("telephony-ofono: battery.charge_level.current"
+ " is %d", *value);
+
+ if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
+ int new, max;
+
+ if (battchg_last > 0)
+ max = battchg_last;
+ else
+ max = battchg_design;
+
+ new = battchg_cur * 5 / max;
+
+ telephony_update_indicator(ofono_indicators, "battchg", new);
+ }
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+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 gboolean handle_hal_property_modified(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ 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 TRUE;
+ }
+
+ 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 TRUE;
+ }
+
+ 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);
+ }
+
+ return TRUE;
+}
+
+static void add_watch(const char *sender, const char *path,
+ const char *interface, const char *member,
+ GDBusSignalFunction function)
+{
+ guint watch;
+
+ watch = g_dbus_add_signal_watch(connection, sender, path, interface,
+ member, function, NULL, NULL);
+
+ watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
+}
+
+static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply;
+ DBusError err;
+ DBusMessageIter iter, sub;
+ int type;
+ const char *path;
+
+ DBG("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);
+
+ DBG("telephony-ofono: found battery device at %s", path);
+
+ add_watch(NULL, path, "org.freedesktop.Hal.Device",
+ "PropertyModified", handle_hal_property_modified);
+
+ 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);
+ remove_pending(call);
+}
+
+static void handle_service_connect(DBusConnection *conn, void *user_data)
+{
+ DBG("telephony-ofono: %s found", OFONO_BUS_NAME);
+
+ send_method_call(OFONO_BUS_NAME, OFONO_PATH,
+ OFONO_MANAGER_INTERFACE, "GetModems",
+ get_modems_reply, NULL, DBUS_TYPE_INVALID);
+}
+
+static void handle_service_disconnect(DBusConnection *conn, void *user_data)
+{
+ DBG("telephony-ofono: %s exitted", OFONO_BUS_NAME);
+
+ if (modem_obj_path)
+ modem_removed(modem_obj_path);
+}
+
+int telephony_init(void)
+{
+ const char *battery_cap = "battery";
+ int ret;
+ guint watch;
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_MODEM_INTERFACE,
+ "PropertyChanged", handle_modem_property_changed);
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_NETWORKREG_INTERFACE,
+ "PropertyChanged", handle_network_property_changed);
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_MANAGER_INTERFACE,
+ "ModemAdded", handle_manager_modem_added);
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_MANAGER_INTERFACE,
+ "ModemRemoved", handle_manager_modem_removed);
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_VCMANAGER_INTERFACE,
+ "CallAdded", handle_vcmanager_call_added);
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_VCMANAGER_INTERFACE,
+ "CallRemoved", handle_vcmanager_call_removed);
+
+ watch = g_dbus_add_service_watch(connection, OFONO_BUS_NAME,
+ handle_service_connect,
+ handle_service_disconnect,
+ NULL, NULL);
+ if (watch == 0)
+ return -ENOMEM;
+
+ watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
+
+ 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;
+
+ DBG("telephony_init() successfully");
+
+ return ret;
+}
+
+static void remove_watch(gpointer data)
+{
+ g_dbus_remove_watch(connection, GPOINTER_TO_UINT(data));
+}
+
+void telephony_exit(void)
+{
+ DBG("");
+
+ g_free(last_dialed_number);
+ last_dialed_number = NULL;
+
+ if (modem_obj_path)
+ modem_removed(modem_obj_path);
+
+ g_slist_foreach(watches, (GFunc) remove_watch, NULL);
+ g_slist_free(watches);
+ watches = NULL;
+
+ g_slist_foreach(pending, (GFunc) dbus_pending_call_cancel, NULL);
+ g_slist_foreach(pending, (GFunc) dbus_pending_call_unref, NULL);
+ g_slist_free(pending);
+ pending = NULL;
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+
+ telephony_deinit();
+}