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