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