Home | History | Annotate | Download | only in audio
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2009-2010  Intel Corporation
      6  *  Copyright (C) 2006-2009  Nokia Corporation
      7  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel (at) holtmann.org>
      8  *
      9  *
     10  *  This program is free software; you can redistribute it and/or modify
     11  *  it under the terms of the GNU General Public License as published by
     12  *  the Free Software Foundation; either version 2 of the License, or
     13  *  (at your option) any later version.
     14  *
     15  *  This program is distributed in the hope that it will be useful,
     16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18  *  GNU General Public License for more details.
     19  *
     20  *  You should have received a copy of the GNU General Public License
     21  *  along with this program; if not, write to the Free Software
     22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     23  *
     24  */
     25 
     26 #ifdef HAVE_CONFIG_H
     27 #include <config.h>
     28 #endif
     29 
     30 #include <stdlib.h>
     31 #include <stdio.h>
     32 #include <string.h>
     33 #include <stdint.h>
     34 #include <glib.h>
     35 #include <dbus/dbus.h>
     36 #include <gdbus.h>
     37 
     38 #include "log.h"
     39 #include "telephony.h"
     40 
     41 enum net_registration_status {
     42 	NETWORK_REG_STATUS_HOME = 0x00,
     43 	NETWORK_REG_STATUS_ROAM,
     44 	NETWORK_REG_STATUS_NOSERV
     45 };
     46 
     47 struct voice_call {
     48 	char *obj_path;
     49 	int status;
     50 	gboolean originating;
     51 	char *number;
     52 	guint watch;
     53 };
     54 
     55 static DBusConnection *connection = NULL;
     56 static char *modem_obj_path = NULL;
     57 static char *last_dialed_number = NULL;
     58 static GSList *calls = NULL;
     59 
     60 #define OFONO_BUS_NAME "org.ofono"
     61 #define OFONO_PATH "/"
     62 #define OFONO_MANAGER_INTERFACE "org.ofono.Manager"
     63 #define OFONO_NETWORKREG_INTERFACE "org.ofono.NetworkRegistration"
     64 #define OFONO_VCMANAGER_INTERFACE "org.ofono.VoiceCallManager"
     65 #define OFONO_VC_INTERFACE "org.ofono.VoiceCall"
     66 
     67 static guint registration_watch = 0;
     68 static guint voice_watch = 0;
     69 static guint device_watch = 0;
     70 
     71 /* HAL battery namespace key values */
     72 static int battchg_cur = -1;    /* "battery.charge_level.current" */
     73 static int battchg_last = -1;   /* "battery.charge_level.last_full" */
     74 static int battchg_design = -1; /* "battery.charge_level.design" */
     75 
     76 static struct {
     77 	uint8_t status;
     78 	uint32_t signals_bar;
     79 	char *operator_name;
     80 } net = {
     81 	.status = NETWORK_REG_STATUS_NOSERV,
     82 	.signals_bar = 0,
     83 	.operator_name = NULL,
     84 };
     85 
     86 static const char *chld_str = "0,1,1x,2,2x,3,4";
     87 static char *subscriber_number = NULL;
     88 
     89 static gboolean events_enabled = FALSE;
     90 
     91 /* Response and hold state
     92  * -1 = none
     93  *  0 = incoming call is put on hold in the AG
     94  *  1 = held incoming call is accepted in the AG
     95  *  2 = held incoming call is rejected in the AG
     96  */
     97 static int response_and_hold = -1;
     98 
     99 static struct indicator ofono_indicators[] =
    100 {
    101 	{ "battchg",	"0-5",	5,	TRUE },
    102 	{ "signal",	"0-5",	5,	TRUE },
    103 	{ "service",	"0,1",	1,	TRUE },
    104 	{ "call",	"0,1",	0,	TRUE },
    105 	{ "callsetup",	"0-3",	0,	TRUE },
    106 	{ "callheld",	"0-2",	0,	FALSE },
    107 	{ "roam",	"0,1",	0,	TRUE },
    108 	{ NULL }
    109 };
    110 
    111 static struct voice_call *find_vc(const char *path)
    112 {
    113 	GSList *l;
    114 
    115 	for (l = calls; l != NULL; l = l->next) {
    116 		struct voice_call *vc = l->data;
    117 
    118 		if (g_str_equal(vc->obj_path, path))
    119 			return vc;
    120 	}
    121 
    122 	return NULL;
    123 }
    124 
    125 static struct voice_call *find_vc_with_status(int status)
    126 {
    127 	GSList *l;
    128 
    129 	for (l = calls; l != NULL; l = l->next) {
    130 		struct voice_call *vc = l->data;
    131 
    132 		if (vc->status == status)
    133 			return vc;
    134 	}
    135 
    136 	return NULL;
    137 }
    138 
    139 void telephony_device_connected(void *telephony_device)
    140 {
    141 	DBG("telephony-ofono: device %p connected", telephony_device);
    142 }
    143 
    144 void telephony_device_disconnected(void *telephony_device)
    145 {
    146 	DBG("telephony-ofono: device %p disconnected", telephony_device);
    147 	events_enabled = FALSE;
    148 }
    149 
    150 void telephony_event_reporting_req(void *telephony_device, int ind)
    151 {
    152 	events_enabled = ind == 1 ? TRUE : FALSE;
    153 
    154 	telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
    155 }
    156 
    157 void telephony_response_and_hold_req(void *telephony_device, int rh)
    158 {
    159 	response_and_hold = rh;
    160 
    161 	telephony_response_and_hold_ind(response_and_hold);
    162 
    163 	telephony_response_and_hold_rsp(telephony_device, CME_ERROR_NONE);
    164 }
    165 
    166 void telephony_last_dialed_number_req(void *telephony_device)
    167 {
    168 	DBG("telephony-ofono: last dialed number request");
    169 
    170 	if (last_dialed_number)
    171 		telephony_dial_number_req(telephony_device, last_dialed_number);
    172 	else
    173 		telephony_last_dialed_number_rsp(telephony_device,
    174 				CME_ERROR_NOT_ALLOWED);
    175 }
    176 
    177 static int send_method_call(const char *dest, const char *path,
    178                                 const char *interface, const char *method,
    179                                 DBusPendingCallNotifyFunction cb,
    180                                 void *user_data, int type, ...)
    181 {
    182 	DBusMessage *msg;
    183 	DBusPendingCall *call;
    184 	va_list args;
    185 
    186 	msg = dbus_message_new_method_call(dest, path, interface, method);
    187 	if (!msg) {
    188 		error("Unable to allocate new D-Bus %s message", method);
    189 		return -ENOMEM;
    190 	}
    191 
    192 	va_start(args, type);
    193 
    194 	if (!dbus_message_append_args_valist(msg, type, args)) {
    195 		dbus_message_unref(msg);
    196 		va_end(args);
    197 		return -EIO;
    198 	}
    199 
    200 	va_end(args);
    201 
    202 	if (!cb) {
    203 		g_dbus_send_message(connection, msg);
    204 		return 0;
    205 	}
    206 
    207 	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
    208 		error("Sending %s failed", method);
    209 		dbus_message_unref(msg);
    210 		return -EIO;
    211 	}
    212 
    213 	dbus_pending_call_set_notify(call, cb, user_data, NULL);
    214 	dbus_pending_call_unref(call);
    215 	dbus_message_unref(msg);
    216 
    217 	return 0;
    218 }
    219 
    220 void telephony_terminate_call_req(void *telephony_device)
    221 {
    222 	struct voice_call *vc;
    223 	int ret;
    224 
    225 	if ((vc = find_vc_with_status(CALL_STATUS_ACTIVE))) {
    226 	} else if ((vc = find_vc_with_status(CALL_STATUS_DIALING))) {
    227 	} else if ((vc = find_vc_with_status(CALL_STATUS_ALERTING))) {
    228 	} else if ((vc = find_vc_with_status(CALL_STATUS_INCOMING))) {
    229 	}
    230 
    231 	if (!vc) {
    232 		error("in telephony_terminate_call_req, no active call");
    233 		telephony_terminate_call_rsp(telephony_device,
    234 					CME_ERROR_NOT_ALLOWED);
    235 		return;
    236 	}
    237 
    238 	ret = send_method_call(OFONO_BUS_NAME, vc->obj_path,
    239 					OFONO_VC_INTERFACE,
    240 					"Hangup", NULL,
    241 					NULL, DBUS_TYPE_INVALID);
    242 
    243 	if (ret < 0) {
    244 		telephony_answer_call_rsp(telephony_device,
    245 					CME_ERROR_AG_FAILURE);
    246 		return;
    247 	}
    248 
    249 	telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
    250 }
    251 
    252 void telephony_answer_call_req(void *telephony_device)
    253 {
    254 	struct voice_call *vc = find_vc_with_status(CALL_STATUS_INCOMING);
    255 	int ret;
    256 
    257 	if (!vc) {
    258 		telephony_answer_call_rsp(telephony_device,
    259 					CME_ERROR_NOT_ALLOWED);
    260 		return;
    261 	}
    262 
    263 	ret = send_method_call(OFONO_BUS_NAME, vc->obj_path,
    264 			OFONO_VC_INTERFACE,
    265 			"Answer", NULL,
    266 			NULL, DBUS_TYPE_INVALID);
    267 
    268 	if (ret < 0) {
    269 		telephony_answer_call_rsp(telephony_device,
    270 					CME_ERROR_AG_FAILURE);
    271 		return;
    272 	}
    273 
    274 	telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
    275 }
    276 
    277 void telephony_dial_number_req(void *telephony_device, const char *number)
    278 {
    279 	const char *clir;
    280 	int ret;
    281 
    282 	DBG("telephony-ofono: dial request to %s", number);
    283 
    284 	if (!modem_obj_path) {
    285 		telephony_dial_number_rsp(telephony_device,
    286 					CME_ERROR_AG_FAILURE);
    287 		return;
    288 	}
    289 
    290 	if (!strncmp(number, "*31#", 4)) {
    291 		number += 4;
    292 		clir = "enabled";
    293 	} else if (!strncmp(number, "#31#", 4)) {
    294 		number += 4;
    295 		clir =  "disabled";
    296 	} else
    297 		clir = "default";
    298 
    299 	ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
    300 			OFONO_VCMANAGER_INTERFACE,
    301                         "Dial", NULL, NULL,
    302 			DBUS_TYPE_STRING, &number,
    303 			DBUS_TYPE_STRING, &clir,
    304 			DBUS_TYPE_INVALID);
    305 
    306 	if (ret < 0)
    307 		telephony_dial_number_rsp(telephony_device,
    308 			CME_ERROR_AG_FAILURE);
    309 	else
    310 		telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
    311 }
    312 
    313 void telephony_transmit_dtmf_req(void *telephony_device, char tone)
    314 {
    315 	char *tone_string;
    316 	int ret;
    317 
    318 	DBG("telephony-ofono: transmit dtmf: %c", tone);
    319 
    320 	if (!modem_obj_path) {
    321 		telephony_transmit_dtmf_rsp(telephony_device,
    322 					CME_ERROR_AG_FAILURE);
    323 		return;
    324 	}
    325 
    326 	tone_string = g_strdup_printf("%c", tone);
    327 	ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
    328 			OFONO_VCMANAGER_INTERFACE,
    329 			"SendTones", NULL, NULL,
    330 			DBUS_TYPE_STRING, &tone_string,
    331 			DBUS_TYPE_INVALID);
    332 	g_free(tone_string);
    333 
    334 	if (ret < 0)
    335 		telephony_transmit_dtmf_rsp(telephony_device,
    336 			CME_ERROR_AG_FAILURE);
    337 	else
    338 		telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
    339 }
    340 
    341 void telephony_subscriber_number_req(void *telephony_device)
    342 {
    343 	DBG("telephony-ofono: subscriber number request");
    344 
    345 	if (subscriber_number)
    346 		telephony_subscriber_number_ind(subscriber_number,
    347 						NUMBER_TYPE_TELEPHONY,
    348 						SUBSCRIBER_SERVICE_VOICE);
    349 	telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
    350 }
    351 
    352 void telephony_list_current_calls_req(void *telephony_device)
    353 {
    354 	GSList *l;
    355 	int i;
    356 
    357 	DBG("telephony-ofono: list current calls request");
    358 
    359 	for (l = calls, i = 1; l != NULL; l = l->next, i++) {
    360 		struct voice_call *vc = l->data;
    361 		int direction;
    362 
    363 		direction = vc->originating ?
    364 				CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
    365 
    366 		telephony_list_current_call_ind(i, direction, vc->status,
    367 					CALL_MODE_VOICE, CALL_MULTIPARTY_NO,
    368 					vc->number, NUMBER_TYPE_TELEPHONY);
    369 	}
    370 	telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
    371 }
    372 
    373 void telephony_operator_selection_req(void *telephony_device)
    374 {
    375 	DBG("telephony-ofono: operator selection request");
    376 
    377 	telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
    378 				net.operator_name ? net.operator_name : "");
    379 	telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
    380 }
    381 
    382 void telephony_call_hold_req(void *telephony_device, const char *cmd)
    383 {
    384 	DBG("telephony-ofono: got call hold request %s", cmd);
    385 	telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
    386 }
    387 
    388 void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
    389 {
    390 	DBG("telephony-ofono: got %s NR and EC request",
    391 			enable ? "enable" : "disable");
    392 
    393 	telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
    394 }
    395 
    396 void telephony_key_press_req(void *telephony_device, const char *keys)
    397 {
    398 	DBG("telephony-ofono: got key press request for %s", keys);
    399 	telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
    400 }
    401 
    402 void telephony_voice_dial_req(void *telephony_device, gboolean enable)
    403 {
    404 	DBG("telephony-ofono: got %s voice dial request",
    405 			enable ? "enable" : "disable");
    406 
    407 	telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
    408 }
    409 
    410 static gboolean iter_get_basic_args(DBusMessageIter *iter,
    411 					int first_arg_type, ...)
    412 {
    413 	int type;
    414 	va_list ap;
    415 
    416 	va_start(ap, first_arg_type);
    417 
    418 	for (type = first_arg_type; type != DBUS_TYPE_INVALID;
    419 		type = va_arg(ap, int)) {
    420 		void *value = va_arg(ap, void *);
    421 		int real_type = dbus_message_iter_get_arg_type(iter);
    422 
    423 		if (real_type != type) {
    424 			error("iter_get_basic_args: expected %c but got %c",
    425 				(char) type, (char) real_type);
    426 			break;
    427 		}
    428 
    429 		dbus_message_iter_get_basic(iter, value);
    430 		dbus_message_iter_next(iter);
    431 	}
    432 
    433 	va_end(ap);
    434 
    435 	return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
    436 }
    437 
    438 static void handle_registration_property(const char *property, DBusMessageIter sub)
    439 {
    440 	const char *status, *operator;
    441 	unsigned int signals_bar;
    442 
    443 	if (g_str_equal(property, "Status")) {
    444 		dbus_message_iter_get_basic(&sub, &status);
    445 		DBG("Status is %s", status);
    446 		if (g_str_equal(status, "registered")) {
    447 			net.status = NETWORK_REG_STATUS_HOME;
    448 			telephony_update_indicator(ofono_indicators,
    449 						"roam", EV_ROAM_INACTIVE);
    450 			telephony_update_indicator(ofono_indicators,
    451 						"service", EV_SERVICE_PRESENT);
    452 		} else if (g_str_equal(status, "roaming")) {
    453 			net.status = NETWORK_REG_STATUS_ROAM;
    454 			telephony_update_indicator(ofono_indicators,
    455 						"roam", EV_ROAM_ACTIVE);
    456 			telephony_update_indicator(ofono_indicators,
    457 						"service", EV_SERVICE_PRESENT);
    458 		} else {
    459 			net.status = NETWORK_REG_STATUS_NOSERV;
    460 			telephony_update_indicator(ofono_indicators,
    461 						"roam", EV_ROAM_INACTIVE);
    462 			telephony_update_indicator(ofono_indicators,
    463 						"service", EV_SERVICE_NONE);
    464 		}
    465 	} else if (g_str_equal(property, "Operator")) {
    466 		dbus_message_iter_get_basic(&sub, &operator);
    467 		DBG("Operator is %s", operator);
    468 		g_free(net.operator_name);
    469 		net.operator_name = g_strdup(operator);
    470 	} else if (g_str_equal(property, "SignalStrength")) {
    471 		dbus_message_iter_get_basic(&sub, &signals_bar);
    472 		DBG("SignalStrength is %d", signals_bar);
    473 		net.signals_bar = signals_bar;
    474 		telephony_update_indicator(ofono_indicators, "signal",
    475 						(signals_bar + 20) / 21);
    476 	}
    477 }
    478 
    479 static void get_registration_reply(DBusPendingCall *call, void *user_data)
    480 {
    481 	DBusError err;
    482 	DBusMessage *reply;
    483 	DBusMessageIter iter, iter_entry;
    484 	uint32_t features = AG_FEATURE_EC_ANDOR_NR |
    485 				AG_FEATURE_REJECT_A_CALL |
    486 				AG_FEATURE_ENHANCED_CALL_STATUS |
    487 				AG_FEATURE_EXTENDED_ERROR_RESULT_CODES;
    488 
    489 	reply = dbus_pending_call_steal_reply(call);
    490 
    491 	dbus_error_init(&err);
    492 	if (dbus_set_error_from_message(&err, reply)) {
    493 		error("ofono replied with an error: %s, %s",
    494 				err.name, err.message);
    495 		dbus_error_free(&err);
    496 		goto done;
    497 	}
    498 
    499 	dbus_message_iter_init(reply, &iter);
    500 
    501 	/* ARRAY -> ENTRY -> VARIANT */
    502 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
    503 		error("Unexpected signature in GetProperties return");
    504 		goto done;
    505 	}
    506 
    507 	dbus_message_iter_recurse(&iter, &iter_entry);
    508 
    509 	if (dbus_message_iter_get_arg_type(&iter_entry)
    510 					!= DBUS_TYPE_DICT_ENTRY) {
    511 		error("Unexpected signature in GetProperties return");
    512 		goto done;
    513 	}
    514 
    515 	while (dbus_message_iter_get_arg_type(&iter_entry)
    516 					!= DBUS_TYPE_INVALID) {
    517 		DBusMessageIter iter_property, sub;
    518 		char *property;
    519 
    520 		dbus_message_iter_recurse(&iter_entry, &iter_property);
    521 		if (dbus_message_iter_get_arg_type(&iter_property)
    522 					!= DBUS_TYPE_STRING) {
    523 			error("Unexpected signature in GetProperties return");
    524 			goto done;
    525 		}
    526 
    527 		dbus_message_iter_get_basic(&iter_property, &property);
    528 
    529 		dbus_message_iter_next(&iter_property);
    530 		dbus_message_iter_recurse(&iter_property, &sub);
    531 
    532 		handle_registration_property(property, sub);
    533 
    534                 dbus_message_iter_next(&iter_entry);
    535         }
    536 
    537 	telephony_ready_ind(features, ofono_indicators,
    538 				response_and_hold, chld_str);
    539 
    540 done:
    541 	dbus_message_unref(reply);
    542 }
    543 
    544 static int get_registration_and_signal_status()
    545 {
    546 	if (!modem_obj_path)
    547 		return -ENOENT;
    548 
    549 	return send_method_call(OFONO_BUS_NAME, modem_obj_path,
    550 			OFONO_NETWORKREG_INTERFACE,
    551 			"GetProperties", get_registration_reply,
    552 			NULL, DBUS_TYPE_INVALID);
    553 }
    554 
    555 static void list_modem_reply(DBusPendingCall *call, void *user_data)
    556 {
    557 	DBusError err;
    558 	DBusMessage *reply;
    559 	DBusMessageIter iter, iter_entry, iter_property, iter_arrary, sub;
    560 	char *property, *modem_obj_path_local;
    561 	int ret;
    562 
    563 	DBG("list_modem_reply is called\n");
    564 	reply = dbus_pending_call_steal_reply(call);
    565 
    566 	dbus_error_init(&err);
    567 	if (dbus_set_error_from_message(&err, reply)) {
    568 		error("ofono replied with an error: %s, %s",
    569 				err.name, err.message);
    570 		dbus_error_free(&err);
    571 		goto done;
    572 	}
    573 
    574 	dbus_message_iter_init(reply, &iter);
    575 
    576 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
    577 		error("Unexpected signature in ListModems return");
    578 		goto done;
    579 	}
    580 
    581 	dbus_message_iter_recurse(&iter, &iter_entry);
    582 
    583 	if (dbus_message_iter_get_arg_type(&iter_entry)
    584 					!= DBUS_TYPE_DICT_ENTRY) {
    585 		error("Unexpected signature in ListModems return 2, %c",
    586 				dbus_message_iter_get_arg_type(&iter_entry));
    587 		goto done;
    588 	}
    589 
    590 	dbus_message_iter_recurse(&iter_entry, &iter_property);
    591 
    592 	dbus_message_iter_get_basic(&iter_property, &property);
    593 
    594 	dbus_message_iter_next(&iter_property);
    595 	dbus_message_iter_recurse(&iter_property, &iter_arrary);
    596 	dbus_message_iter_recurse(&iter_arrary, &sub);
    597 	while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
    598 
    599 		dbus_message_iter_get_basic(&sub, &modem_obj_path_local);
    600 		modem_obj_path = g_strdup(modem_obj_path_local);
    601 		if (modem_obj_path != NULL) {
    602 			DBG("modem_obj_path is %p, %s\n", modem_obj_path,
    603 							modem_obj_path);
    604 			break;
    605 		}
    606 		dbus_message_iter_next(&sub);
    607 	}
    608 
    609 	ret = get_registration_and_signal_status();
    610 	if (ret < 0)
    611 		error("get_registration_and_signal_status() failed(%d)", ret);
    612 done:
    613 	dbus_message_unref(reply);
    614 }
    615 
    616 static gboolean handle_registration_property_changed(DBusConnection *conn,
    617 						DBusMessage *msg, void *data)
    618 {
    619 	DBusMessageIter iter, sub;
    620 	const char *property;
    621 
    622 	dbus_message_iter_init(msg, &iter);
    623 
    624 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
    625 		error("Unexpected signature in networkregistration"
    626 					" PropertyChanged signal");
    627 		return TRUE;
    628 	}
    629 	dbus_message_iter_get_basic(&iter, &property);
    630 	DBG("in handle_registration_property_changed(),"
    631 					" the property is %s", property);
    632 
    633 	dbus_message_iter_next(&iter);
    634 	dbus_message_iter_recurse(&iter, &sub);
    635 
    636 	handle_registration_property(property, sub);
    637 
    638 	return TRUE;
    639 }
    640 
    641 static void vc_getproperties_reply(DBusPendingCall *call, void *user_data)
    642 {
    643 	DBusMessage *reply;
    644 	DBusError err;
    645 	DBusMessageIter iter, iter_entry;
    646 	const char *path = user_data;
    647 	struct voice_call *vc;
    648 
    649 	DBG("in vc_getproperties_reply");
    650 
    651 	reply = dbus_pending_call_steal_reply(call);
    652 	dbus_error_init(&err);
    653 	if (dbus_set_error_from_message(&err, reply)) {
    654 		error("ofono replied with an error: %s, %s",
    655 				err.name, err.message);
    656 		dbus_error_free(&err);
    657 		goto done;
    658 	}
    659 
    660 	vc = find_vc(path);
    661 	if (!vc) {
    662 		error("in vc_getproperties_reply, vc is NULL");
    663 		goto done;
    664 	}
    665 
    666 	dbus_message_iter_init(reply, &iter);
    667 
    668 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
    669 		error("Unexpected signature in vc_getproperties_reply()");
    670 		goto done;
    671 	}
    672 
    673 	dbus_message_iter_recurse(&iter, &iter_entry);
    674 
    675 	if (dbus_message_iter_get_arg_type(&iter_entry)
    676 			!= DBUS_TYPE_DICT_ENTRY) {
    677 		error("Unexpected signature in vc_getproperties_reply()");
    678 		goto done;
    679 	}
    680 
    681 	while (dbus_message_iter_get_arg_type(&iter_entry)
    682 			!= DBUS_TYPE_INVALID) {
    683 		DBusMessageIter iter_property, sub;
    684 		char *property, *cli, *state;
    685 
    686 		dbus_message_iter_recurse(&iter_entry, &iter_property);
    687 		if (dbus_message_iter_get_arg_type(&iter_property)
    688 				!= DBUS_TYPE_STRING) {
    689 			error("Unexpected signature in"
    690 					" vc_getproperties_reply()");
    691 			goto done;
    692 		}
    693 
    694 		dbus_message_iter_get_basic(&iter_property, &property);
    695 
    696 		dbus_message_iter_next(&iter_property);
    697 		dbus_message_iter_recurse(&iter_property, &sub);
    698 		if (g_str_equal(property, "LineIdentification")) {
    699 			dbus_message_iter_get_basic(&sub, &cli);
    700 			DBG("in vc_getproperties_reply(), cli is %s", cli);
    701 			vc->number = g_strdup(cli);
    702 		} else if (g_str_equal(property, "State")) {
    703 			dbus_message_iter_get_basic(&sub, &state);
    704 			DBG("in vc_getproperties_reply(),"
    705 					" state is %s", state);
    706 			if (g_str_equal(state, "incoming"))
    707 				vc->status = CALL_STATUS_INCOMING;
    708 			else if (g_str_equal(state, "dialing"))
    709 				vc->status = CALL_STATUS_DIALING;
    710 			else if (g_str_equal(state, "alerting"))
    711 				vc->status = CALL_STATUS_ALERTING;
    712 			else if (g_str_equal(state, "waiting"))
    713 				vc->status = CALL_STATUS_WAITING;
    714 		}
    715 
    716 		dbus_message_iter_next(&iter_entry);
    717 	}
    718 
    719 	switch (vc->status) {
    720 	case CALL_STATUS_INCOMING:
    721 		printf("in CALL_STATUS_INCOMING: case\n");
    722 		vc->originating = FALSE;
    723 		telephony_update_indicator(ofono_indicators, "callsetup",
    724 					EV_CALLSETUP_INCOMING);
    725 		telephony_incoming_call_ind(vc->number, NUMBER_TYPE_TELEPHONY);
    726 		break;
    727 	case CALL_STATUS_DIALING:
    728 		printf("in CALL_STATUS_DIALING: case\n");
    729 		vc->originating = TRUE;
    730 		g_free(last_dialed_number);
    731 		last_dialed_number = g_strdup(vc->number);
    732 		telephony_update_indicator(ofono_indicators, "callsetup",
    733 					EV_CALLSETUP_OUTGOING);
    734 		break;
    735 	case CALL_STATUS_ALERTING:
    736 		printf("in CALL_STATUS_ALERTING: case\n");
    737 		vc->originating = TRUE;
    738 		g_free(last_dialed_number);
    739 		last_dialed_number = g_strdup(vc->number);
    740 		telephony_update_indicator(ofono_indicators, "callsetup",
    741 					EV_CALLSETUP_ALERTING);
    742 		break;
    743 	case CALL_STATUS_WAITING:
    744 		DBG("in CALL_STATUS_WAITING: case");
    745 		vc->originating = FALSE;
    746 		telephony_update_indicator(ofono_indicators, "callsetup",
    747 					EV_CALLSETUP_INCOMING);
    748 		telephony_call_waiting_ind(vc->number, NUMBER_TYPE_TELEPHONY);
    749 		break;
    750 	}
    751 done:
    752 	dbus_message_unref(reply);
    753 }
    754 
    755 static void vc_free(struct voice_call *vc)
    756 {
    757 	if (!vc)
    758 		return;
    759 
    760 	g_dbus_remove_watch(connection, vc->watch);
    761 	g_free(vc->obj_path);
    762 	g_free(vc->number);
    763 	g_free(vc);
    764 }
    765 
    766 static gboolean handle_vc_property_changed(DBusConnection *conn,
    767 					DBusMessage *msg, void *data)
    768 {
    769 	struct voice_call *vc = data;
    770 	const char *obj_path = dbus_message_get_path(msg);
    771 	DBusMessageIter iter, sub;
    772 	const char *property, *state;
    773 
    774 	DBG("in handle_vc_property_changed, obj_path is %s", obj_path);
    775 
    776 	dbus_message_iter_init(msg, &iter);
    777 
    778 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
    779 		error("Unexpected signature in vc PropertyChanged signal");
    780 		return TRUE;
    781 	}
    782 
    783 	dbus_message_iter_get_basic(&iter, &property);
    784 	DBG("in handle_vc_property_changed(), the property is %s", property);
    785 
    786 	dbus_message_iter_next(&iter);
    787 	dbus_message_iter_recurse(&iter, &sub);
    788 	if (g_str_equal(property, "State")) {
    789 		dbus_message_iter_get_basic(&sub, &state);
    790 		DBG("in handle_vc_property_changed(), State is %s", state);
    791 		if (g_str_equal(state, "disconnected")) {
    792 			printf("in disconnected case\n");
    793 			if (vc->status == CALL_STATUS_ACTIVE)
    794 				telephony_update_indicator(ofono_indicators,
    795 						"call", EV_CALL_INACTIVE);
    796 			else
    797 				telephony_update_indicator(ofono_indicators,
    798 					"callsetup", EV_CALLSETUP_INACTIVE);
    799 			if (vc->status == CALL_STATUS_INCOMING)
    800 				telephony_calling_stopped_ind();
    801 			calls = g_slist_remove(calls, vc);
    802 			vc_free(vc);
    803 		} else if (g_str_equal(state, "active")) {
    804 			telephony_update_indicator(ofono_indicators,
    805 							"call", EV_CALL_ACTIVE);
    806 			telephony_update_indicator(ofono_indicators,
    807 							"callsetup",
    808 							EV_CALLSETUP_INACTIVE);
    809 			if (vc->status == CALL_STATUS_INCOMING)
    810 				telephony_calling_stopped_ind();
    811 			vc->status = CALL_STATUS_ACTIVE;
    812 			DBG("vc status is CALL_STATUS_ACTIVE");
    813 		} else if (g_str_equal(state, "alerting")) {
    814 			telephony_update_indicator(ofono_indicators,
    815 					"callsetup", EV_CALLSETUP_ALERTING);
    816 			vc->status = CALL_STATUS_ALERTING;
    817 			DBG("vc status is CALL_STATUS_ALERTING");
    818 		} else if (g_str_equal(state, "incoming")) {
    819 			/* state change from waiting to incoming */
    820 			telephony_update_indicator(ofono_indicators,
    821 					"callsetup", EV_CALLSETUP_INCOMING);
    822 			telephony_incoming_call_ind(vc->number,
    823 						NUMBER_TYPE_TELEPHONY);
    824 			vc->status = CALL_STATUS_INCOMING;
    825 			DBG("vc status is CALL_STATUS_INCOMING");
    826 		}
    827 	}
    828 
    829 	return TRUE;
    830 }
    831 
    832 static gboolean handle_vcmanager_property_changed(DBusConnection *conn,
    833 						DBusMessage *msg, void *data)
    834 {
    835 	DBusMessageIter iter, sub, array;
    836 	const char *property, *vc_obj_path = NULL;
    837 	struct voice_call *vc, *vc_new = NULL;
    838 
    839 	DBG("in handle_vcmanager_property_changed");
    840 
    841 	dbus_message_iter_init(msg, &iter);
    842 
    843 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
    844 		error("Unexpected signature in vcmanager"
    845 					" PropertyChanged signal");
    846 		return TRUE;
    847 	}
    848 
    849 	dbus_message_iter_get_basic(&iter, &property);
    850 	DBG("in handle_vcmanager_property_changed(),"
    851 				" the property is %s", property);
    852 
    853 	dbus_message_iter_next(&iter);
    854 	dbus_message_iter_recurse(&iter, &sub);
    855 	if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY) {
    856 		error("Unexpected signature in vcmanager"
    857 					" PropertyChanged signal");
    858 		return TRUE;
    859 	}
    860 	dbus_message_iter_recurse(&sub, &array);
    861 	while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
    862 		dbus_message_iter_get_basic(&array, &vc_obj_path);
    863 		vc = find_vc(vc_obj_path);
    864 		if (vc) {
    865 			DBG("in handle_vcmanager_property_changed,"
    866 					" found an existing vc");
    867 		} else {
    868 			vc_new = g_new0(struct voice_call, 1);
    869 			vc_new->obj_path = g_strdup(vc_obj_path);
    870 			calls = g_slist_append(calls, vc_new);
    871 			vc_new->watch = g_dbus_add_signal_watch(connection,
    872 					NULL, vc_obj_path,
    873 					OFONO_VC_INTERFACE,
    874 					"PropertyChanged",
    875 					handle_vc_property_changed,
    876 					vc_new, NULL);
    877 		}
    878 		dbus_message_iter_next(&array);
    879 	}
    880 
    881 	if (!vc_new)
    882 		return TRUE;
    883 
    884 	send_method_call(OFONO_BUS_NAME, vc_new->obj_path,
    885 				OFONO_VC_INTERFACE,
    886 				"GetProperties", vc_getproperties_reply,
    887 				vc_new->obj_path, DBUS_TYPE_INVALID);
    888 
    889 	return TRUE;
    890 }
    891 
    892 static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
    893 {
    894 	DBusMessage *reply;
    895 	DBusError err;
    896 	dbus_int32_t level;
    897 	int *value = user_data;
    898 
    899 	reply = dbus_pending_call_steal_reply(call);
    900 
    901 	dbus_error_init(&err);
    902 	if (dbus_set_error_from_message(&err, reply)) {
    903 		error("hald replied with an error: %s, %s",
    904 				err.name, err.message);
    905 		dbus_error_free(&err);
    906 		goto done;
    907 	}
    908 
    909 	dbus_error_init(&err);
    910 	if (dbus_message_get_args(reply, &err,
    911 				DBUS_TYPE_INT32, &level,
    912 				DBUS_TYPE_INVALID) == FALSE) {
    913 		error("Unable to parse GetPropertyInteger reply: %s, %s",
    914 							err.name, err.message);
    915 		dbus_error_free(&err);
    916 		goto done;
    917 	}
    918 
    919 	*value = (int) level;
    920 
    921 	if (value == &battchg_last)
    922 		DBG("telephony-ofono: battery.charge_level.last_full"
    923 					" is %d", *value);
    924 	else if (value == &battchg_design)
    925 		DBG("telephony-ofono: battery.charge_level.design"
    926 					" is %d", *value);
    927 	else
    928 		DBG("telephony-ofono: battery.charge_level.current"
    929 					" is %d", *value);
    930 
    931 	if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
    932 		int new, max;
    933 
    934 		if (battchg_last > 0)
    935 			max = battchg_last;
    936 		else
    937 			max = battchg_design;
    938 
    939 		new = battchg_cur * 5 / max;
    940 
    941 		telephony_update_indicator(ofono_indicators, "battchg", new);
    942 	}
    943 done:
    944 	dbus_message_unref(reply);
    945 }
    946 
    947 static void hal_get_integer(const char *path, const char *key, void *user_data)
    948 {
    949 	send_method_call("org.freedesktop.Hal", path,
    950 			"org.freedesktop.Hal.Device",
    951 			"GetPropertyInteger",
    952 			hal_battery_level_reply, user_data,
    953 			DBUS_TYPE_STRING, &key,
    954 			DBUS_TYPE_INVALID);
    955 }
    956 
    957 static gboolean handle_hal_property_modified(DBusConnection *conn,
    958 						DBusMessage *msg, void *data)
    959 {
    960 	const char *path;
    961 	DBusMessageIter iter, array;
    962 	dbus_int32_t num_changes;
    963 
    964 	path = dbus_message_get_path(msg);
    965 
    966 	dbus_message_iter_init(msg, &iter);
    967 
    968 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
    969 		error("Unexpected signature in hal PropertyModified signal");
    970 		return TRUE;
    971 	}
    972 
    973 	dbus_message_iter_get_basic(&iter, &num_changes);
    974 	dbus_message_iter_next(&iter);
    975 
    976 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
    977 		error("Unexpected signature in hal PropertyModified signal");
    978 		return TRUE;
    979 	}
    980 
    981 	dbus_message_iter_recurse(&iter, &array);
    982 
    983 	while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
    984 		DBusMessageIter prop;
    985 		const char *name;
    986 		dbus_bool_t added, removed;
    987 
    988 		dbus_message_iter_recurse(&array, &prop);
    989 
    990 		if (!iter_get_basic_args(&prop,
    991 					DBUS_TYPE_STRING, &name,
    992 					DBUS_TYPE_BOOLEAN, &added,
    993 					DBUS_TYPE_BOOLEAN, &removed,
    994 					DBUS_TYPE_INVALID)) {
    995 			error("Invalid hal PropertyModified parameters");
    996 			break;
    997 		}
    998 
    999 		if (g_str_equal(name, "battery.charge_level.last_full"))
   1000 			hal_get_integer(path, name, &battchg_last);
   1001 		else if (g_str_equal(name, "battery.charge_level.current"))
   1002 			hal_get_integer(path, name, &battchg_cur);
   1003 		else if (g_str_equal(name, "battery.charge_level.design"))
   1004 			hal_get_integer(path, name, &battchg_design);
   1005 
   1006 		dbus_message_iter_next(&array);
   1007 	}
   1008 
   1009 	return TRUE;
   1010 }
   1011 
   1012 static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
   1013 {
   1014 	DBusMessage *reply;
   1015 	DBusError err;
   1016 	DBusMessageIter iter, sub;
   1017 	int type;
   1018 	const char *path;
   1019 
   1020 	DBG("begin of hal_find_device_reply()");
   1021 	reply = dbus_pending_call_steal_reply(call);
   1022 
   1023 	dbus_error_init(&err);
   1024 
   1025 	if (dbus_set_error_from_message(&err, reply)) {
   1026 		error("hald replied with an error: %s, %s",
   1027 				err.name, err.message);
   1028 		dbus_error_free(&err);
   1029 		goto done;
   1030 	}
   1031 
   1032 	dbus_message_iter_init(reply, &iter);
   1033 
   1034 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
   1035 		error("Unexpected signature in hal_find_device_reply()");
   1036 		goto done;
   1037 	}
   1038 
   1039 	dbus_message_iter_recurse(&iter, &sub);
   1040 
   1041 	type = dbus_message_iter_get_arg_type(&sub);
   1042 
   1043 	if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
   1044 		error("No hal device with battery capability found");
   1045 		goto done;
   1046 	}
   1047 
   1048 	dbus_message_iter_get_basic(&sub, &path);
   1049 
   1050 	DBG("telephony-ofono: found battery device at %s", path);
   1051 
   1052 	device_watch = g_dbus_add_signal_watch(connection, NULL, path,
   1053 					"org.freedesktop.Hal.Device",
   1054 					"PropertyModified",
   1055 					handle_hal_property_modified,
   1056 					NULL, NULL);
   1057 
   1058 	hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
   1059 	hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
   1060 	hal_get_integer(path, "battery.charge_level.design", &battchg_design);
   1061 done:
   1062 	dbus_message_unref(reply);
   1063 }
   1064 
   1065 int telephony_init(void)
   1066 {
   1067 	const char *battery_cap = "battery";
   1068 	int ret;
   1069 
   1070 	connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
   1071 
   1072 	registration_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
   1073 					OFONO_NETWORKREG_INTERFACE,
   1074 					"PropertyChanged",
   1075 					handle_registration_property_changed,
   1076 					NULL, NULL);
   1077 
   1078 	voice_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
   1079 					OFONO_VCMANAGER_INTERFACE,
   1080 					"PropertyChanged",
   1081 					handle_vcmanager_property_changed,
   1082 					NULL, NULL);
   1083 
   1084 	ret = send_method_call(OFONO_BUS_NAME, OFONO_PATH,
   1085 				OFONO_MANAGER_INTERFACE, "GetProperties",
   1086 				list_modem_reply, NULL, DBUS_TYPE_INVALID);
   1087 	if (ret < 0)
   1088 		return ret;
   1089 
   1090 	ret = send_method_call("org.freedesktop.Hal",
   1091 				"/org/freedesktop/Hal/Manager",
   1092 				"org.freedesktop.Hal.Manager",
   1093 				"FindDeviceByCapability",
   1094 				hal_find_device_reply, NULL,
   1095 				DBUS_TYPE_STRING, &battery_cap,
   1096 				DBUS_TYPE_INVALID);
   1097 	if (ret < 0)
   1098 		return ret;
   1099 
   1100 	DBG("telephony_init() successfully");
   1101 
   1102 	return ret;
   1103 }
   1104 
   1105 void telephony_exit(void)
   1106 {
   1107 	g_free(net.operator_name);
   1108 
   1109 	g_free(modem_obj_path);
   1110 	g_free(last_dialed_number);
   1111 
   1112 	g_slist_foreach(calls, (GFunc) vc_free, NULL);
   1113 	g_slist_free(calls);
   1114 	calls = NULL;
   1115 
   1116 	g_dbus_remove_watch(connection, registration_watch);
   1117 	g_dbus_remove_watch(connection, voice_watch);
   1118 	g_dbus_remove_watch(connection, device_watch);
   1119 
   1120 	dbus_connection_unref(connection);
   1121 	connection = NULL;
   1122 }
   1123