Add first version of oFono GPRS support
[framework/connectivity/connman.git] / plugins / ofono.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27
28 #include <gdbus.h>
29 #include <string.h>
30
31 #define CONNMAN_API_SUBJECT_TO_CHANGE
32 #include <connman/plugin.h>
33 #include <connman/device.h>
34 #include <connman/network.h>
35 #include <connman/dbus.h>
36 #include <connman/inet.h>
37 #include <connman/log.h>
38
39 #define OFONO_SERVICE                   "org.ofono"
40
41 #define OFONO_MANAGER_INTERFACE         OFONO_SERVICE ".Manager"
42 #define OFONO_MODEM_INTERFACE           OFONO_SERVICE ".Modem"
43 #define OFONO_GPRS_INTERFACE            OFONO_SERVICE ".DataConnectionManager"
44 #define OFONO_SIM_INTERFACE             OFONO_SERVICE ".SimManager"
45 #define OFONO_PRI_CONTEXT_INTERFACE     OFONO_SERVICE ".PrimaryDataContext"
46
47 #define PROPERTY_CHANGED                "PropertyChanged"
48 #define GET_PROPERTIES                  "GetProperties"
49 #define SET_PROPERTY                    "SetProperty"
50
51 #define TIMEOUT 5000
52
53 static DBusConnection *connection;
54
55 static GHashTable *modem_hash = NULL;
56
57 struct modem_data {
58         char *path;
59         struct connman_device *device;
60         gboolean available;
61 };
62
63 static int modem_probe(struct connman_device *device)
64 {
65         DBG("device %p", device);
66
67         return 0;
68 }
69
70 static void modem_remove(struct connman_device *device)
71 {
72         DBG("device %p", device);
73 }
74
75 static void powered_reply(DBusPendingCall *call, void *user_data)
76 {
77         DBusMessage *reply;
78         DBusError error;
79
80         DBG("");
81
82         dbus_error_init(&error);
83
84         reply = dbus_pending_call_steal_reply(call);
85
86         if (dbus_set_error_from_message(&error, reply)) {
87                 connman_error("%s", error.message);
88                 dbus_error_free(&error);
89         }
90
91         dbus_message_unref(reply);
92 }
93
94 static int gprs_change_powered(const char *path, dbus_bool_t powered)
95 {
96         DBusMessage *message;
97         DBusMessageIter iter;
98         DBusPendingCall *call;
99
100         DBG("path %s powered %d", path, powered);
101
102         if (path == NULL)
103                 return -EINVAL;
104
105         message = dbus_message_new_method_call(OFONO_SERVICE, path,
106                                         OFONO_GPRS_INTERFACE, SET_PROPERTY);
107         if (message == NULL)
108                 return -ENOMEM;
109
110         dbus_message_set_auto_start(message, FALSE);
111
112         dbus_message_iter_init_append(message, &iter);
113         connman_dbus_property_append_variant(&iter, "Powered",
114                                                 DBUS_TYPE_BOOLEAN, &powered);
115
116         if (dbus_connection_send_with_reply(connection, message,
117                                                 &call, TIMEOUT) == FALSE) {
118                 connman_error("Failed to change powered property");
119                 dbus_message_unref(message);
120                 return -EINVAL;
121         }
122
123         if (call == NULL) {
124                 connman_error("D-Bus connection not available");
125                 dbus_message_unref(message);
126                 return -EINVAL;
127         }
128
129         dbus_pending_call_set_notify(call, powered_reply, (void *)path, NULL);
130
131         dbus_message_unref(message);
132
133         return -EINPROGRESS;
134 }
135
136 static int modem_enable(struct connman_device *device)
137 {
138         const char *path = connman_device_get_string(device, "Path");
139
140         DBG("device %p, path, %s", device, path);
141
142         return gprs_change_powered(path, TRUE);
143 }
144
145 static int modem_disable(struct connman_device *device)
146 {
147         const char *path = connman_device_get_string(device, "Path");
148
149         DBG("device %p, path %s", device, path);
150
151         return gprs_change_powered(path, FALSE);
152 }
153
154 static struct connman_device_driver modem_driver = {
155         .name           = "modem",
156         .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
157         .probe          = modem_probe,
158         .remove         = modem_remove,
159         .enable         = modem_enable,
160         .disable        = modem_disable,
161 };
162
163 static char *get_ident(const char *path)
164 {
165         char *ident, *pos;
166
167         if (*path != '/')
168                 return NULL;
169
170         ident = g_strdup(path + 1);
171
172         pos = ident;
173
174         while ((pos = strchr(pos, '/')) != NULL)
175                 *pos = '_';
176
177         return ident;
178 }
179
180 static void config_network_reply(DBusPendingCall *call, void *user_data)
181 {
182         struct connman_network *network = user_data;
183         DBusMessage *reply;
184         DBusMessageIter array, dict;
185         gboolean internet_type;
186
187         DBG("network %p", network);
188
189         reply = dbus_pending_call_steal_reply(call);
190         if (reply == NULL)
191                 goto done;
192
193         if (dbus_message_iter_init(reply, &array) == FALSE)
194                 goto done;
195
196         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
197                 goto done;
198
199         dbus_message_iter_recurse(&array, &dict);
200
201         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
202                 DBusMessageIter entry, value;
203                 const char *key;
204
205                 dbus_message_iter_recurse(&dict, &entry);
206                 dbus_message_iter_get_basic(&entry, &key);
207
208                 dbus_message_iter_next(&entry);
209                 dbus_message_iter_recurse(&entry, &value);
210
211                 if (g_str_equal(key, "Name") == TRUE) {
212                         const char *name;
213
214                         dbus_message_iter_get_basic(&value, &name);
215                         connman_network_set_name(network, name);
216                 } else if (g_str_equal(key, "Type") == TRUE) {
217                         const char *type;
218
219                         dbus_message_iter_get_basic(&value, &type);
220                         if (g_strcmp0(type, "internet") == 0) {
221                                 internet_type = TRUE;
222
223                                 connman_network_set_protocol(network,
224                                                 CONNMAN_NETWORK_PROTOCOL_IP);
225                         } else {
226                                 internet_type = FALSE;
227
228                                 connman_network_set_protocol(network,
229                                         CONNMAN_NETWORK_PROTOCOL_UNKNOWN);
230                         }
231                 }
232
233                 dbus_message_iter_next(&dict);
234         }
235
236         if (internet_type == TRUE) {
237                 const char *path;
238                 char *group;
239
240                 path = connman_network_get_string(network, "Path");
241
242                 group = get_ident(path);
243
244                 connman_network_set_group(network, group);
245
246                 g_free(group);
247         }
248
249 done:
250         dbus_message_unref(reply);
251 }
252
253 static void config_network(struct connman_network *network, const char *path)
254 {
255         DBusMessage *message;
256         DBusPendingCall *call;
257
258         DBG("path %s", path);
259
260         message = dbus_message_new_method_call(OFONO_SERVICE, path,
261                                 OFONO_PRI_CONTEXT_INTERFACE, GET_PROPERTIES);
262         if (message == NULL)
263                 return;
264
265         dbus_message_set_auto_start(message, FALSE);
266
267         if (dbus_connection_send_with_reply(connection, message,
268                                                 &call, TIMEOUT) == FALSE) {
269                 connman_error("Failed to get Primary Context");
270                 goto done;
271         }
272
273         if (call == NULL) {
274                 connman_error("D-Bus connection not available");
275                 goto done;
276         }
277
278         dbus_pending_call_set_notify(call, config_network_reply,
279                                                 (void *)network, NULL);
280
281 done:
282         dbus_message_unref(message);
283 }
284
285 static int network_probe(struct connman_network *network)
286 {
287         const char *path;
288
289         path = connman_network_get_string(network, "Path");
290
291         DBG("network %p path %s", network, path);
292
293         config_network(network, path);
294
295         return 0;
296 }
297
298 static struct connman_network *pending_network;
299
300 static gboolean pending_network_is_available(
301                 struct connman_network *pending_network)
302 {
303         struct connman_device *device;
304         struct connman_network *network;
305         const char *identifier;
306         char *ident;
307
308         /* Modem may be removed during waiting for active reply */
309         device  = connman_network_get_device(pending_network);
310         if (device == NULL)
311                 return FALSE;
312
313         identifier = connman_network_get_identifier(pending_network);
314
315         ident = g_strdup(identifier);
316
317         connman_network_unref(pending_network);
318
319         /* network may be removed during waiting for active reply */
320         network = connman_device_get_network(device, ident);
321
322         g_free(ident);
323
324         if (network == NULL)
325                 return FALSE;
326
327         return TRUE;
328 }
329
330 static void set_active_reply(DBusPendingCall *call, void *user_data)
331 {
332         DBusMessage *reply;
333         DBusError error;
334         struct connman_network *network = user_data;
335
336         DBG("network %p", network);
337
338         if (pending_network_is_available(network) == FALSE)
339                 return;
340
341         reply = dbus_pending_call_steal_reply(call);
342         if (reply == NULL)
343                 return;
344
345         dbus_error_init(&error);
346         if (dbus_set_error_from_message(&error, reply)) {
347                 if (connman_network_get_index(network) < 0)
348                         connman_network_set_error(network,
349                                 CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
350
351                 pending_network = NULL;
352
353                 connman_error("%s", error.message);
354
355                 dbus_error_free(&error);
356         } else
357                 pending_network = network;
358
359         dbus_message_unref(reply);
360 }
361
362 static int set_network_active(struct connman_network *network,
363                                                 dbus_bool_t active)
364 {
365         DBusMessage *message;
366         DBusPendingCall *call;
367         DBusMessageIter iter;
368
369         const char *path = connman_network_get_string(network, "Path");
370
371         DBG("network %p, path %s, active %d", network, path, active);
372
373         if (path == NULL)
374                 return -EINVAL;
375
376         message = dbus_message_new_method_call(OFONO_SERVICE, path,
377                                 OFONO_PRI_CONTEXT_INTERFACE, SET_PROPERTY);
378         if (message == NULL)
379                 return -ENOMEM;
380
381         dbus_message_set_auto_start(message, FALSE);
382
383         dbus_message_iter_init_append(message, &iter);
384         connman_dbus_property_append_variant(&iter, "Active",
385                                                 DBUS_TYPE_BOOLEAN, &active);
386
387         if (dbus_connection_send_with_reply(connection, message,
388                                         &call, TIMEOUT * 10) == FALSE) {
389                 connman_error("Failed to connect service");
390                 dbus_message_unref(message);
391                 return -EINVAL;
392         }
393
394         if (call == NULL) {
395                 connman_error("D-Bus connection not available");
396                 dbus_message_unref(message);
397                 return -EINVAL;
398         }
399
400         connman_network_ref(network);
401
402         dbus_pending_call_set_notify(call, set_active_reply, network, NULL);
403
404         dbus_message_unref(message);
405
406         if (active == TRUE)
407                 return -EINPROGRESS;
408
409         return 0;
410 }
411
412 static int network_connect(struct connman_network *network)
413 {
414         if (connman_network_get_index(network) >= 0)
415                 return -EISCONN;
416
417         return set_network_active(network, TRUE);
418 }
419
420 static int network_disconnect(struct connman_network *network)
421 {
422         if (connman_network_get_index(network) < 0)
423                 return -ENOTCONN;
424
425         return set_network_active(network, FALSE);
426 }
427
428 static void network_remove(struct connman_network *network)
429 {
430         DBG("network %p", network);
431 }
432
433 static struct connman_network_driver network_driver = {
434         .name           = "network",
435         .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
436         .probe          = network_probe,
437         .remove         = network_remove,
438         .connect        = network_connect,
439         .disconnect     = network_disconnect,
440 };
441
442 static void add_network(struct connman_device *device, const char *path)
443 {
444         struct connman_network *network;
445         char *ident;
446
447         DBG("device %p path %s", device, path);
448
449         network = connman_device_get_network(device, path);
450         if (network != NULL)
451                 return;
452
453         ident = get_ident(path);
454
455         network = connman_network_create(ident,
456                                         CONNMAN_NETWORK_TYPE_CELLULAR);
457         if (network == NULL)
458                 return;
459
460         g_free(ident);
461
462         connman_network_set_string(network, "Path", path);
463         connman_network_set_available(network, TRUE);
464         connman_network_set_index(network, -1);
465         connman_device_add_network(device, network);
466 }
467
468 static void add_networks(struct connman_device *device, DBusMessageIter *array)
469 {
470         DBusMessageIter entry;
471
472         DBG("");
473
474         dbus_message_iter_recurse(array, &entry);
475
476         while (dbus_message_iter_get_arg_type(&entry) ==
477                                         DBUS_TYPE_OBJECT_PATH) {
478                 const char *path;
479
480                 dbus_message_iter_get_basic(&entry, &path);
481
482                 add_network(device, path);
483
484                 dbus_message_iter_next(&entry);
485         }
486 }
487
488 static void check_networks_reply(DBusPendingCall *call, void *user_data)
489 {
490         struct connman_device *device = user_data;
491         DBusMessage *reply;
492         DBusMessageIter array, dict, contexts;
493         dbus_bool_t attached;
494
495         DBG("device %p", device);
496
497         reply = dbus_pending_call_steal_reply(call);
498         if (reply == NULL)
499                 goto done;
500
501         if (dbus_message_iter_init(reply, &array) == FALSE)
502                 goto done;
503
504         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
505                 goto done;
506
507         dbus_message_iter_recurse(&array, &dict);
508
509         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
510                 DBusMessageIter entry, value;
511                 const char *key;
512
513                 dbus_message_iter_recurse(&dict, &entry);
514                 dbus_message_iter_get_basic(&entry, &key);
515
516                 dbus_message_iter_next(&entry);
517                 dbus_message_iter_recurse(&entry, &value);
518
519                 DBG("key %s", key);
520
521                 if (g_str_equal(key, "Attached") == TRUE) {
522                         dbus_message_iter_get_basic(&value, &attached);
523                         DBG("Attached %d", attached);
524                 } else if (g_str_equal(key, "PrimaryContexts") == TRUE) {
525                         contexts = value;
526                 } else if (g_str_equal(key, "Status") == TRUE) {
527                         const char *status;
528
529                         dbus_message_iter_get_basic(&value, &status);
530                         /* FIXME: add roaming support */
531                 } else if (g_str_equal(key, "Powered") == TRUE) {
532                         dbus_bool_t powered;
533
534                         dbus_message_iter_get_basic(&value, &powered);
535
536                         connman_device_set_powered(device, powered);
537                 }
538
539                 dbus_message_iter_next(&dict);
540         }
541
542         if (attached == TRUE)
543                 add_networks(device, &contexts);
544
545 done:
546         dbus_message_unref(reply);
547 }
548
549 static void check_networks(struct modem_data *modem)
550 {
551         DBusMessage *message;
552         DBusPendingCall *call;
553         struct connman_device *device;
554
555         DBG("modem %p", modem);
556
557         if (modem == NULL)
558                 return;
559
560         device = modem->device;
561         if (device == NULL)
562                 return;
563
564         message = dbus_message_new_method_call(OFONO_SERVICE, modem->path,
565                                         OFONO_GPRS_INTERFACE, GET_PROPERTIES);
566         if (message == NULL)
567                 return;
568
569         dbus_message_set_auto_start(message, FALSE);
570
571         if (dbus_connection_send_with_reply(connection, message,
572                                                 &call, TIMEOUT) == FALSE) {
573                 connman_error("Failed to get ofono GPRS");
574                 goto done;
575         }
576
577         if (call == NULL) {
578                 connman_error("D-Bus connection not available");
579                 goto done;
580         }
581
582         dbus_pending_call_set_notify(call, check_networks_reply,
583                                                 (void *)device, NULL);
584
585 done:
586         dbus_message_unref(message);
587 }
588
589 static void add_device(const char *path, const char *imsi)
590 {
591         struct modem_data *modem;
592         struct connman_device *device;
593
594         DBG("path %s imsi %s", path, imsi);
595
596         if (path == NULL)
597                 return;
598
599         if (imsi == NULL)
600                 return;
601
602         modem = g_hash_table_lookup(modem_hash, path);
603         if (modem == NULL)
604                 return;
605
606         device = connman_device_create(imsi, CONNMAN_DEVICE_TYPE_CELLULAR);
607         if (device == NULL)
608                 return;
609
610         connman_device_set_ident(device, imsi);
611
612         connman_device_set_mode(device, CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE);
613
614         connman_device_set_string(device, "Path", path);
615
616         if (connman_device_register(device) < 0) {
617                 connman_device_unref(device);
618                 return;
619         }
620
621         modem->device = device;
622
623         check_networks(modem);
624 }
625
626 static void sim_properties_reply(DBusPendingCall *call, void *user_data)
627 {
628         const char *path = user_data;
629         DBusMessage *reply;
630         DBusMessageIter array, dict;
631
632         DBG("path %s", path);
633
634         reply = dbus_pending_call_steal_reply(call);
635         if (reply == NULL)
636                 return;
637
638         if (dbus_message_iter_init(reply, &array) == FALSE)
639                 goto done;
640
641         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
642                 goto done;
643
644         dbus_message_iter_recurse(&array, &dict);
645
646         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
647                 DBusMessageIter entry, value;
648                 const char *key, *imsi;
649
650                 dbus_message_iter_recurse(&dict, &entry);
651                 dbus_message_iter_get_basic(&entry, &key);
652
653                 dbus_message_iter_next(&entry);
654                 dbus_message_iter_recurse(&entry, &value);
655
656                 if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
657                         dbus_message_iter_get_basic(&value, &imsi);
658
659                         add_device(path, imsi);
660                 }
661
662                 dbus_message_iter_next(&dict);
663         }
664
665 done:
666         dbus_message_unref(reply);
667 }
668
669 static void get_imsi(const char *path)
670 {
671         DBusMessage *message;
672         DBusPendingCall *call;
673
674         DBG("path %s", path);
675
676         message = dbus_message_new_method_call(OFONO_SERVICE, path,
677                                 OFONO_SIM_INTERFACE, GET_PROPERTIES);
678         if (message == NULL)
679                 return;
680
681         dbus_message_set_auto_start(message, FALSE);
682
683         if (dbus_connection_send_with_reply(connection, message,
684                                                 &call, TIMEOUT) == FALSE) {
685                 connman_error("Failed to get ofono modem sim");
686                 goto done;
687         }
688
689         if (call == NULL) {
690                 connman_error("D-Bus connection not available");
691                 goto done;
692         }
693
694         dbus_pending_call_set_notify(call, sim_properties_reply,
695                                                 (void *)path, NULL);
696
697 done:
698         dbus_message_unref(message);
699 }
700
701 static int modem_change_powered(const char *path, dbus_bool_t powered)
702 {
703         DBusMessage *message;
704         DBusMessageIter iter;
705         DBusPendingCall *call;
706
707         DBG("path %s powered %d", path, powered);
708
709         if (path == NULL)
710                 return -EINVAL;
711
712         message = dbus_message_new_method_call(OFONO_SERVICE, path,
713                                         OFONO_MODEM_INTERFACE, SET_PROPERTY);
714         if (message == NULL)
715                 return -ENOMEM;
716
717         dbus_message_set_auto_start(message, FALSE);
718
719         dbus_message_iter_init_append(message, &iter);
720         connman_dbus_property_append_variant(&iter, "Powered",
721                                                 DBUS_TYPE_BOOLEAN, &powered);
722
723         if (dbus_connection_send_with_reply(connection, message,
724                                                 &call, TIMEOUT) == FALSE) {
725                 connman_error("Failed to change powered property");
726                 dbus_message_unref(message);
727                 return -EINVAL;
728         }
729
730         if (call == NULL) {
731                 connman_error("D-Bus connection not available");
732                 dbus_message_unref(message);
733                 return -EINVAL;
734         }
735
736         dbus_pending_call_set_notify(call, powered_reply, NULL, NULL);
737
738         dbus_message_unref(message);
739
740         return -EINPROGRESS;
741 }
742
743 static struct modem_data *add_modem(const char *path)
744 {
745         struct modem_data *modem;
746
747         if (path == NULL)
748                 return NULL;
749
750         modem = g_hash_table_lookup(modem_hash, path);
751         if (modem != NULL) {
752                 modem->available = TRUE;
753
754                 return modem;
755         }
756
757         modem = g_try_new0(struct modem_data, 1);
758         if (modem == NULL)
759                 return NULL;
760
761         modem->path = g_strdup(path);
762         modem->device = NULL;
763         modem->available = TRUE;
764
765         g_hash_table_insert(modem_hash, g_strdup(path), modem);
766
767         return modem;
768 }
769
770 static gboolean modem_has_gprs(DBusMessageIter *array)
771 {
772         DBusMessageIter entry;
773
774         dbus_message_iter_recurse(array, &entry);
775
776         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
777                 const char *interface;
778
779                 dbus_message_iter_get_basic(&entry, &interface);
780
781                 if (g_strcmp0(OFONO_GPRS_INTERFACE, interface) == 0)
782                         return TRUE;
783
784                 dbus_message_iter_next(&entry);
785         }
786
787         return FALSE;
788 }
789
790 static void modem_properties_reply(DBusPendingCall *call, void *user_data)
791 {
792         DBusMessage *reply;
793         DBusMessageIter array, dict;
794         const char *path = user_data;
795
796         DBG("path %s", path);
797
798         reply = dbus_pending_call_steal_reply(call);
799         if (reply == NULL)
800                 goto done;
801
802         if (dbus_message_iter_init(reply, &array) == FALSE)
803                 goto done;
804
805         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
806                 goto done;
807
808         dbus_message_iter_recurse(&array, &dict);
809
810         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
811                 DBusMessageIter entry, value;
812                 const char *key;
813                 dbus_bool_t powered;
814
815                 dbus_message_iter_recurse(&dict, &entry);
816                 dbus_message_iter_get_basic(&entry, &key);
817
818                 dbus_message_iter_next(&entry);
819                 dbus_message_iter_recurse(&entry, &value);
820
821                 if (g_str_equal(key, "Powered") == TRUE) {
822                         dbus_message_iter_get_basic(&value, &powered);
823
824                         if (powered == FALSE) {
825                                 modem_change_powered(path, TRUE);
826                                 break;
827                         }
828                 } else if (g_str_equal(key, "Interface") == TRUE) {
829                         if (modem_has_gprs(&value) == TRUE)
830                                 get_imsi(path);
831                 }
832
833                 dbus_message_iter_next(&dict);
834         }
835
836 done:
837         dbus_message_unref(reply);
838 }
839
840 static void get_modem_properties(struct modem_data *modem)
841 {
842         DBusMessage *message;
843         DBusPendingCall *call;
844
845         DBG("path %s", modem->path);
846
847         if (modem->path == NULL)
848                 return;
849
850         message = dbus_message_new_method_call(OFONO_SERVICE, modem->path,
851                                 OFONO_MODEM_INTERFACE, GET_PROPERTIES);
852         if (message == NULL)
853                 return;
854
855         dbus_message_set_auto_start(message, FALSE);
856
857         if (dbus_connection_send_with_reply(connection, message,
858                                                 &call, TIMEOUT) == FALSE) {
859                 connman_error("Failed to get ofono modem");
860                 goto done;
861         }
862
863         if (call == NULL) {
864                 connman_error("D-Bus connection not available");
865                 goto done;
866         }
867
868         dbus_pending_call_set_notify(call, modem_properties_reply,
869                                                 (void *)modem->path, NULL);
870
871 done:
872         dbus_message_unref(message);
873 }
874
875 static void mask_unavailable(gpointer key, gpointer value, gpointer user_data)
876 {
877         struct modem_data *modem = value;
878
879         modem->available = FALSE;
880 }
881
882 static void modems_set_unavailable()
883 {
884         g_hash_table_foreach(modem_hash, mask_unavailable, NULL);
885 }
886
887 static void cleanup_modem(gpointer key, gpointer value, gpointer user_data)
888 {
889         struct modem_data *modem = value;
890
891         if (modem->available == FALSE)
892                 g_hash_table_remove(modem_hash, key);
893 }
894
895 static void cleanup_modems()
896 {
897         g_hash_table_foreach(modem_hash, cleanup_modem, NULL);
898 }
899
900 static void update_modems(DBusMessageIter *array)
901 {
902         DBusMessageIter entry;
903
904         dbus_message_iter_recurse(array, &entry);
905
906         modems_set_unavailable();
907
908         while (dbus_message_iter_get_arg_type(&entry) ==
909                                         DBUS_TYPE_OBJECT_PATH) {
910                 const char *path;
911                 struct modem_data *modem;
912
913                 dbus_message_iter_get_basic(&entry, &path);
914
915                 modem = add_modem(path);
916                 if (modem != NULL)
917                         get_modem_properties(modem);
918
919                 dbus_message_iter_next(&entry);
920         }
921
922         cleanup_modems();
923 }
924
925 static void manager_properties_reply(DBusPendingCall *call, void *user_data)
926 {
927         DBusMessage *reply;
928         DBusMessageIter array, dict;
929
930         DBG("");
931
932         reply = dbus_pending_call_steal_reply(call);
933         if (reply == NULL)
934                 goto done;
935
936         if (dbus_message_iter_init(reply, &array) == FALSE)
937                 goto done;
938
939         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
940                 goto done;
941
942         dbus_message_iter_recurse(&array, &dict);
943
944         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
945                 DBusMessageIter entry, value;
946                 const char *key;
947
948                 dbus_message_iter_recurse(&dict, &entry);
949                 dbus_message_iter_get_basic(&entry, &key);
950
951                 dbus_message_iter_next(&entry);
952                 dbus_message_iter_recurse(&entry, &value);
953
954                 if (g_str_equal(key, "Modems") == TRUE) {
955                         update_modems(&value);
956                         break;
957                 }
958
959                 dbus_message_iter_next(&dict);
960         }
961
962 done:
963         dbus_message_unref(reply);
964 }
965
966 static void modem_remove_device(struct modem_data *modem)
967 {
968         if (modem->device == NULL)
969                 return;
970
971         connman_device_unregister(modem->device);
972         connman_device_unref(modem->device);
973
974         modem->device = NULL;
975 }
976
977 static void remove_modem(gpointer data)
978 {
979         struct modem_data *modem = data;
980
981         g_free(modem->path);
982
983         modem_remove_device(modem);
984
985         g_free(modem);
986 }
987
988 static void ofono_connect(DBusConnection *connection, void *user_data)
989 {
990         DBusMessage *message;
991         DBusPendingCall *call;
992
993         DBG("connection %p", connection);
994
995         modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
996                                                 g_free, remove_modem);
997
998         message = dbus_message_new_method_call(OFONO_SERVICE, "/",
999                                 OFONO_MANAGER_INTERFACE, GET_PROPERTIES);
1000         if (message == NULL)
1001                 return;
1002
1003         dbus_message_set_auto_start(message, FALSE);
1004
1005         if (dbus_connection_send_with_reply(connection, message,
1006                                                 &call, TIMEOUT) == FALSE) {
1007                 connman_error("Failed to get ofono modems");
1008                 goto done;
1009         }
1010
1011         if (call == NULL) {
1012                 connman_error("D-Bus connection not available");
1013                 goto done;
1014         }
1015
1016         dbus_pending_call_set_notify(call, manager_properties_reply,
1017                                                                 NULL, NULL);
1018
1019 done:
1020         dbus_message_unref(message);
1021
1022 }
1023
1024 static void ofono_disconnect(DBusConnection *connection, void *user_data)
1025 {
1026         DBG("connection %p", connection);
1027
1028         if (modem_hash == NULL)
1029                 return;
1030
1031         g_hash_table_destroy(modem_hash);
1032
1033         modem_hash = NULL;
1034 }
1035
1036 static void modem_changed(DBusConnection *connection, DBusMessage *message)
1037 {
1038         const char *path = dbus_message_get_path(message);
1039         struct modem_data *modem;
1040         DBusMessageIter iter, value;
1041         const char *key;
1042
1043         DBG("path %s", path);
1044
1045         modem = g_hash_table_lookup(modem_hash, path);
1046         if (modem == NULL)
1047                 return;
1048
1049         if (dbus_message_iter_init(message, &iter) == FALSE)
1050                 return;
1051
1052         dbus_message_iter_get_basic(&iter, &key);
1053
1054         dbus_message_iter_next(&iter);
1055         dbus_message_iter_recurse(&iter, &value);
1056
1057         if (g_str_equal(key, "Powered") == TRUE) {
1058                 dbus_bool_t powered;
1059
1060                 dbus_message_iter_get_basic(&value, &powered);
1061                 if (powered == TRUE)
1062                         return;
1063
1064                 modem_remove_device(modem);
1065         } else if (g_str_equal(key, "Interfaces") == TRUE) {
1066                 if (modem_has_gprs(&value) == TRUE) {
1067                         if (modem->device == NULL)
1068                                 get_imsi(modem->path);
1069                 } else if (modem->device != NULL)
1070                         modem_remove_device(modem);
1071         }
1072 }
1073
1074 static void gprs_changed(DBusConnection *connection, DBusMessage *message)
1075 {
1076         const char *path = dbus_message_get_path(message);
1077         struct modem_data *modem;
1078         DBusMessageIter iter, value;
1079         const char *key;
1080
1081         DBG("path %s", path);
1082
1083         modem = g_hash_table_lookup(modem_hash, path);
1084         if (modem == NULL)
1085                 return;
1086
1087         if (dbus_message_iter_init(message, &iter) == FALSE)
1088                 return;
1089
1090         dbus_message_iter_get_basic(&iter, &key);
1091
1092         dbus_message_iter_next(&iter);
1093         dbus_message_iter_recurse(&iter, &value);
1094
1095         if (g_str_equal(key, "Attached") == TRUE) {
1096                 dbus_bool_t attached;
1097
1098                 dbus_message_iter_get_basic(&value, &attached);
1099
1100                 DBG("Attached %d", attached);
1101
1102                 if (attached == TRUE)
1103                         check_networks(modem);
1104                 else if (modem->device != NULL)
1105                         connman_device_remove_all_networks(modem->device);
1106
1107         } else if (g_str_equal(key, "Status") == TRUE) {
1108                 const char *status;
1109                 dbus_message_iter_get_basic(&value, &status);
1110
1111                 DBG("status %s", status);
1112
1113                 /* FIXME: add roaming support */
1114         } else if (g_str_equal(key, "PrimaryContexts") == TRUE) {
1115                 check_networks(modem);
1116         } else if (g_str_equal(key, "Powered") == TRUE) {
1117                 dbus_bool_t powered;
1118
1119                 if (modem->device == NULL)
1120                         return;
1121
1122                 dbus_message_iter_get_basic(&value, &powered);
1123                 connman_device_set_powered(modem->device, powered);
1124         }
1125 }
1126
1127 static void manager_changed(DBusConnection *connection, DBusMessage *message)
1128 {
1129         const char *path = dbus_message_get_path(message);
1130         DBusMessageIter iter, value;
1131         const char *key;
1132
1133         DBG("path %s", path);
1134
1135         if (dbus_message_iter_init(message, &iter) == FALSE)
1136                 return;
1137
1138         dbus_message_iter_get_basic(&iter, &key);
1139
1140         dbus_message_iter_next(&iter);
1141         dbus_message_iter_recurse(&iter, &value);
1142
1143         if (g_str_equal(key, "Modems") == TRUE)
1144                 update_modems(&value);
1145 }
1146
1147 static void update_settings(DBusMessageIter *array)
1148 {
1149         DBusMessageIter dict;
1150         const char *interface = NULL;
1151
1152         DBG("");
1153
1154         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
1155                 return;
1156
1157         dbus_message_iter_recurse(array, &dict);
1158
1159         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
1160                 DBusMessageIter entry, value;
1161                 const char *key;
1162
1163                 dbus_message_iter_recurse(&dict, &entry);
1164                 dbus_message_iter_get_basic(&entry, &key);
1165
1166                 dbus_message_iter_next(&entry);
1167                 dbus_message_iter_recurse(&entry, &value);
1168
1169                 if (g_str_equal(key, "Interface") == TRUE) {
1170                         int index;
1171
1172                         dbus_message_iter_get_basic(&value, &interface);
1173
1174                         DBG("interface %s", interface);
1175
1176                         index = connman_inet_ifindex(interface);
1177                         if (index >= 0) {
1178                                 connman_network_set_index(
1179                                         pending_network, index);
1180                         } else {
1181                                 connman_error("Can not find interface %s",
1182                                                                 interface);
1183                                 break;
1184                         }
1185                 } else if (g_str_equal(key, "Method") == TRUE) {
1186                         const char *method;
1187
1188                         dbus_message_iter_get_basic(&value, &method);
1189                         if (g_strcmp0(method, "static") == 0) {
1190                                 DBG("static");
1191                         } else if (g_strcmp0(method, "dhcp") == 0) {
1192                                 DBG("dhcp");
1193                                 break;
1194                         }
1195                 } else if (g_str_equal(key, "address") == TRUE) {
1196                         const char *address;
1197
1198                         dbus_message_iter_get_basic(&value, &address);
1199
1200                         DBG("address %s", address);
1201                 }
1202                 /* FIXME: add static setting */
1203                 dbus_message_iter_next(&dict);
1204         }
1205
1206         /* deactive, oFono send NULL inteface before deactive signal */
1207         if (interface == NULL)
1208                 connman_network_set_index(pending_network, -1);
1209 }
1210
1211 static void pri_context_changed(DBusConnection *connection,
1212                                         DBusMessage *message)
1213 {
1214         const char *path = dbus_message_get_path(message);
1215         const char *pending_path;
1216         DBusMessageIter iter, value;
1217         const char *key;
1218
1219         DBG("pending_network %p, path %s", pending_network, path);
1220
1221         if (pending_network == NULL)
1222                 return;
1223
1224         pending_path = connman_network_get_string(pending_network, "Path");
1225         if (g_strcmp0(pending_path, path) != 0)
1226                 return;
1227
1228         if (dbus_message_iter_init(message, &iter) == FALSE)
1229                 return;
1230
1231         dbus_message_iter_get_basic(&iter, &key);
1232
1233         dbus_message_iter_next(&iter);
1234         dbus_message_iter_recurse(&iter, &value);
1235
1236         if (g_str_equal(key, "Settings") == TRUE) {
1237                 update_settings(&value);
1238         } else if (g_str_equal(key, "Active") == TRUE) {
1239                 dbus_bool_t active;
1240
1241                 dbus_message_iter_get_basic(&value, &active);
1242                 connman_network_set_connected(pending_network, active);
1243
1244                 pending_network = NULL;
1245         }
1246 }
1247
1248 static DBusHandlerResult ofono_signal(DBusConnection *connection,
1249                                         DBusMessage *message, void *user_data)
1250 {
1251         if (dbus_message_is_signal(message, OFONO_MODEM_INTERFACE,
1252                                                 PROPERTY_CHANGED) == TRUE) {
1253                 modem_changed(connection, message);
1254         } else if (dbus_message_is_signal(message, OFONO_GPRS_INTERFACE,
1255                                                 PROPERTY_CHANGED) == TRUE) {
1256                 gprs_changed(connection, message);
1257         } else if (dbus_message_is_signal(message, OFONO_MANAGER_INTERFACE,
1258                                                 PROPERTY_CHANGED) == TRUE) {
1259                 manager_changed(connection, message);
1260         } else if (dbus_message_is_signal(message, OFONO_PRI_CONTEXT_INTERFACE,
1261                                                 PROPERTY_CHANGED) == TRUE) {
1262                 pri_context_changed(connection, message);
1263         }
1264
1265         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1266 }
1267
1268 static const char *gprs_rule = "type=signal, member=" PROPERTY_CHANGED
1269                                         ",interface=" OFONO_GPRS_INTERFACE;
1270 static const char *modem_rule = "type=signal,member=" PROPERTY_CHANGED
1271                                         ",interface=" OFONO_MODEM_INTERFACE;
1272 static const char *manager_rule = "type=signal,member=" PROPERTY_CHANGED
1273                                         ",interface=" OFONO_MANAGER_INTERFACE;
1274 static const char *pri_context_rule = "type=signal,member=" PROPERTY_CHANGED
1275                                 ", interface=" OFONO_PRI_CONTEXT_INTERFACE;
1276
1277 static guint watch;
1278
1279 static int ofono_init(void)
1280 {
1281         int err;
1282
1283         connection = connman_dbus_get_connection();
1284         if (connection == NULL)
1285                 return -EIO;
1286
1287         if (dbus_connection_add_filter(connection, ofono_signal,
1288                                                 NULL, NULL) == FALSE) {
1289                 err = -EIO;
1290                 goto unref;
1291         }
1292
1293         err = connman_network_driver_register(&network_driver);
1294         if (err < 0)
1295                 goto remove;
1296
1297         err = connman_device_driver_register(&modem_driver);
1298         if (err < 0) {
1299                 connman_network_driver_unregister(&network_driver);
1300                 goto remove;
1301         }
1302
1303         watch = g_dbus_add_service_watch(connection, OFONO_SERVICE,
1304                         ofono_connect, ofono_disconnect, NULL, NULL);
1305         if (watch == 0) {
1306                 err = -EIO;
1307                 goto remove;
1308         }
1309
1310         dbus_bus_add_match(connection, modem_rule, NULL);
1311         dbus_bus_add_match(connection, gprs_rule, NULL);
1312         dbus_bus_add_match(connection, manager_rule, NULL);
1313         dbus_bus_add_match(connection, pri_context_rule, NULL);
1314
1315         return 0;
1316
1317 remove:
1318         dbus_connection_remove_filter(connection, ofono_signal, NULL);
1319
1320 unref:
1321         dbus_connection_unref(connection);
1322
1323         return err;
1324 }
1325
1326 static void ofono_exit(void)
1327 {
1328         dbus_bus_remove_match(connection, modem_rule, NULL);
1329         dbus_bus_remove_match(connection, gprs_rule, NULL);
1330         dbus_bus_remove_match(connection, manager_rule, NULL);
1331         dbus_bus_remove_match(connection, pri_context_rule, NULL);
1332
1333         g_dbus_remove_watch(connection, watch);
1334
1335         ofono_disconnect(connection, NULL);
1336
1337         connman_device_driver_unregister(&modem_driver);
1338         connman_network_driver_unregister(&network_driver);
1339
1340         dbus_connection_remove_filter(connection, ofono_signal, NULL);
1341
1342         dbus_connection_unref(connection);
1343 }
1344
1345 CONNMAN_PLUGIN_DEFINE(ofono, "oFono telephony plugin", VERSION,
1346                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ofono_init, ofono_exit)