| /* |
| * |
| * 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(void) |
| { |
| 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(); |
| } |