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