1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2008-2010  Nokia Corporation
6 *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
9 *  This program is free software; you can redistribute it and/or modify
10 *  it under the terms of the GNU General Public License as published by
11 *  the Free Software Foundation; either version 2 of the License, or
12 *  (at your option) any later version.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include <stdlib.h>
30#include <stdio.h>
31#include <unistd.h>
32#include <fcntl.h>
33#include <stdint.h>
34#include <string.h>
35#include <glib.h>
36#include <dbus/dbus.h>
37#include <gdbus.h>
38
39#include "log.h"
40#include "telephony.h"
41
42/* SSC D-Bus definitions */
43#define SSC_DBUS_NAME  "com.nokia.phone.SSC"
44#define SSC_DBUS_IFACE "com.nokia.phone.SSC"
45#define SSC_DBUS_PATH  "/com/nokia/phone/SSC"
46
47/* libcsnet D-Bus definitions */
48#define CSD_CSNET_BUS_NAME	"com.nokia.csd.CSNet"
49#define CSD_CSNET_PATH		"/com/nokia/csd/csnet"
50#define CSD_CSNET_IFACE		"com.nokia.csd.CSNet"
51#define CSD_CSNET_REGISTRATION	"com.nokia.csd.CSNet.NetworkRegistration"
52#define CSD_CSNET_OPERATOR	"com.nokia.csd.CSNet.NetworkOperator"
53#define CSD_CSNET_SIGNAL	"com.nokia.csd.CSNet.SignalStrength"
54
55enum net_registration_status {
56	NETWORK_REG_STATUS_HOME,
57	NETWORK_REG_STATUS_ROAMING,
58	NETWORK_REG_STATUS_OFFLINE,
59	NETWORK_REG_STATUS_SEARCHING,
60	NETWORK_REG_STATUS_NO_SIM,
61	NETWORK_REG_STATUS_POWEROFF,
62	NETWORK_REG_STATUS_POWERSAFE,
63	NETWORK_REG_STATUS_NO_COVERAGE,
64	NETWORK_REG_STATUS_REJECTED,
65	NETWORK_REG_STATUS_UNKOWN
66};
67
68/* Driver definitions */
69#define TELEPHONY_MAEMO_PATH		"/com/nokia/MaemoTelephony"
70#define TELEPHONY_MAEMO_INTERFACE	"com.nokia.MaemoTelephony"
71
72#define CALLERID_BASE		"/var/lib/bluetooth/maemo-callerid-"
73#define ALLOWED_FLAG_FILE	"/var/lib/bluetooth/maemo-callerid-allowed"
74#define RESTRICTED_FLAG_FILE	"/var/lib/bluetooth/maemo-callerid-restricted"
75#define NONE_FLAG_FILE		"/var/lib/bluetooth/maemo-callerid-none"
76
77static uint32_t callerid = 0;
78
79/* CSD CALL plugin D-Bus definitions */
80#define CSD_CALL_BUS_NAME	"com.nokia.csd.Call"
81#define CSD_CALL_INTERFACE	"com.nokia.csd.Call"
82#define CSD_CALL_INSTANCE	"com.nokia.csd.Call.Instance"
83#define CSD_CALL_CONFERENCE	"com.nokia.csd.Call.Conference"
84#define CSD_CALL_PATH		"/com/nokia/csd/call"
85#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference"
86
87/* Call status values as exported by the CSD CALL plugin */
88#define CSD_CALL_STATUS_IDLE			0
89#define CSD_CALL_STATUS_CREATE			1
90#define CSD_CALL_STATUS_COMING			2
91#define CSD_CALL_STATUS_PROCEEDING		3
92#define CSD_CALL_STATUS_MO_ALERTING		4
93#define CSD_CALL_STATUS_MT_ALERTING		5
94#define CSD_CALL_STATUS_WAITING			6
95#define CSD_CALL_STATUS_ANSWERED		7
96#define CSD_CALL_STATUS_ACTIVE			8
97#define CSD_CALL_STATUS_MO_RELEASE		9
98#define CSD_CALL_STATUS_MT_RELEASE		10
99#define CSD_CALL_STATUS_HOLD_INITIATED		11
100#define CSD_CALL_STATUS_HOLD			12
101#define CSD_CALL_STATUS_RETRIEVE_INITIATED	13
102#define CSD_CALL_STATUS_RECONNECT_PENDING	14
103#define CSD_CALL_STATUS_TERMINATED		15
104#define CSD_CALL_STATUS_SWAP_INITIATED		16
105
106#define CALL_FLAG_NONE				0
107#define CALL_FLAG_PRESENTATION_ALLOWED		0x01
108#define CALL_FLAG_PRESENTATION_RESTRICTED	0x02
109
110/* SIM Phonebook D-Bus definitions */
111#define CSD_SIMPB_BUS_NAME			"com.nokia.csd.SIM"
112#define CSD_SIMPB_INTERFACE			"com.nokia.csd.SIM.Phonebook"
113#define CSD_SIMPB_PATH				"/com/nokia/csd/sim/phonebook"
114
115#define CSD_SIMPB_TYPE_ADN			"ADN"
116#define CSD_SIMPB_TYPE_FDN			"FDN"
117#define CSD_SIMPB_TYPE_SDN			"SDN"
118#define CSD_SIMPB_TYPE_VMBX			"VMBX"
119#define CSD_SIMPB_TYPE_MBDN			"MBDN"
120#define CSD_SIMPB_TYPE_EN			"EN"
121#define CSD_SIMPB_TYPE_MSISDN			"MSISDN"
122
123struct csd_call {
124	char *object_path;
125	int status;
126	gboolean originating;
127	gboolean emergency;
128	gboolean on_hold;
129	gboolean conference;
130	char *number;
131	gboolean setup;
132};
133
134static struct {
135	char *operator_name;
136	uint8_t status;
137	int32_t signal_bars;
138} net = {
139	.operator_name = NULL,
140	.status = NETWORK_REG_STATUS_UNKOWN,
141	/* Init as 0 meaning inactive mode. In modem power off state
142	 * can be be -1, but we treat all values as 0s regardless
143	 * inactive or power off. */
144	.signal_bars = 0,
145};
146
147static int get_property(const char *iface, const char *prop);
148
149static DBusConnection *connection = NULL;
150
151static GSList *calls = NULL;
152
153/* Reference count for determining the call indicator status */
154static GSList *active_calls = NULL;
155
156static char *msisdn = NULL;	/* Subscriber number */
157static char *vmbx = NULL;	/* Voice mailbox number */
158
159/* HAL battery namespace key values */
160static int battchg_cur = -1;	/* "battery.charge_level.current" */
161static int battchg_last = -1;	/* "battery.charge_level.last_full" */
162static int battchg_design = -1;	/* "battery.charge_level.design" */
163
164static gboolean get_calls_active = FALSE;
165
166static gboolean events_enabled = FALSE;
167
168/* Supported set of call hold operations */
169static const char *chld_str = "0,1,1x,2,2x,3,4";
170
171/* Response and hold state
172 * -1 = none
173 *  0 = incoming call is put on hold in the AG
174 *  1 = held incoming call is accepted in the AG
175 *  2 = held incoming call is rejected in the AG
176 */
177static int response_and_hold = -1;
178
179static char *last_dialed_number = NULL;
180
181/* Timer for tracking call creation requests */
182static guint create_request_timer = 0;
183
184static struct indicator maemo_indicators[] =
185{
186	{ "battchg",	"0-5",	5,	TRUE },
187	/* signal strength in terms of bars */
188	{ "signal",	"0-5",	0,	TRUE },
189	{ "service",	"0,1",	0,	TRUE },
190	{ "call",	"0,1",	0,	TRUE },
191	{ "callsetup",	"0-3",	0,	TRUE },
192	{ "callheld",	"0-2",	0,	FALSE },
193	{ "roam",	"0,1",	0,	TRUE },
194	{ NULL }
195};
196
197static char *call_status_str[] = {
198	"IDLE",
199	"CREATE",
200	"COMING",
201	"PROCEEDING",
202	"MO_ALERTING",
203	"MT_ALERTING",
204	"WAITING",
205	"ANSWERED",
206	"ACTIVE",
207	"MO_RELEASE",
208	"MT_RELEASE",
209	"HOLD_INITIATED",
210	"HOLD",
211	"RETRIEVE_INITIATED",
212	"RECONNECT_PENDING",
213	"TERMINATED",
214	"SWAP_INITIATED",
215	"???"
216};
217
218static struct csd_call *find_call(const char *path)
219{
220	GSList *l;
221
222	for (l = calls; l != NULL; l = l->next) {
223		struct csd_call *call = l->data;
224
225		if (g_str_equal(call->object_path, path))
226			return call;
227	}
228
229	return NULL;
230}
231
232static struct csd_call *find_non_held_call(void)
233{
234	GSList *l;
235
236	for (l = calls; l != NULL; l = l->next) {
237		struct csd_call *call = l->data;
238
239		if (call->status == CSD_CALL_STATUS_IDLE)
240			continue;
241
242		if (call->status != CSD_CALL_STATUS_HOLD)
243			return call;
244	}
245
246	return NULL;
247}
248
249static struct csd_call *find_non_idle_call(void)
250{
251	GSList *l;
252
253	for (l = calls; l != NULL; l = l->next) {
254		struct csd_call *call = l->data;
255
256		if (call->status != CSD_CALL_STATUS_IDLE)
257			return call;
258	}
259
260	return NULL;
261}
262
263static struct csd_call *find_call_with_status(int status)
264{
265	GSList *l;
266
267	for (l = calls; l != NULL; l = l->next) {
268		struct csd_call *call = l->data;
269
270		if (call->status == status)
271			return call;
272	}
273
274	return NULL;
275}
276
277static int release_conference(void)
278{
279	DBusMessage *msg;
280
281	DBG("telephony-maemo6: releasing conference call");
282
283	msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
284						CSD_CALL_CONFERENCE_PATH,
285						CSD_CALL_INSTANCE,
286						"Release");
287	if (!msg) {
288		error("Unable to allocate new D-Bus message");
289		return -ENOMEM;
290	}
291
292	g_dbus_send_message(connection, msg);
293
294	return 0;
295}
296
297static int release_call(struct csd_call *call)
298{
299	DBusMessage *msg;
300
301	msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
302						call->object_path,
303						CSD_CALL_INSTANCE,
304						"Release");
305	if (!msg) {
306		error("Unable to allocate new D-Bus message");
307		return -ENOMEM;
308	}
309
310	g_dbus_send_message(connection, msg);
311
312	return 0;
313}
314
315static int answer_call(struct csd_call *call)
316{
317	DBusMessage *msg;
318
319	msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
320						call->object_path,
321						CSD_CALL_INSTANCE,
322						"Answer");
323	if (!msg) {
324		error("Unable to allocate new D-Bus message");
325		return -ENOMEM;
326	}
327
328	g_dbus_send_message(connection, msg);
329
330	return 0;
331}
332
333static int split_call(struct csd_call *call)
334{
335	DBusMessage *msg;
336
337	msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
338						call->object_path,
339						CSD_CALL_INSTANCE,
340						"Split");
341	if (!msg) {
342		error("Unable to allocate new D-Bus message");
343		return -ENOMEM;
344	}
345
346	g_dbus_send_message(connection, msg);
347
348	return 0;
349}
350
351static int unhold_call(struct csd_call *call)
352{
353	DBusMessage *msg;
354
355	msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
356						CSD_CALL_INTERFACE,
357						"Unhold");
358	if (!msg) {
359		error("Unable to allocate new D-Bus message");
360		return -ENOMEM;
361	}
362
363	g_dbus_send_message(connection, msg);
364
365	return 0;
366}
367
368static int hold_call(struct csd_call *call)
369{
370	DBusMessage *msg;
371
372	msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
373						CSD_CALL_INTERFACE,
374						"Hold");
375	if (!msg) {
376		error("Unable to allocate new D-Bus message");
377		return -ENOMEM;
378	}
379
380	g_dbus_send_message(connection, msg);
381
382	return 0;
383}
384
385static int swap_calls(void)
386{
387	DBusMessage *msg;
388
389	msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
390						CSD_CALL_INTERFACE,
391						"Swap");
392	if (!msg) {
393		error("Unable to allocate new D-Bus message");
394		return -ENOMEM;
395	}
396
397	g_dbus_send_message(connection, msg);
398
399	return 0;
400}
401
402static int create_conference(void)
403{
404	DBusMessage *msg;
405
406	msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
407						CSD_CALL_INTERFACE,
408						"Conference");
409	if (!msg) {
410		error("Unable to allocate new D-Bus message");
411		return -ENOMEM;
412	}
413
414	g_dbus_send_message(connection, msg);
415
416	return 0;
417}
418
419static int call_transfer(void)
420{
421	DBusMessage *msg;
422
423	msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
424						CSD_CALL_INTERFACE,
425						"Transfer");
426	if (!msg) {
427		error("Unable to allocate new D-Bus message");
428		return -ENOMEM;
429	}
430
431	g_dbus_send_message(connection, msg);
432
433	return 0;
434}
435
436static int number_type(const char *number)
437{
438	if (number == NULL)
439		return NUMBER_TYPE_TELEPHONY;
440
441	if (number[0] == '+' || strncmp(number, "00", 2) == 0)
442		return NUMBER_TYPE_INTERNATIONAL;
443
444	return NUMBER_TYPE_TELEPHONY;
445}
446
447void telephony_device_connected(void *telephony_device)
448{
449	struct csd_call *coming;
450
451	DBG("telephony-maemo6: device %p connected", telephony_device);
452
453	coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
454	if (coming) {
455		if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
456			telephony_call_waiting_ind(coming->number,
457						number_type(coming->number));
458		else
459			telephony_incoming_call_ind(coming->number,
460						number_type(coming->number));
461	}
462}
463
464void telephony_device_disconnected(void *telephony_device)
465{
466	DBG("telephony-maemo6: device %p disconnected", telephony_device);
467	events_enabled = FALSE;
468}
469
470void telephony_event_reporting_req(void *telephony_device, int ind)
471{
472	events_enabled = ind == 1 ? TRUE : FALSE;
473
474	telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
475}
476
477void telephony_response_and_hold_req(void *telephony_device, int rh)
478{
479	response_and_hold = rh;
480
481	telephony_response_and_hold_ind(response_and_hold);
482
483	telephony_response_and_hold_rsp(telephony_device, CME_ERROR_NONE);
484}
485
486void telephony_last_dialed_number_req(void *telephony_device)
487{
488	DBG("telephony-maemo6: last dialed number request");
489
490	if (last_dialed_number)
491		telephony_dial_number_req(telephony_device,
492						last_dialed_number);
493	else
494		telephony_last_dialed_number_rsp(telephony_device,
495						CME_ERROR_NOT_ALLOWED);
496}
497
498void telephony_terminate_call_req(void *telephony_device)
499{
500	struct csd_call *call;
501	int err;
502
503	call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
504	if (!call)
505		call = find_non_idle_call();
506
507	if (!call) {
508		error("No active call");
509		telephony_terminate_call_rsp(telephony_device,
510						CME_ERROR_NOT_ALLOWED);
511		return;
512	}
513
514	if (call->conference)
515		err = release_conference();
516	else
517		err = release_call(call);
518
519	if (err < 0)
520		telephony_terminate_call_rsp(telephony_device,
521						CME_ERROR_AG_FAILURE);
522	else
523		telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
524}
525
526void telephony_answer_call_req(void *telephony_device)
527{
528	struct csd_call *call;
529
530	call = find_call_with_status(CSD_CALL_STATUS_COMING);
531	if (!call)
532		call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
533
534	if (!call)
535		call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
536
537	if (!call)
538		call = find_call_with_status(CSD_CALL_STATUS_WAITING);
539
540	if (!call) {
541		telephony_answer_call_rsp(telephony_device,
542						CME_ERROR_NOT_ALLOWED);
543		return;
544	}
545
546	if (answer_call(call) < 0)
547		telephony_answer_call_rsp(telephony_device,
548						CME_ERROR_AG_FAILURE);
549	else
550		telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
551}
552
553static int send_method_call(const char *dest, const char *path,
554				const char *interface, const char *method,
555				DBusPendingCallNotifyFunction cb,
556				void *user_data, int type, ...)
557{
558	DBusMessage *msg;
559	DBusPendingCall *call;
560	va_list args;
561
562	msg = dbus_message_new_method_call(dest, path, interface, method);
563	if (!msg) {
564		error("Unable to allocate new D-Bus %s message", method);
565		return -ENOMEM;
566	}
567
568	va_start(args, type);
569
570	if (!dbus_message_append_args_valist(msg, type, args)) {
571		dbus_message_unref(msg);
572		va_end(args);
573		return -EIO;
574	}
575
576	va_end(args);
577
578	if (!cb) {
579		g_dbus_send_message(connection, msg);
580		return 0;
581	}
582
583	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
584		error("Sending %s failed", method);
585		dbus_message_unref(msg);
586		return -EIO;
587	}
588
589	dbus_pending_call_set_notify(call, cb, user_data, NULL);
590	dbus_pending_call_unref(call);
591	dbus_message_unref(msg);
592
593	return 0;
594}
595
596static const char *memory_dial_lookup(int location)
597{
598	if (location == 1)
599		return vmbx;
600	else
601		return NULL;
602}
603
604void telephony_dial_number_req(void *telephony_device, const char *number)
605{
606	uint32_t flags = callerid;
607	int ret;
608
609	DBG("telephony-maemo6: dial request to %s", number);
610
611	if (strncmp(number, "*31#", 4) == 0) {
612		number += 4;
613		flags = CALL_FLAG_PRESENTATION_ALLOWED;
614	} else if (strncmp(number, "#31#", 4) == 0) {
615		number += 4;
616		flags = CALL_FLAG_PRESENTATION_RESTRICTED;
617	} else if (number[0] == '>') {
618		const char *location = &number[1];
619
620		number = memory_dial_lookup(strtol(&number[1], NULL, 0));
621		if (!number) {
622			error("No number at memory location %s", location);
623			telephony_dial_number_rsp(telephony_device,
624						CME_ERROR_INVALID_INDEX);
625			return;
626		}
627	}
628
629	ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
630				CSD_CALL_INTERFACE, "CreateWith",
631				NULL, NULL,
632				DBUS_TYPE_STRING, &number,
633				DBUS_TYPE_UINT32, &flags,
634				DBUS_TYPE_INVALID);
635	if (ret < 0) {
636		telephony_dial_number_rsp(telephony_device,
637						CME_ERROR_AG_FAILURE);
638		return;
639	}
640
641	telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
642}
643
644void telephony_transmit_dtmf_req(void *telephony_device, char tone)
645{
646	int ret;
647	char buf[2] = { tone, '\0' }, *buf_ptr = buf;
648
649	DBG("telephony-maemo6: transmit dtmf: %s", buf);
650
651	ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
652				CSD_CALL_INTERFACE, "SendDTMF",
653				NULL, NULL,
654				DBUS_TYPE_STRING, &buf_ptr,
655				DBUS_TYPE_INVALID);
656	if (ret < 0) {
657		telephony_transmit_dtmf_rsp(telephony_device,
658						CME_ERROR_AG_FAILURE);
659		return;
660	}
661
662	telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
663}
664
665void telephony_subscriber_number_req(void *telephony_device)
666{
667	DBG("telephony-maemo6: subscriber number request");
668	if (msisdn)
669		telephony_subscriber_number_ind(msisdn,
670						number_type(msisdn),
671						SUBSCRIBER_SERVICE_VOICE);
672	telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
673}
674
675static int csd_status_to_hfp(struct csd_call *call)
676{
677	switch (call->status) {
678	case CSD_CALL_STATUS_IDLE:
679	case CSD_CALL_STATUS_MO_RELEASE:
680	case CSD_CALL_STATUS_MT_RELEASE:
681	case CSD_CALL_STATUS_TERMINATED:
682		return -1;
683	case CSD_CALL_STATUS_CREATE:
684		return CALL_STATUS_DIALING;
685	case CSD_CALL_STATUS_WAITING:
686		return CALL_STATUS_WAITING;
687	case CSD_CALL_STATUS_PROCEEDING:
688		/* PROCEEDING can happen in outgoing/incoming */
689		if (call->originating)
690			return CALL_STATUS_DIALING;
691		else
692			return CALL_STATUS_INCOMING;
693	case CSD_CALL_STATUS_COMING:
694		return CALL_STATUS_INCOMING;
695	case CSD_CALL_STATUS_MO_ALERTING:
696		return CALL_STATUS_ALERTING;
697	case CSD_CALL_STATUS_MT_ALERTING:
698		return CALL_STATUS_INCOMING;
699	case CSD_CALL_STATUS_ANSWERED:
700	case CSD_CALL_STATUS_ACTIVE:
701	case CSD_CALL_STATUS_RECONNECT_PENDING:
702	case CSD_CALL_STATUS_SWAP_INITIATED:
703	case CSD_CALL_STATUS_HOLD_INITIATED:
704		return CALL_STATUS_ACTIVE;
705	case CSD_CALL_STATUS_RETRIEVE_INITIATED:
706	case CSD_CALL_STATUS_HOLD:
707		return CALL_STATUS_HELD;
708	default:
709		return -1;
710	}
711}
712
713void telephony_list_current_calls_req(void *telephony_device)
714{
715	GSList *l;
716	int i;
717
718	DBG("telephony-maemo6: list current calls request");
719
720	for (l = calls, i = 1; l != NULL; l = l->next, i++) {
721		struct csd_call *call = l->data;
722		int status, direction, multiparty;
723
724		status = csd_status_to_hfp(call);
725		if (status < 0)
726			continue;
727
728		direction = call->originating ?
729				CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
730
731		multiparty = call->conference ?
732				CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
733
734		telephony_list_current_call_ind(i, direction, status,
735						CALL_MODE_VOICE, multiparty,
736						call->number,
737						number_type(call->number));
738	}
739
740	telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
741}
742
743void telephony_operator_selection_req(void *telephony_device)
744{
745	telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
746				net.operator_name ? net.operator_name : "");
747	telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
748}
749
750static void foreach_call_with_status(int status,
751					int (*func)(struct csd_call *call))
752{
753	GSList *l;
754
755	for (l = calls; l != NULL; l = l->next) {
756		struct csd_call *call = l->data;
757
758		if (call->status == status)
759			func(call);
760	}
761}
762
763void telephony_call_hold_req(void *telephony_device, const char *cmd)
764{
765	const char *idx;
766	struct csd_call *call;
767	int err = 0;
768
769	DBG("telephony-maemo6: got call hold request %s", cmd);
770
771	if (strlen(cmd) > 1)
772		idx = &cmd[1];
773	else
774		idx = NULL;
775
776	if (idx)
777		call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
778	else
779		call = NULL;
780
781	switch (cmd[0]) {
782	case '0':
783		foreach_call_with_status(CSD_CALL_STATUS_HOLD, release_call);
784		foreach_call_with_status(CSD_CALL_STATUS_WAITING,
785								release_call);
786		break;
787	case '1':
788		if (idx) {
789			if (call)
790				err = release_call(call);
791			break;
792		}
793		foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call);
794		call = find_call_with_status(CSD_CALL_STATUS_WAITING);
795		if (call)
796			err = answer_call(call);
797		break;
798	case '2':
799		if (idx) {
800			if (call)
801				err = split_call(call);
802		} else {
803			struct csd_call *held, *wait;
804
805			call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
806			held = find_call_with_status(CSD_CALL_STATUS_HOLD);
807			wait = find_call_with_status(CSD_CALL_STATUS_WAITING);
808
809			if (wait)
810				err = answer_call(wait);
811			else if (call && held)
812				err = swap_calls();
813			else {
814				if (call)
815					err = hold_call(call);
816				if (held)
817					err = unhold_call(held);
818			}
819		}
820		break;
821	case '3':
822		if (find_call_with_status(CSD_CALL_STATUS_HOLD) ||
823				find_call_with_status(CSD_CALL_STATUS_WAITING))
824			err = create_conference();
825		break;
826	case '4':
827		err = call_transfer();
828		break;
829	default:
830		DBG("Unknown call hold request");
831		break;
832	}
833
834	if (err)
835		telephony_call_hold_rsp(telephony_device,
836					CME_ERROR_AG_FAILURE);
837	else
838		telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
839}
840
841void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
842{
843	DBG("telephony-maemo6: got %s NR and EC request",
844			enable ? "enable" : "disable");
845	telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
846}
847
848void telephony_key_press_req(void *telephony_device, const char *keys)
849{
850	struct csd_call *active, *waiting;
851	int err;
852
853	DBG("telephony-maemo6: got key press request for %s", keys);
854
855	waiting = find_call_with_status(CSD_CALL_STATUS_COMING);
856	if (!waiting)
857		waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
858	if (!waiting)
859		waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
860
861	active = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
862
863	if (waiting)
864		err = answer_call(waiting);
865	else if (active)
866		err = release_call(active);
867	else
868		err = 0;
869
870	if (err < 0)
871		telephony_key_press_rsp(telephony_device,
872							CME_ERROR_AG_FAILURE);
873	else
874		telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
875}
876
877void telephony_voice_dial_req(void *telephony_device, gboolean enable)
878{
879	DBG("telephony-maemo6: got %s voice dial request",
880			enable ? "enable" : "disable");
881
882	telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
883}
884
885static void handle_incoming_call(DBusMessage *msg)
886{
887	const char *number, *call_path;
888	struct csd_call *call;
889
890	if (!dbus_message_get_args(msg, NULL,
891					DBUS_TYPE_OBJECT_PATH, &call_path,
892					DBUS_TYPE_STRING, &number,
893					DBUS_TYPE_INVALID)) {
894		error("Unexpected parameters in Call.Coming() signal");
895		return;
896	}
897
898	call = find_call(call_path);
899	if (!call) {
900		error("Didn't find any matching call object for %s",
901				call_path);
902		return;
903	}
904
905	DBG("Incoming call to %s from number %s", call_path, number);
906
907	g_free(call->number);
908	call->number = g_strdup(number);
909
910	telephony_update_indicator(maemo_indicators, "callsetup",
911					EV_CALLSETUP_INCOMING);
912
913	if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
914		telephony_call_waiting_ind(call->number,
915						number_type(call->number));
916	else
917		telephony_incoming_call_ind(call->number,
918						number_type(call->number));
919}
920
921static void handle_outgoing_call(DBusMessage *msg)
922{
923	const char *number, *call_path;
924	struct csd_call *call;
925
926	if (!dbus_message_get_args(msg, NULL,
927					DBUS_TYPE_OBJECT_PATH, &call_path,
928					DBUS_TYPE_STRING, &number,
929					DBUS_TYPE_INVALID)) {
930		error("Unexpected parameters in Call.Created() signal");
931		return;
932	}
933
934	call = find_call(call_path);
935	if (!call) {
936		error("Didn't find any matching call object for %s",
937				call_path);
938		return;
939	}
940
941	DBG("Outgoing call from %s to number %s", call_path, number);
942
943	g_free(call->number);
944	call->number = g_strdup(number);
945
946	g_free(last_dialed_number);
947	last_dialed_number = g_strdup(number);
948
949	if (create_request_timer) {
950		g_source_remove(create_request_timer);
951		create_request_timer = 0;
952	}
953}
954
955static gboolean create_timeout(gpointer user_data)
956{
957	telephony_update_indicator(maemo_indicators, "callsetup",
958					EV_CALLSETUP_INACTIVE);
959	create_request_timer = 0;
960	return FALSE;
961}
962
963static void handle_create_requested(DBusMessage *msg)
964{
965	DBG("Call.CreateRequested()");
966
967	if (create_request_timer)
968		g_source_remove(create_request_timer);
969
970	create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL);
971
972	telephony_update_indicator(maemo_indicators, "callsetup",
973					EV_CALLSETUP_OUTGOING);
974}
975
976static void handle_call_status(DBusMessage *msg, const char *call_path)
977{
978	struct csd_call *call;
979	dbus_uint32_t status, cause_type, cause;
980	int callheld = telephony_get_indicator(maemo_indicators, "callheld");
981
982	if (!dbus_message_get_args(msg, NULL,
983					DBUS_TYPE_UINT32, &status,
984					DBUS_TYPE_UINT32, &cause_type,
985					DBUS_TYPE_UINT32, &cause,
986					DBUS_TYPE_INVALID)) {
987		error("Unexpected paramters in Instance.CallStatus() signal");
988		return;
989	}
990
991	call = find_call(call_path);
992	if (!call) {
993		error("Didn't find any matching call object for %s",
994				call_path);
995		return;
996	}
997
998	if (status > 16) {
999		error("Invalid call status %u", status);
1000		return;
1001	}
1002
1003	DBG("Call %s changed from %s to %s", call_path,
1004		call_status_str[call->status], call_status_str[status]);
1005
1006	if (call->status == (int) status) {
1007		DBG("Ignoring CSD Call state change to existing state");
1008		return;
1009	}
1010
1011	call->status = (int) status;
1012
1013	switch (status) {
1014	case CSD_CALL_STATUS_IDLE:
1015		if (call->setup) {
1016			telephony_update_indicator(maemo_indicators,
1017							"callsetup",
1018							EV_CALLSETUP_INACTIVE);
1019			if (!call->originating)
1020				telephony_calling_stopped_ind();
1021		}
1022
1023		g_free(call->number);
1024		call->number = NULL;
1025		call->originating = FALSE;
1026		call->emergency = FALSE;
1027		call->on_hold = FALSE;
1028		call->conference = FALSE;
1029		call->setup = FALSE;
1030		break;
1031	case CSD_CALL_STATUS_CREATE:
1032		call->originating = TRUE;
1033		call->setup = TRUE;
1034		break;
1035	case CSD_CALL_STATUS_COMING:
1036		call->originating = FALSE;
1037		call->setup = TRUE;
1038		break;
1039	case CSD_CALL_STATUS_PROCEEDING:
1040		break;
1041	case CSD_CALL_STATUS_MO_ALERTING:
1042		telephony_update_indicator(maemo_indicators, "callsetup",
1043						EV_CALLSETUP_ALERTING);
1044		break;
1045	case CSD_CALL_STATUS_MT_ALERTING:
1046		break;
1047	case CSD_CALL_STATUS_WAITING:
1048		break;
1049	case CSD_CALL_STATUS_ANSWERED:
1050		break;
1051	case CSD_CALL_STATUS_ACTIVE:
1052		if (call->on_hold) {
1053			call->on_hold = FALSE;
1054			if (find_call_with_status(CSD_CALL_STATUS_HOLD))
1055				telephony_update_indicator(maemo_indicators,
1056							"callheld",
1057							EV_CALLHELD_MULTIPLE);
1058			else
1059				telephony_update_indicator(maemo_indicators,
1060							"callheld",
1061							EV_CALLHELD_NONE);
1062		} else {
1063			if (!g_slist_find(active_calls, call))
1064				active_calls = g_slist_prepend(active_calls, call);
1065			if (g_slist_length(active_calls) == 1)
1066				telephony_update_indicator(maemo_indicators,
1067								"call",
1068								EV_CALL_ACTIVE);
1069			/* Upgrade callheld status if necessary */
1070			if (callheld == EV_CALLHELD_ON_HOLD)
1071				telephony_update_indicator(maemo_indicators,
1072							"callheld",
1073							EV_CALLHELD_MULTIPLE);
1074			telephony_update_indicator(maemo_indicators,
1075							"callsetup",
1076							EV_CALLSETUP_INACTIVE);
1077			if (!call->originating)
1078				telephony_calling_stopped_ind();
1079			call->setup = FALSE;
1080		}
1081		break;
1082	case CSD_CALL_STATUS_MO_RELEASE:
1083	case CSD_CALL_STATUS_MT_RELEASE:
1084		active_calls = g_slist_remove(active_calls, call);
1085		if (g_slist_length(active_calls) == 0)
1086			telephony_update_indicator(maemo_indicators, "call",
1087							EV_CALL_INACTIVE);
1088		break;
1089	case CSD_CALL_STATUS_HOLD_INITIATED:
1090		break;
1091	case CSD_CALL_STATUS_HOLD:
1092		call->on_hold = TRUE;
1093		if (find_non_held_call())
1094			telephony_update_indicator(maemo_indicators,
1095							"callheld",
1096							EV_CALLHELD_MULTIPLE);
1097		else
1098			telephony_update_indicator(maemo_indicators,
1099							"callheld",
1100							EV_CALLHELD_ON_HOLD);
1101		break;
1102	case CSD_CALL_STATUS_RETRIEVE_INITIATED:
1103		break;
1104	case CSD_CALL_STATUS_RECONNECT_PENDING:
1105		break;
1106	case CSD_CALL_STATUS_TERMINATED:
1107		if (call->on_hold &&
1108				!find_call_with_status(CSD_CALL_STATUS_HOLD))
1109			telephony_update_indicator(maemo_indicators,
1110							"callheld",
1111							EV_CALLHELD_NONE);
1112		else if (callheld == EV_CALLHELD_MULTIPLE &&
1113				find_call_with_status(CSD_CALL_STATUS_HOLD))
1114			telephony_update_indicator(maemo_indicators,
1115							"callheld",
1116							EV_CALLHELD_ON_HOLD);
1117		break;
1118	case CSD_CALL_STATUS_SWAP_INITIATED:
1119		break;
1120	default:
1121		error("Unknown call status %u", status);
1122		break;
1123	}
1124}
1125
1126static void handle_conference(DBusMessage *msg, gboolean joined)
1127{
1128	const char *path;
1129	struct csd_call *call;
1130
1131	if (!dbus_message_get_args(msg, NULL,
1132					DBUS_TYPE_OBJECT_PATH, &path,
1133					DBUS_TYPE_INVALID)) {
1134		error("Unexpected parameters in Conference.%s",
1135					dbus_message_get_member(msg));
1136		return;
1137	}
1138
1139	call = find_call(path);
1140	if (!call) {
1141		error("Conference signal for unknown call %s", path);
1142		return;
1143	}
1144
1145	DBG("Call %s %s the conference", path, joined ? "joined" : "left");
1146
1147	call->conference = joined;
1148}
1149
1150static uint8_t str2status(const char *state)
1151{
1152	if (g_strcmp0(state, "Home") == 0)
1153		return NETWORK_REG_STATUS_HOME;
1154	else if (g_strcmp0(state, "Roaming") == 0)
1155		return NETWORK_REG_STATUS_ROAMING;
1156	else if (g_strcmp0(state, "Offline") == 0)
1157		return NETWORK_REG_STATUS_OFFLINE;
1158	else if (g_strcmp0(state, "Searching") == 0)
1159		return NETWORK_REG_STATUS_SEARCHING;
1160	else if (g_strcmp0(state, "NoSim") == 0)
1161		return NETWORK_REG_STATUS_NO_SIM;
1162	else if (g_strcmp0(state, "Poweroff") == 0)
1163		return NETWORK_REG_STATUS_POWEROFF;
1164	else if (g_strcmp0(state, "Powersafe") == 0)
1165		return NETWORK_REG_STATUS_POWERSAFE;
1166	else if (g_strcmp0(state, "NoCoverage") == 0)
1167		return NETWORK_REG_STATUS_NO_COVERAGE;
1168	else if (g_strcmp0(state, "Reject") == 0)
1169		return NETWORK_REG_STATUS_REJECTED;
1170	else
1171		return NETWORK_REG_STATUS_UNKOWN;
1172}
1173
1174static void update_registration_status(const char *status)
1175{
1176	uint8_t new_status;
1177
1178	new_status = str2status(status);
1179
1180	if (net.status == new_status)
1181		return;
1182
1183	switch (new_status) {
1184	case NETWORK_REG_STATUS_HOME:
1185		telephony_update_indicator(maemo_indicators, "roam",
1186							EV_ROAM_INACTIVE);
1187		if (net.status > NETWORK_REG_STATUS_ROAMING)
1188			telephony_update_indicator(maemo_indicators,
1189							"service",
1190							EV_SERVICE_PRESENT);
1191		break;
1192	case NETWORK_REG_STATUS_ROAMING:
1193		telephony_update_indicator(maemo_indicators, "roam",
1194							EV_ROAM_ACTIVE);
1195		if (net.status > NETWORK_REG_STATUS_ROAMING)
1196			telephony_update_indicator(maemo_indicators,
1197							"service",
1198							EV_SERVICE_PRESENT);
1199		break;
1200	case NETWORK_REG_STATUS_OFFLINE:
1201	case NETWORK_REG_STATUS_SEARCHING:
1202	case NETWORK_REG_STATUS_NO_SIM:
1203	case NETWORK_REG_STATUS_POWEROFF:
1204	case NETWORK_REG_STATUS_POWERSAFE:
1205	case NETWORK_REG_STATUS_NO_COVERAGE:
1206	case NETWORK_REG_STATUS_REJECTED:
1207	case NETWORK_REG_STATUS_UNKOWN:
1208		if (net.status < NETWORK_REG_STATUS_OFFLINE)
1209			telephony_update_indicator(maemo_indicators,
1210							"service",
1211							EV_SERVICE_NONE);
1212		break;
1213	}
1214
1215	net.status = new_status;
1216
1217	DBG("telephony-maemo6: registration status changed: %s", status);
1218}
1219
1220static void handle_registration_changed(DBusMessage *msg)
1221{
1222	const char *status;
1223
1224	if (!dbus_message_get_args(msg, NULL,
1225					DBUS_TYPE_STRING, &status,
1226					DBUS_TYPE_INVALID)) {
1227		error("Unexpected parameters in RegistrationChanged");
1228		return;
1229	}
1230
1231	update_registration_status(status);
1232}
1233
1234static void update_signal_strength(int32_t signal_bars)
1235{
1236	if (signal_bars < 0) {
1237		DBG("signal strength smaller than expected: %d < 0",
1238								signal_bars);
1239		signal_bars = 0;
1240	} else if (signal_bars > 5) {
1241		DBG("signal strength greater than expected: %d > 5",
1242								signal_bars);
1243		signal_bars = 5;
1244	}
1245
1246	if (net.signal_bars == signal_bars)
1247		return;
1248
1249	telephony_update_indicator(maemo_indicators, "signal", signal_bars);
1250
1251	net.signal_bars = signal_bars;
1252	DBG("telephony-maemo6: signal strength updated: %d/5", signal_bars);
1253}
1254
1255static void handle_signal_bars_changed(DBusMessage *msg)
1256{
1257	int32_t signal_bars;
1258
1259	if (!dbus_message_get_args(msg, NULL,
1260					DBUS_TYPE_INT32, &signal_bars,
1261					DBUS_TYPE_INVALID)) {
1262		error("Unexpected parameters in SignalBarsChanged");
1263		return;
1264	}
1265
1266	update_signal_strength(signal_bars);
1267}
1268
1269static gboolean iter_get_basic_args(DBusMessageIter *iter,
1270					int first_arg_type, ...)
1271{
1272	int type;
1273	va_list ap;
1274
1275	va_start(ap, first_arg_type);
1276
1277	for (type = first_arg_type; type != DBUS_TYPE_INVALID;
1278			type = va_arg(ap, int)) {
1279		void *value = va_arg(ap, void *);
1280		int real_type = dbus_message_iter_get_arg_type(iter);
1281
1282		if (real_type != type) {
1283			error("iter_get_basic_args: expected %c but got %c",
1284					(char) type, (char) real_type);
1285			break;
1286		}
1287
1288		dbus_message_iter_get_basic(iter, value);
1289		dbus_message_iter_next(iter);
1290	}
1291
1292	va_end(ap);
1293
1294	return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
1295}
1296
1297static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
1298{
1299	DBusError err;
1300	DBusMessage *reply;
1301	dbus_int32_t level;
1302	int *value = user_data;
1303
1304	reply = dbus_pending_call_steal_reply(call);
1305
1306	dbus_error_init(&err);
1307	if (dbus_set_error_from_message(&err, reply)) {
1308		error("hald replied with an error: %s, %s",
1309				err.name, err.message);
1310		dbus_error_free(&err);
1311		goto done;
1312	}
1313
1314	if (!dbus_message_get_args(reply, NULL,
1315				DBUS_TYPE_INT32, &level,
1316				DBUS_TYPE_INVALID)) {
1317		error("Unexpected args in hald reply");
1318		goto done;
1319	}
1320
1321	*value = (int) level;
1322
1323	if (value == &battchg_last)
1324		DBG("telephony-maemo6: battery.charge_level.last_full is %d",
1325				*value);
1326	else if (value == &battchg_design)
1327		DBG("telephony-maemo6: battery.charge_level.design is %d",
1328				*value);
1329	else
1330		DBG("telephony-maemo6: battery.charge_level.current is %d",
1331				*value);
1332
1333	if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
1334		int new, max;
1335
1336		if (battchg_last > 0)
1337			max = battchg_last;
1338		else
1339			max = battchg_design;
1340
1341		new = battchg_cur * 5 / max;
1342
1343		telephony_update_indicator(maemo_indicators, "battchg", new);
1344	}
1345done:
1346	dbus_message_unref(reply);
1347}
1348
1349static void hal_get_integer(const char *path, const char *key, void *user_data)
1350{
1351	send_method_call("org.freedesktop.Hal", path,
1352				"org.freedesktop.Hal.Device",
1353				"GetPropertyInteger",
1354				hal_battery_level_reply, user_data,
1355				DBUS_TYPE_STRING, &key,
1356				DBUS_TYPE_INVALID);
1357}
1358
1359static void handle_hal_property_modified(DBusMessage *msg)
1360{
1361	DBusMessageIter iter, array;
1362	dbus_int32_t num_changes;
1363	const char *path;
1364
1365	path = dbus_message_get_path(msg);
1366
1367	dbus_message_iter_init(msg, &iter);
1368
1369	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
1370		error("Unexpected signature in hal PropertyModified signal");
1371		return;
1372	}
1373
1374	dbus_message_iter_get_basic(&iter, &num_changes);
1375	dbus_message_iter_next(&iter);
1376
1377	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1378		error("Unexpected signature in hal PropertyModified signal");
1379		return;
1380	}
1381
1382	dbus_message_iter_recurse(&iter, &array);
1383
1384	while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
1385		DBusMessageIter prop;
1386		const char *name;
1387		dbus_bool_t added, removed;
1388
1389		dbus_message_iter_recurse(&array, &prop);
1390
1391		if (!iter_get_basic_args(&prop,
1392					DBUS_TYPE_STRING, &name,
1393					DBUS_TYPE_BOOLEAN, &added,
1394					DBUS_TYPE_BOOLEAN, &removed,
1395					DBUS_TYPE_INVALID)) {
1396			error("Invalid hal PropertyModified parameters");
1397			break;
1398		}
1399
1400		if (g_str_equal(name, "battery.charge_level.last_full"))
1401			hal_get_integer(path, name, &battchg_last);
1402		else if (g_str_equal(name, "battery.charge_level.current"))
1403			hal_get_integer(path, name, &battchg_cur);
1404		else if (g_str_equal(name, "battery.charge_level.design"))
1405			hal_get_integer(path, name, &battchg_design);
1406
1407		dbus_message_iter_next(&array);
1408	}
1409}
1410
1411static void csd_call_free(struct csd_call *call)
1412{
1413	if (!call)
1414		return;
1415
1416	g_free(call->object_path);
1417	g_free(call->number);
1418
1419	g_free(call);
1420}
1421
1422static void parse_call_list(DBusMessageIter *iter)
1423{
1424	do {
1425		DBusMessageIter call_iter;
1426		struct csd_call *call;
1427		const char *object_path, *number;
1428		dbus_uint32_t status;
1429		dbus_bool_t originating, terminating, emerg, on_hold, conf;
1430
1431		if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) {
1432			error("Unexpected signature in GetCallInfoAll reply");
1433			break;
1434		}
1435
1436		dbus_message_iter_recurse(iter, &call_iter);
1437
1438		if (!iter_get_basic_args(&call_iter,
1439					DBUS_TYPE_OBJECT_PATH, &object_path,
1440					DBUS_TYPE_UINT32, &status,
1441					DBUS_TYPE_BOOLEAN, &originating,
1442					DBUS_TYPE_BOOLEAN, &terminating,
1443					DBUS_TYPE_BOOLEAN, &emerg,
1444					DBUS_TYPE_BOOLEAN, &on_hold,
1445					DBUS_TYPE_BOOLEAN, &conf,
1446					DBUS_TYPE_STRING, &number,
1447					DBUS_TYPE_INVALID)) {
1448			error("Parsing call D-Bus parameters failed");
1449			break;
1450		}
1451
1452		call = find_call(object_path);
1453		if (!call) {
1454			call = g_new0(struct csd_call, 1);
1455			call->object_path = g_strdup(object_path);
1456			call->status = (int) status;
1457			calls = g_slist_append(calls, call);
1458			DBG("telephony-maemo6: new csd call instance at %s",
1459								object_path);
1460		}
1461
1462		if (call->status == CSD_CALL_STATUS_IDLE)
1463			continue;
1464
1465		/* CSD gives incorrect call_hold property sometimes */
1466		if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) ||
1467				(call->status == CSD_CALL_STATUS_HOLD &&
1468								!on_hold)) {
1469			error("Conflicting call status and on_hold property!");
1470			on_hold = call->status == CSD_CALL_STATUS_HOLD;
1471		}
1472
1473		call->originating = originating;
1474		call->on_hold = on_hold;
1475		call->conference = conf;
1476		g_free(call->number);
1477		call->number = g_strdup(number);
1478
1479	} while (dbus_message_iter_next(iter));
1480}
1481
1482static void update_operator_name(const char *name)
1483{
1484	if (name == NULL)
1485		return;
1486
1487	g_free(net.operator_name);
1488	net.operator_name = g_strdup(name);
1489
1490	DBG("telephony-maemo6: operator name updated: %s", name);
1491}
1492
1493static void get_property_reply(DBusPendingCall *call, void *user_data)
1494{
1495	char *prop = user_data;
1496	DBusError err;
1497	DBusMessage *reply;
1498	DBusMessageIter iter, sub;
1499
1500	reply = dbus_pending_call_steal_reply(call);
1501
1502	dbus_error_init(&err);
1503	if (dbus_set_error_from_message(&err, reply)) {
1504		error("csd replied with an error: %s, %s",
1505				err.name, err.message);
1506		dbus_error_free(&err);
1507		goto done;
1508	}
1509
1510	dbus_message_iter_init(reply, &iter);
1511
1512	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
1513		error("Unexpected signature in Get return");
1514		goto done;
1515	}
1516
1517	dbus_message_iter_recurse(&iter, &sub);
1518
1519	if (g_strcmp0(prop, "RegistrationStatus") == 0) {
1520		const char *status;
1521
1522		dbus_message_iter_get_basic(&sub, &status);
1523		update_registration_status(status);
1524
1525		get_property(CSD_CSNET_OPERATOR, "OperatorName");
1526		get_property(CSD_CSNET_SIGNAL, "SignalBars");
1527	} else if (g_strcmp0(prop, "OperatorName") == 0) {
1528		const char *name;
1529
1530		dbus_message_iter_get_basic(&sub, &name);
1531		update_operator_name(name);
1532	} else if (g_strcmp0(prop, "SignalBars") == 0) {
1533		int32_t signal_bars;
1534
1535		dbus_message_iter_get_basic(&sub, &signal_bars);
1536		update_signal_strength(signal_bars);
1537	}
1538
1539done:
1540	g_free(prop);
1541	dbus_message_unref(reply);
1542}
1543
1544static int get_property(const char *iface, const char *prop)
1545{
1546	return send_method_call(CSD_CSNET_BUS_NAME, CSD_CSNET_PATH,
1547				DBUS_INTERFACE_PROPERTIES, "Get",
1548				get_property_reply, g_strdup(prop),
1549				DBUS_TYPE_STRING, &iface,
1550				DBUS_TYPE_STRING, &prop,
1551				DBUS_TYPE_INVALID);
1552}
1553
1554static void handle_operator_name_changed(DBusMessage *msg)
1555{
1556	const char *name;
1557
1558	if (!dbus_message_get_args(msg, NULL,
1559					DBUS_TYPE_STRING, &name,
1560					DBUS_TYPE_INVALID)) {
1561		error("Unexpected parameters in OperatorNameChanged");
1562		return;
1563	}
1564
1565	update_operator_name(name);
1566}
1567
1568static void call_info_reply(DBusPendingCall *call, void *user_data)
1569{
1570	DBusError err;
1571	DBusMessage *reply;
1572	DBusMessageIter iter, sub;;
1573
1574	get_calls_active = FALSE;
1575
1576	reply = dbus_pending_call_steal_reply(call);
1577
1578	dbus_error_init(&err);
1579	if (dbus_set_error_from_message(&err, reply)) {
1580		error("csd replied with an error: %s, %s",
1581				err.name, err.message);
1582		dbus_error_free(&err);
1583		goto done;
1584	}
1585
1586	dbus_message_iter_init(reply, &iter);
1587
1588	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1589		error("Unexpected signature in GetCallInfoAll return");
1590		goto done;
1591	}
1592
1593	dbus_message_iter_recurse(&iter, &sub);
1594
1595	parse_call_list(&sub);
1596
1597	get_property(CSD_CSNET_REGISTRATION, "RegistrationStatus");
1598
1599done:
1600	dbus_message_unref(reply);
1601}
1602
1603static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
1604{
1605	DBusError err;
1606	DBusMessage *reply;
1607	DBusMessageIter iter, sub;
1608	const char *path;
1609	char match_string[256];
1610	int type;
1611
1612	reply = dbus_pending_call_steal_reply(call);
1613
1614	dbus_error_init(&err);
1615	if (dbus_set_error_from_message(&err, reply)) {
1616		error("hald replied with an error: %s, %s",
1617				err.name, err.message);
1618		dbus_error_free(&err);
1619		goto done;
1620	}
1621
1622	dbus_message_iter_init(reply, &iter);
1623
1624	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1625		error("Unexpected signature in FindDeviceByCapability return");
1626		goto done;
1627	}
1628
1629	dbus_message_iter_recurse(&iter, &sub);
1630
1631	type = dbus_message_iter_get_arg_type(&sub);
1632
1633	if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
1634		error("No hal device with battery capability found");
1635		goto done;
1636	}
1637
1638	dbus_message_iter_get_basic(&sub, &path);
1639
1640	DBG("telephony-maemo6: found battery device at %s", path);
1641
1642	snprintf(match_string, sizeof(match_string),
1643			"type='signal',"
1644			"path='%s',"
1645			"interface='org.freedesktop.Hal.Device',"
1646			"member='PropertyModified'", path);
1647	dbus_bus_add_match(connection, match_string, NULL);
1648
1649	hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
1650	hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
1651	hal_get_integer(path, "battery.charge_level.design", &battchg_design);
1652
1653done:
1654	dbus_message_unref(reply);
1655}
1656
1657static void phonebook_read_reply(DBusPendingCall *call, void *user_data)
1658{
1659	DBusError derr;
1660	DBusMessage *reply;
1661	const char *name, *number, *secondname, *additionalnumber, *email;
1662	int index;
1663	char **number_type = user_data;
1664
1665	reply = dbus_pending_call_steal_reply(call);
1666
1667	dbus_error_init(&derr);
1668	if (dbus_set_error_from_message(&derr, reply)) {
1669		error("%s.ReadFirst replied with an error: %s, %s",
1670				CSD_SIMPB_INTERFACE, derr.name, derr.message);
1671		dbus_error_free(&derr);
1672		if (number_type == &vmbx)
1673			vmbx = g_strdup(getenv("VMBX_NUMBER"));
1674		goto done;
1675	}
1676
1677	dbus_error_init(&derr);
1678	if (dbus_message_get_args(reply, NULL,
1679				DBUS_TYPE_INT32, &index,
1680				DBUS_TYPE_STRING, &name,
1681				DBUS_TYPE_STRING, &number,
1682				DBUS_TYPE_STRING, &secondname,
1683				DBUS_TYPE_STRING, &additionalnumber,
1684				DBUS_TYPE_STRING, &email,
1685				DBUS_TYPE_INVALID) == FALSE) {
1686		error("Unable to parse %s.ReadFirst arguments: %s, %s",
1687				CSD_SIMPB_INTERFACE, derr.name, derr.message);
1688		dbus_error_free(&derr);
1689		goto done;
1690	}
1691
1692	if (number_type == &msisdn) {
1693		g_free(msisdn);
1694		msisdn = g_strdup(number);
1695		DBG("Got MSISDN %s (%s)", number, name);
1696	} else {
1697		g_free(vmbx);
1698		vmbx = g_strdup(number);
1699		DBG("Got voice mailbox number %s (%s)", number, name);
1700	}
1701
1702done:
1703	dbus_message_unref(reply);
1704}
1705
1706static void csd_init(void)
1707{
1708	const char *pb_type;
1709	int ret;
1710
1711	ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
1712				CSD_CALL_INTERFACE, "GetCallInfoAll",
1713				call_info_reply, NULL, DBUS_TYPE_INVALID);
1714	if (ret < 0) {
1715		error("Unable to sent GetCallInfoAll method call");
1716		return;
1717	}
1718
1719	get_calls_active = TRUE;
1720
1721	pb_type = CSD_SIMPB_TYPE_MSISDN;
1722
1723	ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
1724				CSD_SIMPB_INTERFACE, "ReadFirst",
1725				phonebook_read_reply, &msisdn,
1726				DBUS_TYPE_STRING, &pb_type,
1727				DBUS_TYPE_INVALID);
1728	if (ret < 0) {
1729		error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
1730		return;
1731	}
1732
1733	/* Voicemail should be in MBDN index 0 */
1734	pb_type = CSD_SIMPB_TYPE_MBDN;
1735
1736	ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
1737				CSD_SIMPB_INTERFACE, "ReadFirst",
1738				phonebook_read_reply, &vmbx,
1739				DBUS_TYPE_STRING, &pb_type,
1740				DBUS_TYPE_INVALID);
1741	if (ret < 0) {
1742		error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
1743		return;
1744	}
1745}
1746
1747static inline DBusMessage *invalid_args(DBusMessage *msg)
1748{
1749	return g_dbus_create_error(msg,"org.bluez.Error.InvalidArguments",
1750					"Invalid arguments in method call");
1751}
1752
1753static uint32_t get_callflag(const char *callerid_setting)
1754{
1755	if (callerid_setting != NULL) {
1756		if (g_str_equal(callerid_setting, "allowed"))
1757			return CALL_FLAG_PRESENTATION_ALLOWED;
1758		else if (g_str_equal(callerid_setting, "restricted"))
1759			return CALL_FLAG_PRESENTATION_RESTRICTED;
1760		else
1761			return CALL_FLAG_NONE;
1762	} else
1763		return CALL_FLAG_NONE;
1764}
1765
1766static void generate_flag_file(const char *filename)
1767{
1768	int fd;
1769
1770	if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS) ||
1771			g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS) ||
1772			g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
1773		return;
1774
1775	fd = open(filename, O_WRONLY | O_CREAT, 0);
1776	if (fd >= 0)
1777		close(fd);
1778}
1779
1780static void save_callerid_to_file(const char *callerid_setting)
1781{
1782	char callerid_file[FILENAME_MAX];
1783
1784	snprintf(callerid_file, sizeof(callerid_file), "%s%s",
1785					CALLERID_BASE, callerid_setting);
1786
1787	if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS))
1788		rename(ALLOWED_FLAG_FILE, callerid_file);
1789	else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS))
1790		rename(RESTRICTED_FLAG_FILE, callerid_file);
1791	else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
1792		rename(NONE_FLAG_FILE, callerid_file);
1793	else
1794		generate_flag_file(callerid_file);
1795}
1796
1797static uint32_t callerid_from_file(void)
1798{
1799	if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS))
1800		return CALL_FLAG_PRESENTATION_ALLOWED;
1801	else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS))
1802		return CALL_FLAG_PRESENTATION_RESTRICTED;
1803	else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
1804		return CALL_FLAG_NONE;
1805	else
1806		return CALL_FLAG_NONE;
1807}
1808
1809static DBusMessage *set_callerid(DBusConnection *conn, DBusMessage *msg,
1810					void *data)
1811{
1812	const char *callerid_setting;
1813
1814	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING,
1815						&callerid_setting,
1816						DBUS_TYPE_INVALID) == FALSE)
1817		return invalid_args(msg);
1818
1819	if (g_str_equal(callerid_setting, "allowed") ||
1820			g_str_equal(callerid_setting, "restricted") ||
1821			g_str_equal(callerid_setting, "none")) {
1822		save_callerid_to_file(callerid_setting);
1823		callerid = get_callflag(callerid_setting);
1824		DBG("telephony-maemo6 setting callerid flag: %s",
1825							callerid_setting);
1826		return dbus_message_new_method_return(msg);
1827	}
1828
1829	error("telephony-maemo6: invalid argument %s for method call"
1830					" SetCallerId", callerid_setting);
1831		return invalid_args(msg);
1832}
1833
1834static DBusMessage *clear_lastnumber(DBusConnection *conn, DBusMessage *msg,
1835					void *data)
1836{
1837	g_free(last_dialed_number);
1838	last_dialed_number = NULL;
1839
1840	return dbus_message_new_method_return(msg);
1841}
1842
1843static GDBusMethodTable telephony_maemo_methods[] = {
1844	{ "SetCallerId",	"s",	"",	set_callerid,
1845						G_DBUS_METHOD_FLAG_ASYNC },
1846	{ "ClearLastNumber",	"",	"",	clear_lastnumber },
1847	{ }
1848};
1849
1850static void handle_modem_state(DBusMessage *msg)
1851{
1852	const char *state;
1853
1854	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state,
1855							DBUS_TYPE_INVALID)) {
1856		error("Unexpected modem state parameters");
1857		return;
1858	}
1859
1860	DBG("SSC modem state: %s", state);
1861
1862	if (calls != NULL || get_calls_active)
1863		return;
1864
1865	if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online"))
1866		csd_init();
1867}
1868
1869static void modem_state_reply(DBusPendingCall *call, void *user_data)
1870{
1871	DBusMessage *reply = dbus_pending_call_steal_reply(call);
1872	DBusError err;
1873
1874	dbus_error_init(&err);
1875	if (dbus_set_error_from_message(&err, reply)) {
1876		error("get_modem_state: %s, %s", err.name, err.message);
1877		dbus_error_free(&err);
1878	} else
1879		handle_modem_state(reply);
1880
1881	dbus_message_unref(reply);
1882}
1883
1884static DBusHandlerResult signal_filter(DBusConnection *conn,
1885						DBusMessage *msg, void *data)
1886{
1887	const char *path = dbus_message_get_path(msg);
1888
1889	if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
1890		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1891
1892	if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
1893		handle_incoming_call(msg);
1894	else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created"))
1895		handle_outgoing_call(msg);
1896	else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE,
1897							"CreateRequested"))
1898		handle_create_requested(msg);
1899	else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus"))
1900		handle_call_status(msg, path);
1901	else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined"))
1902		handle_conference(msg, TRUE);
1903	else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left"))
1904		handle_conference(msg, FALSE);
1905	else if (dbus_message_is_signal(msg, CSD_CSNET_REGISTRATION,
1906				"RegistrationChanged"))
1907		handle_registration_changed(msg);
1908	else if (dbus_message_is_signal(msg, CSD_CSNET_OPERATOR,
1909				"OperatorNameChanged"))
1910		handle_operator_name_changed(msg);
1911	else if (dbus_message_is_signal(msg, CSD_CSNET_SIGNAL,
1912				"SignalBarsChanged"))
1913		handle_signal_bars_changed(msg);
1914	else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device",
1915					"PropertyModified"))
1916		handle_hal_property_modified(msg);
1917	else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE,
1918						"modem_state_changed_ind"))
1919		handle_modem_state(msg);
1920
1921	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1922}
1923
1924int telephony_init(void)
1925{
1926	const char *battery_cap = "battery";
1927	uint32_t features = AG_FEATURE_EC_ANDOR_NR |
1928				AG_FEATURE_INBAND_RINGTONE |
1929				AG_FEATURE_REJECT_A_CALL |
1930				AG_FEATURE_ENHANCED_CALL_STATUS |
1931				AG_FEATURE_ENHANCED_CALL_CONTROL |
1932				AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
1933				AG_FEATURE_THREE_WAY_CALLING;
1934
1935	connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
1936
1937	if (!dbus_connection_add_filter(connection, signal_filter,
1938						NULL, NULL))
1939		error("Can't add signal filter");
1940
1941	dbus_bus_add_match(connection,
1942			"type=signal,interface=" CSD_CALL_INTERFACE, NULL);
1943	dbus_bus_add_match(connection,
1944			"type=signal,interface=" CSD_CALL_INSTANCE, NULL);
1945	dbus_bus_add_match(connection,
1946			"type=signal,interface=" CSD_CALL_CONFERENCE, NULL);
1947	dbus_bus_add_match(connection,
1948			"type=signal,interface=" CSD_CSNET_REGISTRATION,
1949			NULL);
1950	dbus_bus_add_match(connection,
1951			"type=signal,interface=" CSD_CSNET_OPERATOR,
1952			NULL);
1953	dbus_bus_add_match(connection,
1954			"type=signal,interface=" CSD_CSNET_SIGNAL,
1955			NULL);
1956	dbus_bus_add_match(connection,
1957				"type=signal,interface=" SSC_DBUS_IFACE
1958				",member=modem_state_changed_ind", NULL);
1959
1960	if (send_method_call(SSC_DBUS_NAME, SSC_DBUS_PATH, SSC_DBUS_IFACE,
1961					"get_modem_state", modem_state_reply,
1962					NULL, DBUS_TYPE_INVALID) < 0)
1963		error("Unable to send " SSC_DBUS_IFACE ".get_modem_state()");
1964
1965	generate_flag_file(NONE_FLAG_FILE);
1966	callerid = callerid_from_file();
1967
1968	if (!g_dbus_register_interface(connection, TELEPHONY_MAEMO_PATH,
1969			TELEPHONY_MAEMO_INTERFACE, telephony_maemo_methods,
1970			NULL, NULL, NULL, NULL)) {
1971		error("telephony-maemo6 interface %s init failed on path %s",
1972			TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH);
1973	}
1974
1975	DBG("telephony-maemo6 registering %s interface on path %s",
1976			TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH);
1977
1978	telephony_ready_ind(features, maemo_indicators, response_and_hold,
1979								chld_str);
1980	if (send_method_call("org.freedesktop.Hal",
1981				"/org/freedesktop/Hal/Manager",
1982				"org.freedesktop.Hal.Manager",
1983				"FindDeviceByCapability",
1984				hal_find_device_reply, NULL,
1985				DBUS_TYPE_STRING, &battery_cap,
1986				DBUS_TYPE_INVALID) < 0)
1987		error("Unable to send HAL method call");
1988
1989	return 0;
1990}
1991
1992void telephony_exit(void)
1993{
1994	g_free(net.operator_name);
1995	net.operator_name = NULL;
1996
1997	g_free(last_dialed_number);
1998	last_dialed_number = NULL;
1999
2000	g_slist_foreach(calls, (GFunc) csd_call_free, NULL);
2001	g_slist_free(calls);
2002	calls = NULL;
2003
2004	dbus_connection_remove_filter(connection, signal_filter, NULL);
2005
2006	dbus_connection_unref(connection);
2007	connection = NULL;
2008}
2009