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