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