telephony-ofono.c revision d8ee95eb6d31fa715398a785b4b79846ca0a1f70
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@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
41enum net_registration_status {
42	NETWORK_REG_STATUS_HOME = 0x00,
43	NETWORK_REG_STATUS_ROAM,
44	NETWORK_REG_STATUS_NOSERV
45};
46
47struct voice_call {
48	char *obj_path;
49	int status;
50	gboolean originating;
51	char *number;
52	guint watch;
53};
54
55static DBusConnection *connection = NULL;
56static char *modem_obj_path = NULL;
57static char *last_dialed_number = NULL;
58static GSList *calls = NULL;
59static GSList *watches = NULL;
60static GSList *pending = NULL;
61
62#define OFONO_BUS_NAME "org.ofono"
63#define OFONO_PATH "/"
64#define OFONO_MODEM_INTERFACE "org.ofono.Modem"
65#define OFONO_MANAGER_INTERFACE "org.ofono.Manager"
66#define OFONO_NETWORKREG_INTERFACE "org.ofono.NetworkRegistration"
67#define OFONO_VCMANAGER_INTERFACE "org.ofono.VoiceCallManager"
68#define OFONO_VC_INTERFACE "org.ofono.VoiceCall"
69
70/* HAL battery namespace key values */
71static int battchg_cur = -1;    /* "battery.charge_level.current" */
72static int battchg_last = -1;   /* "battery.charge_level.last_full" */
73static int battchg_design = -1; /* "battery.charge_level.design" */
74
75static struct {
76	uint8_t status;
77	uint32_t signals_bar;
78	char *operator_name;
79} net = {
80	.status = NETWORK_REG_STATUS_NOSERV,
81	.signals_bar = 0,
82	.operator_name = NULL,
83};
84
85static const char *chld_str = "0,1,1x,2,2x,3,4";
86static char *subscriber_number = NULL;
87
88static gboolean events_enabled = FALSE;
89
90static struct indicator ofono_indicators[] =
91{
92	{ "battchg",	"0-5",	5,	TRUE },
93	{ "signal",	"0-5",	5,	TRUE },
94	{ "service",	"0,1",	1,	TRUE },
95	{ "call",	"0,1",	0,	TRUE },
96	{ "callsetup",	"0-3",	0,	TRUE },
97	{ "callheld",	"0-2",	0,	FALSE },
98	{ "roam",	"0,1",	0,	TRUE },
99	{ NULL }
100};
101
102static struct voice_call *find_vc(const char *path)
103{
104	GSList *l;
105
106	for (l = calls; l != NULL; l = l->next) {
107		struct voice_call *vc = l->data;
108
109		if (g_str_equal(vc->obj_path, path))
110			return vc;
111	}
112
113	return NULL;
114}
115
116static struct voice_call *find_vc_with_status(int status)
117{
118	GSList *l;
119
120	for (l = calls; l != NULL; l = l->next) {
121		struct voice_call *vc = l->data;
122
123		if (vc->status == status)
124			return vc;
125	}
126
127	return NULL;
128}
129
130static struct voice_call *find_vc_without_status(int status)
131{
132	GSList *l;
133
134	for (l = calls; l != NULL; l = l->next) {
135		struct voice_call *call = l->data;
136
137		if (call->status != status)
138			return call;
139	}
140
141	return NULL;
142}
143
144static int number_type(const char *number)
145{
146	if (number == NULL)
147		return NUMBER_TYPE_TELEPHONY;
148
149	if (number[0] == '+' || strncmp(number, "00", 2) == 0)
150		return NUMBER_TYPE_INTERNATIONAL;
151
152	return NUMBER_TYPE_TELEPHONY;
153}
154
155void telephony_device_connected(void *telephony_device)
156{
157	struct voice_call *coming;
158
159	DBG("telephony-ofono: device %p connected", telephony_device);
160
161	coming = find_vc_with_status(CALL_STATUS_ALERTING);
162	if (coming) {
163		if (find_vc_with_status(CALL_STATUS_ACTIVE))
164			telephony_call_waiting_ind(coming->number,
165						number_type(coming->number));
166		else
167			telephony_incoming_call_ind(coming->number,
168						number_type(coming->number));
169	}
170}
171
172void telephony_device_disconnected(void *telephony_device)
173{
174	DBG("telephony-ofono: device %p disconnected", telephony_device);
175	events_enabled = FALSE;
176}
177
178void telephony_event_reporting_req(void *telephony_device, int ind)
179{
180	events_enabled = ind == 1 ? TRUE : FALSE;
181
182	telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
183}
184
185void telephony_response_and_hold_req(void *telephony_device, int rh)
186{
187	telephony_response_and_hold_rsp(telephony_device,
188						CME_ERROR_NOT_SUPPORTED);
189}
190
191void telephony_last_dialed_number_req(void *telephony_device)
192{
193	DBG("telephony-ofono: last dialed number request");
194
195	if (last_dialed_number)
196		telephony_dial_number_req(telephony_device, last_dialed_number);
197	else
198		telephony_last_dialed_number_rsp(telephony_device,
199				CME_ERROR_NOT_ALLOWED);
200}
201
202static int send_method_call(const char *dest, const char *path,
203                                const char *interface, const char *method,
204                                DBusPendingCallNotifyFunction cb,
205                                void *user_data, int type, ...)
206{
207	DBusMessage *msg;
208	DBusPendingCall *call;
209	va_list args;
210
211	msg = dbus_message_new_method_call(dest, path, interface, method);
212	if (!msg) {
213		error("Unable to allocate new D-Bus %s message", method);
214		return -ENOMEM;
215	}
216
217	va_start(args, type);
218
219	if (!dbus_message_append_args_valist(msg, type, args)) {
220		dbus_message_unref(msg);
221		va_end(args);
222		return -EIO;
223	}
224
225	va_end(args);
226
227	if (!cb) {
228		g_dbus_send_message(connection, msg);
229		return 0;
230	}
231
232	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
233		error("Sending %s failed", method);
234		dbus_message_unref(msg);
235		return -EIO;
236	}
237
238	dbus_pending_call_set_notify(call, cb, user_data, NULL);
239	pending = g_slist_prepend(pending, call);
240	dbus_message_unref(msg);
241
242	return 0;
243}
244
245static int answer_call(struct voice_call *vc)
246{
247	DBG("%s", vc->number);
248	return send_method_call(OFONO_BUS_NAME, vc->obj_path,
249						OFONO_VC_INTERFACE, "Answer",
250						NULL, NULL, DBUS_TYPE_INVALID);
251}
252
253static int release_call(struct voice_call *vc)
254{
255	DBG("%s", vc->number);
256	return send_method_call(OFONO_BUS_NAME, vc->obj_path,
257						OFONO_VC_INTERFACE, "Hangup",
258						NULL, NULL, DBUS_TYPE_INVALID);
259}
260
261static int release_answer_calls()
262{
263	DBG("");
264	return send_method_call(OFONO_BUS_NAME, modem_obj_path,
265						OFONO_VCMANAGER_INTERFACE,
266						"ReleaseAndAnswer",
267						NULL, NULL, DBUS_TYPE_INVALID);
268}
269
270static int swap_calls(void)
271{
272	DBG("");
273	return send_method_call(OFONO_BUS_NAME, modem_obj_path,
274						OFONO_VCMANAGER_INTERFACE,
275						"SwapCalls",
276						NULL, NULL, DBUS_TYPE_INVALID);
277}
278
279static int call_transfer(void)
280{
281	DBG("");
282	return send_method_call(OFONO_BUS_NAME, modem_obj_path,
283						OFONO_VCMANAGER_INTERFACE,
284						"Transfer",
285						NULL, NULL, DBUS_TYPE_INVALID);
286}
287
288void telephony_terminate_call_req(void *telephony_device)
289{
290	struct voice_call *call;
291	struct voice_call *alerting;
292	int err;
293
294	call = find_vc_with_status(CALL_STATUS_ACTIVE);
295	if (!call)
296		call = calls->data;
297
298	if (!call) {
299		error("No active call");
300		telephony_terminate_call_rsp(telephony_device,
301						CME_ERROR_NOT_ALLOWED);
302		return;
303	}
304
305	alerting = find_vc_with_status(CALL_STATUS_ALERTING);
306	if (call->status == CALL_STATUS_HELD && alerting)
307		err = release_call(alerting);
308	else
309		err = release_call(call);
310
311	if (err < 0)
312		telephony_terminate_call_rsp(telephony_device,
313						CME_ERROR_AG_FAILURE);
314	else
315		telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
316}
317
318void telephony_answer_call_req(void *telephony_device)
319{
320	struct voice_call *vc;
321	int ret;
322
323	vc = find_vc_with_status(CALL_STATUS_INCOMING);
324	if (!vc)
325		vc = find_vc_with_status(CALL_STATUS_ALERTING);
326
327	if (!vc)
328		vc = find_vc_with_status(CALL_STATUS_WAITING);
329
330	if (!vc) {
331		telephony_answer_call_rsp(telephony_device,
332					CME_ERROR_NOT_ALLOWED);
333		return;
334	}
335
336	ret = answer_call(vc);
337	if (ret < 0) {
338		telephony_answer_call_rsp(telephony_device,
339					CME_ERROR_AG_FAILURE);
340		return;
341	}
342
343	telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
344}
345
346void telephony_dial_number_req(void *telephony_device, const char *number)
347{
348	const char *clir;
349	int ret;
350
351	DBG("telephony-ofono: dial request to %s", number);
352
353	if (!modem_obj_path) {
354		telephony_dial_number_rsp(telephony_device,
355					CME_ERROR_AG_FAILURE);
356		return;
357	}
358
359	if (!strncmp(number, "*31#", 4)) {
360		number += 4;
361		clir = "enabled";
362	} else if (!strncmp(number, "#31#", 4)) {
363		number += 4;
364		clir =  "disabled";
365	} else
366		clir = "default";
367
368	ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
369			OFONO_VCMANAGER_INTERFACE,
370                        "Dial", NULL, NULL,
371			DBUS_TYPE_STRING, &number,
372			DBUS_TYPE_STRING, &clir,
373			DBUS_TYPE_INVALID);
374
375	if (ret < 0)
376		telephony_dial_number_rsp(telephony_device,
377			CME_ERROR_AG_FAILURE);
378	else
379		telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
380}
381
382void telephony_transmit_dtmf_req(void *telephony_device, char tone)
383{
384	char *tone_string;
385	int ret;
386
387	DBG("telephony-ofono: transmit dtmf: %c", tone);
388
389	if (!modem_obj_path) {
390		telephony_transmit_dtmf_rsp(telephony_device,
391					CME_ERROR_AG_FAILURE);
392		return;
393	}
394
395	tone_string = g_strdup_printf("%c", tone);
396	ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
397			OFONO_VCMANAGER_INTERFACE,
398			"SendTones", NULL, NULL,
399			DBUS_TYPE_STRING, &tone_string,
400			DBUS_TYPE_INVALID);
401	g_free(tone_string);
402
403	if (ret < 0)
404		telephony_transmit_dtmf_rsp(telephony_device,
405			CME_ERROR_AG_FAILURE);
406	else
407		telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
408}
409
410void telephony_subscriber_number_req(void *telephony_device)
411{
412	DBG("telephony-ofono: subscriber number request");
413
414	if (subscriber_number)
415		telephony_subscriber_number_ind(subscriber_number,
416						NUMBER_TYPE_TELEPHONY,
417						SUBSCRIBER_SERVICE_VOICE);
418	telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
419}
420
421void telephony_list_current_calls_req(void *telephony_device)
422{
423	GSList *l;
424	int i;
425
426	DBG("telephony-ofono: list current calls request");
427
428	for (l = calls, i = 1; l != NULL; l = l->next, i++) {
429		struct voice_call *vc = l->data;
430		int direction;
431
432		direction = vc->originating ?
433				CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
434
435		DBG("call %s direction %d", vc->number, direction);
436
437		telephony_list_current_call_ind(i, direction, vc->status,
438					CALL_MODE_VOICE, CALL_MULTIPARTY_NO,
439					vc->number, number_type(vc->number));
440	}
441
442	telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
443}
444
445void telephony_operator_selection_req(void *telephony_device)
446{
447	DBG("telephony-ofono: operator selection request");
448
449	telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
450				net.operator_name ? net.operator_name : "");
451	telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
452}
453
454static void foreach_vc_with_status(int status,
455					int (*func)(struct voice_call *vc))
456{
457	GSList *l;
458
459	for (l = calls; l != NULL; l = l->next) {
460		struct voice_call *call = l->data;
461
462		if (call->status == status)
463			func(call);
464	}
465}
466
467void telephony_call_hold_req(void *telephony_device, const char *cmd)
468{
469	struct voice_call *call;
470	int err = 0;
471
472	DBG("telephony-ofono: got call hold request %s", cmd);
473
474	switch (cmd[0]) {
475	case '0':
476		if (find_vc_with_status(CALL_STATUS_WAITING))
477			foreach_vc_with_status(CALL_STATUS_WAITING,
478								release_call);
479		else
480			foreach_vc_with_status(CALL_STATUS_HELD, release_call);
481		break;
482	case '1':
483		err = release_answer_calls();
484		break;
485	case '2':
486		call = find_vc_with_status(CALL_STATUS_WAITING);
487
488		if (call)
489			err = answer_call(call);
490		else
491			err = swap_calls();
492		break;
493	case '4':
494		err = call_transfer();
495		break;
496	default:
497		DBG("Unknown call hold request");
498		break;
499	}
500
501	if (err)
502		telephony_call_hold_rsp(telephony_device,
503					CME_ERROR_AG_FAILURE);
504	else
505		telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
506}
507
508void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
509{
510	DBG("telephony-ofono: got %s NR and EC request",
511			enable ? "enable" : "disable");
512
513	telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
514}
515
516void telephony_key_press_req(void *telephony_device, const char *keys)
517{
518	struct voice_call *active, *waiting;
519	int err;
520
521	DBG("telephony-ofono: got key press request for %s", keys);
522
523	waiting = find_vc_with_status(CALL_STATUS_INCOMING);
524	if (!waiting)
525		waiting = find_vc_with_status(CALL_STATUS_DIALING);
526
527	active = find_vc_with_status(CALL_STATUS_ACTIVE);
528
529	if (waiting)
530		err = answer_call(waiting);
531	else if (active)
532		err = release_call(active);
533	else
534		err = 0;
535
536	if (err < 0)
537		telephony_key_press_rsp(telephony_device,
538							CME_ERROR_AG_FAILURE);
539	else
540		telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
541}
542
543void telephony_voice_dial_req(void *telephony_device, gboolean enable)
544{
545	DBG("telephony-ofono: got %s voice dial request",
546			enable ? "enable" : "disable");
547
548	telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
549}
550
551static gboolean iter_get_basic_args(DBusMessageIter *iter,
552					int first_arg_type, ...)
553{
554	int type;
555	va_list ap;
556
557	va_start(ap, first_arg_type);
558
559	for (type = first_arg_type; type != DBUS_TYPE_INVALID;
560		type = va_arg(ap, int)) {
561		void *value = va_arg(ap, void *);
562		int real_type = dbus_message_iter_get_arg_type(iter);
563
564		if (real_type != type) {
565			error("iter_get_basic_args: expected %c but got %c",
566				(char) type, (char) real_type);
567			break;
568		}
569
570		dbus_message_iter_get_basic(iter, value);
571		dbus_message_iter_next(iter);
572	}
573
574	va_end(ap);
575
576	return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
577}
578
579static void call_free(struct voice_call *vc)
580{
581	DBG("%s", vc->obj_path);
582
583	if (vc->status == CALL_STATUS_ACTIVE)
584		telephony_update_indicator(ofono_indicators, "call",
585							EV_CALL_INACTIVE);
586	else
587		telephony_update_indicator(ofono_indicators, "callsetup",
588							EV_CALLSETUP_INACTIVE);
589
590	if (vc->status == CALL_STATUS_INCOMING)
591		telephony_calling_stopped_ind();
592
593	g_dbus_remove_watch(connection, vc->watch);
594	g_free(vc->obj_path);
595	g_free(vc->number);
596	g_free(vc);
597}
598
599static gboolean handle_vc_property_changed(DBusConnection *conn,
600					DBusMessage *msg, void *data)
601{
602	struct voice_call *vc = data;
603	const char *obj_path = dbus_message_get_path(msg);
604	DBusMessageIter iter, sub;
605	const char *property, *state;
606
607	DBG("path %s", obj_path);
608
609	dbus_message_iter_init(msg, &iter);
610
611	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
612		error("Unexpected signature in vc PropertyChanged signal");
613		return TRUE;
614	}
615
616	dbus_message_iter_get_basic(&iter, &property);
617	DBG("property %s", property);
618
619	dbus_message_iter_next(&iter);
620	dbus_message_iter_recurse(&iter, &sub);
621	if (g_str_equal(property, "State")) {
622		dbus_message_iter_get_basic(&sub, &state);
623		DBG("State %s", state);
624		if (g_str_equal(state, "disconnected")) {
625			calls = g_slist_remove(calls, vc);
626			call_free(vc);
627		} else if (g_str_equal(state, "active")) {
628			telephony_update_indicator(ofono_indicators,
629							"call", EV_CALL_ACTIVE);
630			telephony_update_indicator(ofono_indicators,
631							"callsetup",
632							EV_CALLSETUP_INACTIVE);
633			if (vc->status == CALL_STATUS_INCOMING)
634				telephony_calling_stopped_ind();
635			vc->status = CALL_STATUS_ACTIVE;
636		} else if (g_str_equal(state, "alerting")) {
637			telephony_update_indicator(ofono_indicators,
638					"callsetup", EV_CALLSETUP_ALERTING);
639			vc->status = CALL_STATUS_ALERTING;
640			vc->originating = TRUE;
641		} else if (g_str_equal(state, "incoming")) {
642			/* state change from waiting to incoming */
643			telephony_update_indicator(ofono_indicators,
644					"callsetup", EV_CALLSETUP_INCOMING);
645			telephony_incoming_call_ind(vc->number,
646						NUMBER_TYPE_TELEPHONY);
647			vc->status = CALL_STATUS_INCOMING;
648			vc->originating = FALSE;
649		} else if (g_str_equal(state, "held")) {
650			vc->status = CALL_STATUS_HELD;
651			if (find_vc_without_status(CALL_STATUS_HELD))
652				telephony_update_indicator(ofono_indicators,
653							"callheld",
654							EV_CALLHELD_MULTIPLE);
655			else
656				telephony_update_indicator(ofono_indicators,
657							"callheld",
658							EV_CALLHELD_ON_HOLD);
659		}
660	}
661
662	return TRUE;
663}
664
665static struct voice_call *call_new(const char *path, DBusMessageIter *properties)
666{
667	struct voice_call *vc;
668
669	DBG("%s", path);
670
671	vc = g_new0(struct voice_call, 1);
672	vc->obj_path = g_strdup(path);
673	vc->watch = g_dbus_add_signal_watch(connection, NULL, path,
674					OFONO_VC_INTERFACE, "PropertyChanged",
675					handle_vc_property_changed, vc, NULL);
676
677	while (dbus_message_iter_get_arg_type(properties)
678						== DBUS_TYPE_DICT_ENTRY) {
679		DBusMessageIter entry, value;
680		const char *property, *cli, *state;
681
682		dbus_message_iter_recurse(properties, &entry);
683		dbus_message_iter_get_basic(&entry, &property);
684
685		dbus_message_iter_next(&entry);
686		dbus_message_iter_recurse(&entry, &value);
687
688		if (g_str_equal(property, "LineIdentification")) {
689			dbus_message_iter_get_basic(&value, &cli);
690			DBG("cli %s", cli);
691			vc->number = g_strdup(cli);
692		} else if (g_str_equal(property, "State")) {
693			dbus_message_iter_get_basic(&value, &state);
694			DBG("state %s", state);
695			if (g_str_equal(state, "incoming"))
696				vc->status = CALL_STATUS_INCOMING;
697			else if (g_str_equal(state, "dialing"))
698				vc->status = CALL_STATUS_DIALING;
699			else if (g_str_equal(state, "alerting"))
700				vc->status = CALL_STATUS_ALERTING;
701			else if (g_str_equal(state, "waiting"))
702				vc->status = CALL_STATUS_WAITING;
703			else if (g_str_equal(state, "held"))
704				vc->status = CALL_STATUS_HELD;
705		}
706
707		dbus_message_iter_next(properties);
708	}
709
710	switch (vc->status) {
711	case CALL_STATUS_INCOMING:
712		DBG("CALL_STATUS_INCOMING");
713		vc->originating = FALSE;
714		telephony_update_indicator(ofono_indicators, "callsetup",
715					EV_CALLSETUP_INCOMING);
716		telephony_incoming_call_ind(vc->number, NUMBER_TYPE_TELEPHONY);
717		break;
718	case CALL_STATUS_DIALING:
719		DBG("CALL_STATUS_DIALING");
720		vc->originating = TRUE;
721		g_free(last_dialed_number);
722		last_dialed_number = g_strdup(vc->number);
723		telephony_update_indicator(ofono_indicators, "callsetup",
724					EV_CALLSETUP_OUTGOING);
725		break;
726	case CALL_STATUS_ALERTING:
727		DBG("CALL_STATUS_ALERTING");
728		vc->originating = TRUE;
729		g_free(last_dialed_number);
730		last_dialed_number = g_strdup(vc->number);
731		telephony_update_indicator(ofono_indicators, "callsetup",
732					EV_CALLSETUP_ALERTING);
733		break;
734	case CALL_STATUS_WAITING:
735		DBG("CALL_STATUS_WAITING");
736		vc->originating = FALSE;
737		telephony_update_indicator(ofono_indicators, "callsetup",
738					EV_CALLSETUP_INCOMING);
739		telephony_call_waiting_ind(vc->number, NUMBER_TYPE_TELEPHONY);
740		break;
741	}
742
743	return vc;
744}
745
746static void remove_pending(DBusPendingCall *call)
747{
748	pending = g_slist_remove(pending, call);
749	dbus_pending_call_unref(call);
750}
751
752static void call_added(const char *path, DBusMessageIter *properties)
753{
754	struct voice_call *vc;
755
756	DBG("%s", path);
757
758	vc = find_vc(path);
759	if (vc)
760		return;
761
762	vc = call_new(path, properties);
763	calls = g_slist_prepend(calls, vc);
764}
765
766static void get_calls_reply(DBusPendingCall *call, void *user_data)
767{
768	DBusError err;
769	DBusMessage *reply;
770	DBusMessageIter iter, entry;
771
772	DBG("");
773	reply = dbus_pending_call_steal_reply(call);
774
775	dbus_error_init(&err);
776	if (dbus_set_error_from_message(&err, reply)) {
777		error("ofono replied with an error: %s, %s",
778				err.name, err.message);
779		dbus_error_free(&err);
780		goto done;
781	}
782
783	dbus_message_iter_init(reply, &iter);
784
785	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
786		error("Unexpected signature");
787		goto done;
788	}
789
790	dbus_message_iter_recurse(&iter, &entry);
791
792	while (dbus_message_iter_get_arg_type(&entry)
793						== DBUS_TYPE_STRUCT) {
794		const char *path;
795		DBusMessageIter value, properties;
796
797		dbus_message_iter_recurse(&entry, &value);
798		dbus_message_iter_get_basic(&value, &path);
799
800		dbus_message_iter_next(&value);
801		dbus_message_iter_recurse(&value, &properties);
802
803		call_added(path, &properties);
804
805		dbus_message_iter_next(&entry);
806	}
807
808done:
809	dbus_message_unref(reply);
810	remove_pending(call);
811}
812
813static void handle_network_property(const char *property, DBusMessageIter *variant)
814{
815	const char *status, *operator;
816	unsigned int signals_bar;
817
818	if (g_str_equal(property, "Status")) {
819		dbus_message_iter_get_basic(variant, &status);
820		DBG("Status is %s", status);
821		if (g_str_equal(status, "registered")) {
822			net.status = NETWORK_REG_STATUS_HOME;
823			telephony_update_indicator(ofono_indicators,
824						"roam", EV_ROAM_INACTIVE);
825			telephony_update_indicator(ofono_indicators,
826						"service", EV_SERVICE_PRESENT);
827		} else if (g_str_equal(status, "roaming")) {
828			net.status = NETWORK_REG_STATUS_ROAM;
829			telephony_update_indicator(ofono_indicators,
830						"roam", EV_ROAM_ACTIVE);
831			telephony_update_indicator(ofono_indicators,
832						"service", EV_SERVICE_PRESENT);
833		} else {
834			net.status = NETWORK_REG_STATUS_NOSERV;
835			telephony_update_indicator(ofono_indicators,
836						"roam", EV_ROAM_INACTIVE);
837			telephony_update_indicator(ofono_indicators,
838						"service", EV_SERVICE_NONE);
839		}
840	} else if (g_str_equal(property, "Name")) {
841		dbus_message_iter_get_basic(variant, &operator);
842		DBG("Operator is %s", operator);
843		g_free(net.operator_name);
844		net.operator_name = g_strdup(operator);
845	} else if (g_str_equal(property, "SignalStrength")) {
846		dbus_message_iter_get_basic(variant, &signals_bar);
847		DBG("SignalStrength is %d", signals_bar);
848		net.signals_bar = signals_bar;
849		telephony_update_indicator(ofono_indicators, "signal",
850						(signals_bar + 20) / 21);
851	}
852}
853
854static int parse_network_properties(DBusMessageIter *properties)
855{
856	uint32_t features = AG_FEATURE_EC_ANDOR_NR |
857				AG_FEATURE_INBAND_RINGTONE |
858				AG_FEATURE_REJECT_A_CALL |
859				AG_FEATURE_ENHANCED_CALL_STATUS |
860				AG_FEATURE_ENHANCED_CALL_CONTROL |
861				AG_FEATURE_EXTENDED_ERROR_RESULT_CODES;
862
863	while (dbus_message_iter_get_arg_type(properties)
864						== DBUS_TYPE_DICT_ENTRY) {
865		const char *key;
866		DBusMessageIter value, entry;
867
868		dbus_message_iter_recurse(properties, &entry);
869		dbus_message_iter_get_basic(&entry, &key);
870
871		dbus_message_iter_next(&entry);
872		dbus_message_iter_recurse(&entry, &value);
873
874		handle_network_property(key, &value);
875
876		dbus_message_iter_next(properties);
877	}
878
879	telephony_ready_ind(features, ofono_indicators, BTRH_NOT_SUPPORTED,
880								chld_str);
881
882	return 0;
883}
884
885static void get_properties_reply(DBusPendingCall *call, void *user_data)
886{
887	DBusError err;
888	DBusMessage *reply;
889	DBusMessageIter iter, properties;
890	int ret = 0;
891
892	DBG("");
893	reply = dbus_pending_call_steal_reply(call);
894
895	dbus_error_init(&err);
896	if (dbus_set_error_from_message(&err, reply)) {
897		error("ofono replied with an error: %s, %s",
898				err.name, err.message);
899		dbus_error_free(&err);
900		goto done;
901	}
902
903	dbus_message_iter_init(reply, &iter);
904
905	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
906		error("Unexpected signature");
907		goto done;
908	}
909
910	dbus_message_iter_recurse(&iter, &properties);
911
912	ret = parse_network_properties(&properties);
913	if (ret < 0) {
914		error("Unable to parse %s.GetProperty reply",
915						OFONO_NETWORKREG_INTERFACE);
916		goto done;
917	}
918
919	ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
920				OFONO_VCMANAGER_INTERFACE, "GetCalls",
921				get_calls_reply, NULL, DBUS_TYPE_INVALID);
922	if (ret < 0)
923		error("Unable to send %s.GetCalls",
924						OFONO_VCMANAGER_INTERFACE);
925
926done:
927	dbus_message_unref(reply);
928	remove_pending(call);
929}
930
931static void network_found(const char *path)
932{
933	int ret;
934
935	DBG("%s", path);
936
937	modem_obj_path = g_strdup(path);
938
939	ret = send_method_call(OFONO_BUS_NAME, path,
940				OFONO_NETWORKREG_INTERFACE, "GetProperties",
941				get_properties_reply, NULL, DBUS_TYPE_INVALID);
942	if (ret < 0)
943		error("Unable to send %s.GetProperties",
944						OFONO_NETWORKREG_INTERFACE);
945}
946
947static void modem_removed(const char *path)
948{
949	if (g_strcmp0(modem_obj_path, path) != 0)
950		return;
951
952	DBG("%s", path);
953
954	g_slist_foreach(calls, (GFunc) call_free, NULL);
955	g_slist_free(calls);
956	calls = NULL;
957
958	g_free(net.operator_name);
959	net.operator_name = NULL;
960
961	g_free(modem_obj_path);
962	modem_obj_path = NULL;
963}
964
965static void parse_modem_interfaces(const char *path, DBusMessageIter *ifaces)
966{
967	DBG("%s", path);
968
969	while (dbus_message_iter_get_arg_type(ifaces) == DBUS_TYPE_STRING) {
970		const char *iface;
971
972		dbus_message_iter_get_basic(ifaces, &iface);
973
974		if (g_str_equal(iface, OFONO_NETWORKREG_INTERFACE)) {
975			network_found(path);
976			return;
977		}
978
979		dbus_message_iter_next(ifaces);
980	}
981
982	modem_removed(path);
983}
984
985static void modem_added(const char *path, DBusMessageIter *properties)
986{
987	if (modem_obj_path != NULL) {
988		DBG("Ignoring, modem already exist");
989		return;
990	}
991
992	DBG("%s", path);
993
994	while (dbus_message_iter_get_arg_type(properties)
995						== DBUS_TYPE_DICT_ENTRY) {
996		const char *key;
997		DBusMessageIter interfaces, value, entry;
998
999		dbus_message_iter_recurse(properties, &entry);
1000		dbus_message_iter_get_basic(&entry, &key);
1001
1002		dbus_message_iter_next(&entry);
1003		dbus_message_iter_recurse(&entry, &value);
1004
1005		if (strcasecmp(key, "Interfaces") != 0)
1006			goto next;
1007
1008		if (dbus_message_iter_get_arg_type(&value)
1009							!= DBUS_TYPE_ARRAY) {
1010			error("Invalid Signature");
1011			return;
1012		}
1013
1014		dbus_message_iter_recurse(&value, &interfaces);
1015
1016		parse_modem_interfaces(path, &interfaces);
1017
1018		if (modem_obj_path != NULL)
1019			return;
1020
1021	next:
1022		dbus_message_iter_next(properties);
1023	}
1024}
1025
1026static void get_modems_reply(DBusPendingCall *call, void *user_data)
1027{
1028	DBusError err;
1029	DBusMessage *reply;
1030	DBusMessageIter iter, entry;
1031
1032	DBG("list_modem_reply is called\n");
1033	reply = dbus_pending_call_steal_reply(call);
1034
1035	dbus_error_init(&err);
1036	if (dbus_set_error_from_message(&err, reply)) {
1037		error("ofono replied with an error: %s, %s",
1038				err.name, err.message);
1039		dbus_error_free(&err);
1040		goto done;
1041	}
1042
1043	/* Skip modem selection if a modem already exist */
1044	if (modem_obj_path != NULL)
1045		goto done;
1046
1047	dbus_message_iter_init(reply, &iter);
1048
1049	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1050		error("Unexpected signature");
1051		goto done;
1052	}
1053
1054	dbus_message_iter_recurse(&iter, &entry);
1055
1056	while (dbus_message_iter_get_arg_type(&entry)
1057						== DBUS_TYPE_STRUCT) {
1058		const char *path;
1059		DBusMessageIter item, properties;
1060
1061		dbus_message_iter_recurse(&entry, &item);
1062		dbus_message_iter_get_basic(&item, &path);
1063
1064		dbus_message_iter_next(&item);
1065		dbus_message_iter_recurse(&item, &properties);
1066
1067		modem_added(path, &properties);
1068		if (modem_obj_path != NULL)
1069			break;
1070
1071		dbus_message_iter_next(&entry);
1072	}
1073
1074done:
1075	dbus_message_unref(reply);
1076	remove_pending(call);
1077}
1078
1079static gboolean handle_network_property_changed(DBusConnection *conn,
1080						DBusMessage *msg, void *data)
1081{
1082	DBusMessageIter iter, variant;
1083	const char *property;
1084
1085	dbus_message_iter_init(msg, &iter);
1086
1087	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
1088		error("Unexpected signature in networkregistration"
1089					" PropertyChanged signal");
1090		return TRUE;
1091	}
1092	dbus_message_iter_get_basic(&iter, &property);
1093	DBG("in handle_registration_property_changed(),"
1094					" the property is %s", property);
1095
1096	dbus_message_iter_next(&iter);
1097	dbus_message_iter_recurse(&iter, &variant);
1098
1099	handle_network_property(property, &variant);
1100
1101	return TRUE;
1102}
1103
1104static void handle_modem_property(const char *path, const char *property,
1105						DBusMessageIter *variant)
1106{
1107	DBG("%s", property);
1108
1109	if (g_str_equal(property, "Interfaces")) {
1110		DBusMessageIter interfaces;
1111
1112		if (dbus_message_iter_get_arg_type(variant)
1113							!= DBUS_TYPE_ARRAY) {
1114			error("Invalid signature");
1115			return;
1116		}
1117
1118		dbus_message_iter_recurse(variant, &interfaces);
1119		parse_modem_interfaces(path, &interfaces);
1120	}
1121}
1122
1123static gboolean handle_modem_property_changed(DBusConnection *conn,
1124						DBusMessage *msg, void *data)
1125{
1126	DBusMessageIter iter, variant;
1127	const char *property, *path;
1128
1129	path = dbus_message_get_path(msg);
1130
1131	/* Ignore if modem already exist and paths doesn't match */
1132	if (modem_obj_path != NULL &&
1133				g_str_equal(path, modem_obj_path) == FALSE)
1134		return TRUE;
1135
1136	dbus_message_iter_init(msg, &iter);
1137
1138	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
1139		error("Unexpected signature in %s.%s PropertyChanged signal",
1140					dbus_message_get_interface(msg),
1141					dbus_message_get_member(msg));
1142		return TRUE;
1143	}
1144
1145	dbus_message_iter_get_basic(&iter, &property);
1146
1147	dbus_message_iter_next(&iter);
1148	dbus_message_iter_recurse(&iter, &variant);
1149
1150	handle_modem_property(path, property, &variant);
1151
1152	return TRUE;
1153}
1154
1155static gboolean handle_vcmanager_call_added(DBusConnection *conn,
1156						DBusMessage *msg, void *data)
1157{
1158	DBusMessageIter iter, properties;
1159	const char *path = dbus_message_get_path(msg);
1160
1161	/* Ignore call if modem path doesn't math */
1162	if (g_strcmp0(modem_obj_path, path) != 0)
1163		return TRUE;
1164
1165	dbus_message_iter_init(msg, &iter);
1166
1167	if (dbus_message_iter_get_arg_type(&iter)
1168						!= DBUS_TYPE_OBJECT_PATH) {
1169		error("Unexpected signature in %s.%s signal",
1170					dbus_message_get_interface(msg),
1171					dbus_message_get_member(msg));
1172		return TRUE;
1173	}
1174
1175	dbus_message_iter_get_basic(&iter, &path);
1176	dbus_message_iter_next(&iter);
1177	dbus_message_iter_recurse(&iter, &properties);
1178
1179	call_added(path, &properties);
1180
1181	return TRUE;
1182}
1183
1184static void call_removed(const char *path)
1185{
1186	struct voice_call *vc;
1187
1188	DBG("%s", path);
1189
1190	vc = find_vc(path);
1191	if (vc == NULL)
1192		return;
1193
1194	calls = g_slist_remove(calls, vc);
1195	call_free(vc);
1196}
1197
1198static gboolean handle_vcmanager_call_removed(DBusConnection *conn,
1199						DBusMessage *msg, void *data)
1200{
1201	const char *path = dbus_message_get_path(msg);
1202
1203	/* Ignore call if modem path doesn't math */
1204	if (g_strcmp0(modem_obj_path, path) != 0)
1205		return TRUE;
1206
1207	if (!dbus_message_get_args(msg, NULL,
1208				DBUS_TYPE_OBJECT_PATH, &path,
1209				DBUS_TYPE_INVALID)) {
1210		error("Unexpected signature in %s.%s signal",
1211					dbus_message_get_interface(msg),
1212					dbus_message_get_member(msg));
1213		return TRUE;
1214	}
1215
1216	call_removed(path);
1217
1218	return TRUE;
1219}
1220
1221static gboolean handle_manager_modem_added(DBusConnection *conn,
1222						DBusMessage *msg, void *data)
1223{
1224	DBusMessageIter iter, properties;
1225	const char *path;
1226
1227	if (modem_obj_path != NULL)
1228		return TRUE;
1229
1230	dbus_message_iter_init(msg, &iter);
1231
1232	if (dbus_message_iter_get_arg_type(&iter)
1233						!= DBUS_TYPE_OBJECT_PATH) {
1234		error("Unexpected signature in %s.%s signal",
1235					dbus_message_get_interface(msg),
1236					dbus_message_get_member(msg));
1237		return TRUE;
1238	}
1239
1240	dbus_message_iter_get_basic(&iter, &path);
1241	dbus_message_iter_next(&iter);
1242	dbus_message_iter_recurse(&iter, &properties);
1243
1244	modem_added(path, &properties);
1245
1246	return TRUE;
1247}
1248
1249static gboolean handle_manager_modem_removed(DBusConnection *conn,
1250						DBusMessage *msg, void *data)
1251{
1252	const char *path;
1253
1254	if (!dbus_message_get_args(msg, NULL,
1255				DBUS_TYPE_OBJECT_PATH, &path,
1256				DBUS_TYPE_INVALID)) {
1257		error("Unexpected signature in %s.%s signal",
1258					dbus_message_get_interface(msg),
1259					dbus_message_get_member(msg));
1260		return TRUE;
1261	}
1262
1263	modem_removed(path);
1264
1265	return TRUE;
1266}
1267
1268static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
1269{
1270	DBusMessage *reply;
1271	DBusError err;
1272	dbus_int32_t level;
1273	int *value = user_data;
1274
1275	reply = dbus_pending_call_steal_reply(call);
1276
1277	dbus_error_init(&err);
1278	if (dbus_set_error_from_message(&err, reply)) {
1279		error("hald replied with an error: %s, %s",
1280				err.name, err.message);
1281		dbus_error_free(&err);
1282		goto done;
1283	}
1284
1285	dbus_error_init(&err);
1286	if (dbus_message_get_args(reply, &err,
1287				DBUS_TYPE_INT32, &level,
1288				DBUS_TYPE_INVALID) == FALSE) {
1289		error("Unable to parse GetPropertyInteger reply: %s, %s",
1290							err.name, err.message);
1291		dbus_error_free(&err);
1292		goto done;
1293	}
1294
1295	*value = (int) level;
1296
1297	if (value == &battchg_last)
1298		DBG("telephony-ofono: battery.charge_level.last_full"
1299					" is %d", *value);
1300	else if (value == &battchg_design)
1301		DBG("telephony-ofono: battery.charge_level.design"
1302					" is %d", *value);
1303	else
1304		DBG("telephony-ofono: battery.charge_level.current"
1305					" is %d", *value);
1306
1307	if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
1308		int new, max;
1309
1310		if (battchg_last > 0)
1311			max = battchg_last;
1312		else
1313			max = battchg_design;
1314
1315		new = battchg_cur * 5 / max;
1316
1317		telephony_update_indicator(ofono_indicators, "battchg", new);
1318	}
1319done:
1320	dbus_message_unref(reply);
1321	remove_pending(call);
1322}
1323
1324static void hal_get_integer(const char *path, const char *key, void *user_data)
1325{
1326	send_method_call("org.freedesktop.Hal", path,
1327			"org.freedesktop.Hal.Device",
1328			"GetPropertyInteger",
1329			hal_battery_level_reply, user_data,
1330			DBUS_TYPE_STRING, &key,
1331			DBUS_TYPE_INVALID);
1332}
1333
1334static gboolean handle_hal_property_modified(DBusConnection *conn,
1335						DBusMessage *msg, void *data)
1336{
1337	const char *path;
1338	DBusMessageIter iter, array;
1339	dbus_int32_t num_changes;
1340
1341	path = dbus_message_get_path(msg);
1342
1343	dbus_message_iter_init(msg, &iter);
1344
1345	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
1346		error("Unexpected signature in hal PropertyModified signal");
1347		return TRUE;
1348	}
1349
1350	dbus_message_iter_get_basic(&iter, &num_changes);
1351	dbus_message_iter_next(&iter);
1352
1353	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1354		error("Unexpected signature in hal PropertyModified signal");
1355		return TRUE;
1356	}
1357
1358	dbus_message_iter_recurse(&iter, &array);
1359
1360	while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
1361		DBusMessageIter prop;
1362		const char *name;
1363		dbus_bool_t added, removed;
1364
1365		dbus_message_iter_recurse(&array, &prop);
1366
1367		if (!iter_get_basic_args(&prop,
1368					DBUS_TYPE_STRING, &name,
1369					DBUS_TYPE_BOOLEAN, &added,
1370					DBUS_TYPE_BOOLEAN, &removed,
1371					DBUS_TYPE_INVALID)) {
1372			error("Invalid hal PropertyModified parameters");
1373			break;
1374		}
1375
1376		if (g_str_equal(name, "battery.charge_level.last_full"))
1377			hal_get_integer(path, name, &battchg_last);
1378		else if (g_str_equal(name, "battery.charge_level.current"))
1379			hal_get_integer(path, name, &battchg_cur);
1380		else if (g_str_equal(name, "battery.charge_level.design"))
1381			hal_get_integer(path, name, &battchg_design);
1382
1383		dbus_message_iter_next(&array);
1384	}
1385
1386	return TRUE;
1387}
1388
1389static void add_watch(const char *sender, const char *path,
1390				const char *interface, const char *member,
1391				GDBusSignalFunction function)
1392{
1393	guint watch;
1394
1395	watch = g_dbus_add_signal_watch(connection, sender, path, interface,
1396					member, function, NULL, NULL);
1397
1398	watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
1399}
1400
1401static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
1402{
1403	DBusMessage *reply;
1404	DBusError err;
1405	DBusMessageIter iter, sub;
1406	int type;
1407	const char *path;
1408
1409	DBG("begin of hal_find_device_reply()");
1410	reply = dbus_pending_call_steal_reply(call);
1411
1412	dbus_error_init(&err);
1413
1414	if (dbus_set_error_from_message(&err, reply)) {
1415		error("hald replied with an error: %s, %s",
1416				err.name, err.message);
1417		dbus_error_free(&err);
1418		goto done;
1419	}
1420
1421	dbus_message_iter_init(reply, &iter);
1422
1423	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1424		error("Unexpected signature in hal_find_device_reply()");
1425		goto done;
1426	}
1427
1428	dbus_message_iter_recurse(&iter, &sub);
1429
1430	type = dbus_message_iter_get_arg_type(&sub);
1431
1432	if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
1433		error("No hal device with battery capability found");
1434		goto done;
1435	}
1436
1437	dbus_message_iter_get_basic(&sub, &path);
1438
1439	DBG("telephony-ofono: found battery device at %s", path);
1440
1441	add_watch(NULL, path, "org.freedesktop.Hal.Device",
1442			"PropertyModified", handle_hal_property_modified);
1443
1444	hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
1445	hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
1446	hal_get_integer(path, "battery.charge_level.design", &battchg_design);
1447done:
1448	dbus_message_unref(reply);
1449	remove_pending(call);
1450}
1451
1452static void handle_service_connect(DBusConnection *conn, void *user_data)
1453{
1454	DBG("telephony-ofono: %s found", OFONO_BUS_NAME);
1455
1456	send_method_call(OFONO_BUS_NAME, OFONO_PATH,
1457				OFONO_MANAGER_INTERFACE, "GetModems",
1458				get_modems_reply, NULL, DBUS_TYPE_INVALID);
1459}
1460
1461static void handle_service_disconnect(DBusConnection *conn, void *user_data)
1462{
1463	DBG("telephony-ofono: %s exitted", OFONO_BUS_NAME);
1464
1465	if (modem_obj_path)
1466		modem_removed(modem_obj_path);
1467}
1468
1469int telephony_init(void)
1470{
1471	const char *battery_cap = "battery";
1472	int ret;
1473	guint watch;
1474
1475	connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
1476
1477	add_watch(OFONO_BUS_NAME, NULL, OFONO_MODEM_INTERFACE,
1478			"PropertyChanged", handle_modem_property_changed);
1479	add_watch(OFONO_BUS_NAME, NULL, OFONO_NETWORKREG_INTERFACE,
1480			"PropertyChanged", handle_network_property_changed);
1481	add_watch(OFONO_BUS_NAME, NULL, OFONO_MANAGER_INTERFACE,
1482			"ModemAdded", handle_manager_modem_added);
1483	add_watch(OFONO_BUS_NAME, NULL, OFONO_MANAGER_INTERFACE,
1484			"ModemRemoved", handle_manager_modem_removed);
1485	add_watch(OFONO_BUS_NAME, NULL, OFONO_VCMANAGER_INTERFACE,
1486			"CallAdded", handle_vcmanager_call_added);
1487	add_watch(OFONO_BUS_NAME, NULL, OFONO_VCMANAGER_INTERFACE,
1488			"CallRemoved", handle_vcmanager_call_removed);
1489
1490	watch = g_dbus_add_service_watch(connection, OFONO_BUS_NAME,
1491						handle_service_connect,
1492						handle_service_disconnect,
1493						NULL, NULL);
1494	if (watch == 0)
1495		return -ENOMEM;
1496
1497	watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
1498
1499	ret = send_method_call("org.freedesktop.Hal",
1500				"/org/freedesktop/Hal/Manager",
1501				"org.freedesktop.Hal.Manager",
1502				"FindDeviceByCapability",
1503				hal_find_device_reply, NULL,
1504				DBUS_TYPE_STRING, &battery_cap,
1505				DBUS_TYPE_INVALID);
1506	if (ret < 0)
1507		return ret;
1508
1509	DBG("telephony_init() successfully");
1510
1511	return ret;
1512}
1513
1514static void remove_watch(gpointer data)
1515{
1516	g_dbus_remove_watch(connection, GPOINTER_TO_UINT(data));
1517}
1518
1519void telephony_exit(void)
1520{
1521	DBG("");
1522
1523	g_free(last_dialed_number);
1524	last_dialed_number = NULL;
1525
1526	if (modem_obj_path)
1527		modem_removed(modem_obj_path);
1528
1529	g_slist_foreach(watches, (GFunc) remove_watch, NULL);
1530	g_slist_free(watches);
1531	watches = NULL;
1532
1533	g_slist_foreach(pending, (GFunc) dbus_pending_call_cancel, NULL);
1534	g_slist_foreach(pending, (GFunc) dbus_pending_call_unref, NULL);
1535	g_slist_free(pending);
1536	pending = NULL;
1537
1538	dbus_connection_unref(connection);
1539	connection = NULL;
1540
1541	telephony_deinit();
1542}
1543