ofono: Get ConnectionContext properties
[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 #include <stdlib.h>
29
30 #include <gdbus.h>
31 #include <string.h>
32 #include <stdint.h>
33
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/plugin.h>
36 #include <connman/device.h>
37 #include <connman/network.h>
38 #include <connman/inet.h>
39 #include <connman/dbus.h>
40 #include <connman/log.h>
41
42 #define uninitialized_var(x) x = x
43
44 #define OFONO_SERVICE                   "org.ofono"
45
46 #define OFONO_MANAGER_INTERFACE         OFONO_SERVICE ".Manager"
47 #define OFONO_MODEM_INTERFACE           OFONO_SERVICE ".Modem"
48 #define OFONO_SIM_INTERFACE             OFONO_SERVICE ".SimManager"
49 #define OFONO_NETREG_INTERFACE          OFONO_SERVICE ".NetworkRegistration"
50 #define OFONO_CM_INTERFACE              OFONO_SERVICE ".ConnectionManager"
51 #define OFONO_CONTEXT_INTERFACE         OFONO_SERVICE ".ConnectionContext"
52
53 #define MODEM_ADDED                     "ModemAdded"
54 #define MODEM_REMOVED                   "ModemRemoved"
55 #define PROPERTY_CHANGED                "PropertyChanged"
56 #define CONTEXT_ADDED                   "ContextAdded"
57 #define CONTEXT_REMOVED                 "ContextRemoved"
58
59 #define GET_PROPERTIES                  "GetProperties"
60 #define SET_PROPERTY                    "SetProperty"
61 #define GET_MODEMS                      "GetModems"
62 #define GET_CONTEXTS                    "GetContexts"
63
64 #define TIMEOUT 40000
65
66 enum ofono_api {
67         OFONO_API_SIM =         0x1,
68         OFONO_API_NETREG =      0x2,
69         OFONO_API_CM =          0x4,
70 };
71
72 static DBusConnection *connection;
73
74 static GHashTable *modem_hash;
75 static GHashTable *context_hash;
76
77 struct network_context {
78         char *path;
79         int index;
80
81         enum connman_ipconfig_method ipv4_method;
82         struct connman_ipaddress *ipv4_address;
83         char *ipv4_nameservers;
84
85         enum connman_ipconfig_method ipv6_method;
86         struct connman_ipaddress *ipv6_address;
87         char *ipv6_nameservers;
88 };
89
90 struct modem_data {
91         char *path;
92
93         struct connman_device *device;
94
95         struct network_context *context;
96
97         /* Modem Interface */
98         char *serial;
99         connman_bool_t powered;
100         connman_bool_t online;
101         uint8_t interfaces;
102
103         connman_bool_t set_powered;
104         connman_bool_t set_online;
105
106         /* ConnectionManager Interface */
107         connman_bool_t attached;
108         connman_bool_t cm_powered;
109
110         connman_bool_t set_cm_powered;
111
112         /* ConnectionContext Interface */
113         connman_bool_t active;
114
115         /* SimManager Interface */
116         char *imsi;
117
118         /* pending calls */
119         DBusPendingCall *call_set_property;
120         DBusPendingCall *call_get_properties;
121         DBusPendingCall *call_get_contexts;
122 };
123
124 static struct network_context *network_context_alloc(const char *path)
125 {
126         struct network_context *context;
127
128         context = g_try_new0(struct network_context, 1);
129         if (context == NULL)
130                 return NULL;
131
132         context->path = g_strdup(path);
133         context->index = -1;
134
135         context->ipv4_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
136         context->ipv4_address = NULL;
137         context->ipv4_nameservers = NULL;
138
139         context->ipv6_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
140         context->ipv6_address = NULL;
141         context->ipv6_nameservers = NULL;
142
143         return context;
144 }
145
146 static void network_context_free(struct network_context *context)
147 {
148         g_free(context->path);
149
150         connman_ipaddress_free(context->ipv4_address);
151         g_free(context->ipv4_nameservers);
152
153         connman_ipaddress_free(context->ipv6_address);
154         g_free(context->ipv6_nameservers);
155
156         free(context);
157 }
158
159 typedef void (*set_property_cb)(struct modem_data *data,
160                                 connman_bool_t success);
161 typedef void (*get_properties_cb)(struct modem_data *data,
162                                 DBusMessageIter *dict);
163
164 struct property_info {
165         struct modem_data *modem;
166         const char *path;
167         const char *interface;
168         const char *property;
169         set_property_cb set_property_cb;
170         get_properties_cb get_properties_cb;
171 };
172
173 static void set_property_reply(DBusPendingCall *call, void *user_data)
174 {
175         struct property_info *info = user_data;
176         DBusMessage *reply;
177         DBusError error;
178         connman_bool_t success = TRUE;
179
180         DBG("%s path %s %s.%s", info->modem->path,
181                 info->path, info->interface, info->property);
182
183         info->modem->call_set_property = NULL;
184
185         dbus_error_init(&error);
186
187         reply = dbus_pending_call_steal_reply(call);
188
189         if (dbus_set_error_from_message(&error, reply)) {
190                 connman_error("Failed to change property: %s %s.%s: %s %s",
191                                 info->path, info->interface, info->property,
192                                 error.name, error.message);
193                 dbus_error_free(&error);
194                 success = FALSE;
195         }
196
197         if (info->set_property_cb != NULL)
198                 (*info->set_property_cb)(info->modem, success);
199
200         dbus_message_unref(reply);
201
202         dbus_pending_call_unref(call);
203 }
204
205 static int set_property(struct modem_data *modem,
206                         const char *path, const char *interface,
207                         const char *property, int type, void *value,
208                         set_property_cb notify)
209 {
210         DBusMessage *message;
211         DBusMessageIter iter;
212         struct property_info *info;
213
214         DBG("%s path %s %s.%s", modem->path, path, interface, property);
215
216         if (modem->call_set_property != NULL) {
217                 connman_error("Pending SetProperty");
218                 return -EBUSY;
219         }
220
221         message = dbus_message_new_method_call(OFONO_SERVICE, path,
222                                         interface, SET_PROPERTY);
223         if (message == NULL)
224                 return -ENOMEM;
225
226         dbus_message_iter_init_append(message, &iter);
227         connman_dbus_property_append_basic(&iter, property, type, value);
228
229         if (dbus_connection_send_with_reply(connection, message,
230                         &modem->call_set_property, TIMEOUT) == FALSE) {
231                 connman_error("Failed to change property: %s %s.%s",
232                                 path, interface, property);
233                 dbus_message_unref(message);
234                 return -EINVAL;
235         }
236
237         if (modem->call_set_property == NULL) {
238                 connman_error("D-Bus connection not available");
239                 dbus_message_unref(message);
240                 return -EINVAL;
241         }
242
243         info = g_try_new0(struct property_info, 1);
244         if (info == NULL) {
245                 dbus_message_unref(message);
246                 return -ENOMEM;
247         }
248
249         info->modem = modem;
250         info->path = path;
251         info->interface = interface;
252         info->property = property;
253         info->set_property_cb = notify;
254
255         dbus_pending_call_set_notify(modem->call_set_property,
256                                         set_property_reply, info, g_free);
257
258         dbus_message_unref(message);
259
260         return -EINPROGRESS;
261 }
262
263 static void get_properties_reply(DBusPendingCall *call, void *user_data)
264 {
265         struct property_info *info = user_data;
266         DBusMessageIter array, dict;
267         DBusMessage *reply;
268         DBusError error;
269
270         DBG("%s path %s %s", info->modem->path, info->path, info->interface);
271
272         info->modem->call_get_properties = NULL;
273
274         dbus_error_init(&error);
275
276         reply = dbus_pending_call_steal_reply(call);
277
278         if (dbus_set_error_from_message(&error, reply)) {
279                 connman_error("Failed to get properties: %s %s: %s %s",
280                                 info->path, info->interface,
281                                 error.name, error.message);
282                 dbus_error_free(&error);
283
284                 goto done;
285         }
286
287         if (dbus_message_iter_init(reply, &array) == FALSE)
288                 goto done;
289
290         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
291                 goto done;
292
293         dbus_message_iter_recurse(&array, &dict);
294
295         if (info->get_properties_cb != NULL)
296                 (*info->get_properties_cb)(info->modem, &dict);
297
298 done:
299
300         dbus_message_unref(reply);
301
302         dbus_pending_call_unref(call);
303 }
304
305 static int get_properties(const char *path, const char *interface,
306                                 get_properties_cb notify,
307                                 struct modem_data *modem)
308 {
309         DBusMessage *message;
310         struct property_info *info;
311
312         DBG("%s path %s %s", modem->path, path, interface);
313
314         if (modem->call_get_properties != NULL) {
315                 connman_error("Pending GetProperties");
316                 return -EBUSY;
317         }
318
319         message = dbus_message_new_method_call(OFONO_SERVICE, path,
320                                         interface, GET_PROPERTIES);
321         if (message == NULL)
322                 return -ENOMEM;
323
324         if (dbus_connection_send_with_reply(connection, message,
325                         &modem->call_get_properties, TIMEOUT) == FALSE) {
326                 connman_error("Failed to call %s.GetProperties()", interface);
327                 dbus_message_unref(message);
328                 return -EINVAL;
329         }
330
331         if (modem->call_get_properties == NULL) {
332                 connman_error("D-Bus connection not available");
333                 dbus_message_unref(message);
334                 return -EINVAL;
335         }
336
337         info = g_try_new0(struct property_info, 1);
338         if (info == NULL) {
339                 dbus_message_unref(message);
340                 return -ENOMEM;
341         }
342
343         info->modem = modem;
344         info->path = path;
345         info->interface = interface;
346         info->get_properties_cb = notify;
347
348         dbus_pending_call_set_notify(modem->call_get_properties,
349                                         get_properties_reply, info, g_free);
350
351         dbus_message_unref(message);
352
353         return -EINPROGRESS;
354 }
355
356 static void modem_set_online_reply(struct modem_data *modem,
357                                         connman_bool_t success)
358 {
359         DBG("%s", modem->path);
360
361         if (success == TRUE) {
362                 /*
363                  * Don't handle do anything on success here. oFono will send
364                  * the change via PropertyChanged singal.
365                  */
366                 return;
367         }
368
369         modem->set_online = FALSE;
370 }
371
372 static int modem_set_online(struct modem_data *modem)
373 {
374         DBG("%s", modem->path);
375
376         modem->set_online = TRUE;
377
378         return set_property(modem, modem->path,
379                                 OFONO_MODEM_INTERFACE,
380                                 "Online", DBUS_TYPE_BOOLEAN,
381                                 &modem->set_online,
382                                 modem_set_online_reply);
383 }
384
385 static void cm_set_powered_reply(struct modem_data *modem,
386                                         connman_bool_t success)
387 {
388         DBG("%s", modem->path);
389
390         if (success == TRUE) {
391                 /*
392                  * Don't handle do anything on success here. oFono will send
393                  * the change via PropertyChanged singal.
394                  */
395                 return;
396         }
397
398         modem->set_cm_powered = FALSE;
399 }
400
401 static int cm_set_powered(struct modem_data *modem)
402 {
403         DBG("%s", modem->path);
404
405         modem->set_cm_powered = TRUE;
406
407         return set_property(modem, modem->path,
408                                 OFONO_CM_INTERFACE,
409                                 "Powered", DBUS_TYPE_BOOLEAN,
410                                 &modem->set_cm_powered,
411                                 cm_set_powered_reply);
412 }
413
414 static int modem_set_powered(struct modem_data *modem)
415 {
416         DBG("%s", modem->path);
417
418         modem->set_powered = TRUE;
419
420         return set_property(modem, modem->path,
421                                 OFONO_MODEM_INTERFACE,
422                                 "Powered", DBUS_TYPE_BOOLEAN,
423                                 &modem->set_powered,
424                                 NULL);
425 }
426
427 static connman_bool_t has_interface(uint8_t interfaces,
428                                         enum ofono_api api)
429 {
430         if ((interfaces & api) == api)
431                 return TRUE;
432
433         return FALSE;
434 }
435
436 static uint8_t extract_interfaces(DBusMessageIter *array)
437 {
438         DBusMessageIter entry;
439         uint8_t interfaces = 0;
440
441         dbus_message_iter_recurse(array, &entry);
442
443         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
444                 const char *name;
445
446                 dbus_message_iter_get_basic(&entry, &name);
447
448                 if (g_str_equal(name, OFONO_SIM_INTERFACE) == TRUE)
449                         interfaces |= OFONO_API_SIM;
450                 else if (g_str_equal(name, OFONO_NETREG_INTERFACE) == TRUE)
451                         interfaces |= OFONO_API_NETREG;
452                 else if (g_str_equal(name, OFONO_CM_INTERFACE) == TRUE)
453                         interfaces |= OFONO_API_CM;
454
455                 dbus_message_iter_next(&entry);
456         }
457
458         return interfaces;
459 }
460
461 static connman_bool_t ready_to_create_device(struct modem_data *modem)
462 {
463         /*
464          * There are three different modem types which behave slightly
465          * different:
466          * - GSM modems will expose the SIM interface then the
467          *   CM interface.
468          * - DUN modems will expose first a unique serial number (BDADDR)
469          *   and then the CM interface.
470          * - CDMA modems will expose CM first and sometime later
471          *   a unique serial number.
472          *
473          * This functions tests if we have the necessary information gathered
474          * before we are able to create a device.
475          */
476
477         if (modem->device != NULL)
478                 return FALSE;
479
480         if (modem->imsi != NULL || modem->serial != NULL)
481                 return TRUE;
482
483         return FALSE;
484 }
485
486 static void create_device(struct modem_data *modem)
487 {
488         struct connman_device *device;
489         char *uninitialized_var(ident);
490
491         DBG("%s", modem->path);
492
493         if (modem->imsi != NULL)
494                 ident = modem->imsi;
495         else if (modem->serial != NULL)
496                 ident = modem->serial;
497
498         if (connman_dbus_validate_ident(ident) == FALSE)
499                 ident = connman_dbus_encode_string(ident);
500         else
501                 ident = g_strdup(ident);
502
503         device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_CELLULAR);
504         if (device == NULL)
505                 goto out;
506
507         DBG("device %p", device);
508
509         connman_device_set_ident(device, ident);
510
511         connman_device_set_string(device, "Path", modem->path);
512
513         connman_device_set_data(device, modem);
514
515         if (connman_device_register(device) < 0) {
516                 connman_error("Failed to register cellular device");
517                 connman_device_unref(device);
518                 goto out;
519         }
520
521         modem->device = device;
522
523 out:
524         g_free(ident);
525 }
526
527 static void destroy_device(struct modem_data *modem)
528 {
529         DBG("%s", modem->path);
530
531         connman_device_set_powered(modem->device, FALSE);
532
533         connman_device_unregister(modem->device);
534         connman_device_unref(modem->device);
535
536         modem->device = NULL;
537 }
538
539 static int add_cm_context(struct modem_data *modem, const char *context_path,
540                                 DBusMessageIter *dict)
541 {
542         const char *context_type;
543         struct network_context *context = NULL;
544         connman_bool_t active = FALSE;
545
546         DBG("%s context path %s", modem->path, context_path);
547
548         if (modem->context != NULL) {
549                 /*
550                  * We have already assigned a context to this modem
551                  * and we do only support one Internet context.
552                  */
553                 return -EALREADY;
554         }
555
556         context = network_context_alloc(context_path);
557         if (context == NULL)
558                 return -ENOMEM;
559
560         while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
561                 DBusMessageIter entry, value;
562                 const char *key;
563
564                 dbus_message_iter_recurse(dict, &entry);
565                 dbus_message_iter_get_basic(&entry, &key);
566
567                 dbus_message_iter_next(&entry);
568                 dbus_message_iter_recurse(&entry, &value);
569
570                 if (g_str_equal(key, "Type") == TRUE) {
571                         dbus_message_iter_get_basic(&value, &context_type);
572
573                         DBG("%s context %s type %s", modem->path,
574                                 context_path, context_type);
575                 } else if (g_str_equal(key, "Settings") == TRUE) {
576                         DBG("%s Settings", modem->path);
577                 } else if (g_str_equal(key, "IPv6.Settings") == TRUE) {
578                         DBG("%s IPv6.Settings", modem->path);
579                 } else if (g_str_equal(key, "Active") == TRUE) {
580                         dbus_message_iter_get_basic(&value, &active);
581
582                         DBG("%s Active %d", modem->path, active);
583                 }
584
585                 dbus_message_iter_next(dict);
586         }
587
588         if (g_strcmp0(context_type, "internet") != 0) {
589                 network_context_free(context);
590                 return -EINVAL;
591         }
592
593         modem->context = context;
594         modem->active = active;
595
596         g_hash_table_replace(context_hash, g_strdup(context_path), modem);
597
598         return 0;
599 }
600
601 static void remove_cm_context(struct modem_data *modem,
602                                 const char *context_path)
603 {
604         if (modem->context == NULL)
605                 return;
606
607         g_hash_table_remove(context_hash, context_path);
608
609         network_context_free(modem->context);
610         modem->context = NULL;
611 }
612
613 static gboolean context_changed(DBusConnection *connection,
614                                 DBusMessage *message,
615                                 void *user_data)
616 {
617         const char *context_path = dbus_message_get_path(message);
618         struct modem_data *modem = NULL;
619         DBusMessageIter iter, value;
620         const char *key;
621
622         DBG("context_path %s", context_path);
623
624         modem = g_hash_table_lookup(context_hash, context_path);
625         if (modem == NULL)
626                 return TRUE;
627
628         if (dbus_message_iter_init(message, &iter) == FALSE)
629                 return TRUE;
630
631         dbus_message_iter_get_basic(&iter, &key);
632
633         dbus_message_iter_next(&iter);
634         dbus_message_iter_recurse(&iter, &value);
635
636         /*
637          * oFono guarantees the ordering of Settings and
638          * Active. Settings will always be send before Active = True.
639          * That means we don't have to order here.
640          */
641         if (g_str_equal(key, "Settings") == TRUE) {
642                 DBG("%s Settings", modem->path);
643         } else if (g_str_equal(key, "IPv6.Settings") == TRUE) {
644                 DBG("%s IPv6.Settings", modem->path);
645         } else if (g_str_equal(key, "Active") == TRUE) {
646                 dbus_message_iter_get_basic(&value, &modem->active);
647
648                 DBG("%s Active %d", modem->path, modem->active);
649         }
650
651         return TRUE;
652 }
653
654 static void cm_get_contexts_reply(DBusPendingCall *call, void *user_data)
655 {
656         struct modem_data *modem = user_data;
657         DBusMessageIter array, dict, entry, value;
658         DBusMessage *reply;
659         DBusError error;
660
661         DBG("%s", modem->path);
662
663         modem->call_get_contexts = NULL;
664
665         reply = dbus_pending_call_steal_reply(call);
666
667         dbus_error_init(&error);
668
669         if (dbus_set_error_from_message(&error, reply) == TRUE) {
670                 connman_error("%s", error.message);
671                 dbus_error_free(&error);
672                 goto done;
673         }
674
675         if (dbus_message_iter_init(reply, &array) == FALSE)
676                 goto done;
677
678         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
679                 goto done;
680
681         dbus_message_iter_recurse(&array, &dict);
682
683         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
684                 const char *context_path;
685
686                 dbus_message_iter_recurse(&dict, &entry);
687                 dbus_message_iter_get_basic(&entry, &context_path);
688
689                 dbus_message_iter_next(&entry);
690                 dbus_message_iter_recurse(&entry, &value);
691
692                 if (add_cm_context(modem, context_path, &value) == 0)
693                         break;
694
695                 dbus_message_iter_next(&dict);
696         }
697
698 done:
699         dbus_message_unref(reply);
700
701         dbus_pending_call_unref(call);
702 }
703
704 static int cm_get_contexts(struct modem_data *modem)
705 {
706         DBusMessage *message;
707
708         DBG("%s", modem->path);
709
710         if (modem->call_get_contexts != NULL)
711                 return -EBUSY;
712
713         message = dbus_message_new_method_call(OFONO_SERVICE, modem->path,
714                                         OFONO_CM_INTERFACE, GET_CONTEXTS);
715         if (message == NULL)
716                 return -ENOMEM;
717
718         if (dbus_connection_send_with_reply(connection, message,
719                         &modem->call_get_contexts, TIMEOUT) == FALSE) {
720                 connman_error("Failed to call GetContexts()");
721                 dbus_message_unref(message);
722                 return -EINVAL;
723         }
724
725         if (modem->call_get_contexts == NULL) {
726                 connman_error("D-Bus connection not available");
727                 dbus_message_unref(message);
728                 return -EINVAL;
729         }
730
731         dbus_pending_call_set_notify(modem->call_get_contexts,
732                                         cm_get_contexts_reply,
733                                         modem, NULL);
734
735         dbus_message_unref(message);
736
737         return -EINPROGRESS;
738 }
739
740 static gboolean cm_context_added(DBusConnection *connection,
741                                         DBusMessage *message,
742                                         void *user_data)
743 {
744         const char *path = dbus_message_get_path(message);
745         char *context_path;
746         struct modem_data *modem;
747         DBusMessageIter iter, properties;
748
749         DBG("%s", path);
750
751         modem = g_hash_table_lookup(modem_hash, context_path);
752         if (modem == NULL)
753                 return TRUE;
754
755         if (dbus_message_iter_init(message, &iter) == FALSE)
756                 return TRUE;
757
758         dbus_message_iter_get_basic(&iter, &context_path);
759
760         dbus_message_iter_next(&iter);
761         dbus_message_iter_recurse(&iter, &properties);
762
763         if (add_cm_context(modem, context_path, &properties) != 0)
764                 return TRUE;
765
766         return TRUE;
767 }
768
769 static gboolean cm_context_removed(DBusConnection *connection,
770                                         DBusMessage *message,
771                                         void *user_data)
772 {
773         const char *path = dbus_message_get_path(message);
774         const char *context_path;
775         struct modem_data *modem;
776         DBusMessageIter iter;
777
778         DBG("context path %s", path);
779
780         if (dbus_message_iter_init(message, &iter) == FALSE)
781                 return TRUE;
782
783         dbus_message_iter_get_basic(&iter, &context_path);
784
785         modem = g_hash_table_lookup(context_hash, context_path);
786         if (modem == NULL)
787                 return TRUE;
788
789         remove_cm_context(modem, context_path);
790
791         return TRUE;
792 }
793
794 static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message,
795                                 void *user_data)
796 {
797         return TRUE;
798 }
799
800 static gboolean cm_changed(DBusConnection *connection, DBusMessage *message,
801                                 void *user_data)
802 {
803         const char *path = dbus_message_get_path(message);
804         struct modem_data *modem;
805         DBusMessageIter iter, value;
806         const char *key;
807
808         modem = g_hash_table_lookup(modem_hash, path);
809         if (modem == NULL)
810                 return TRUE;
811
812         if (dbus_message_iter_init(message, &iter) == FALSE)
813                 return TRUE;
814
815         dbus_message_iter_get_basic(&iter, &key);
816
817         dbus_message_iter_next(&iter);
818         dbus_message_iter_recurse(&iter, &value);
819
820         if (g_str_equal(key, "Attached") == TRUE) {
821                 dbus_message_iter_get_basic(&value, &modem->attached);
822
823                 DBG("%s Attached %d", modem->path, modem->attached);
824         } else if (g_str_equal(key, "Powered") == TRUE) {
825                 dbus_message_iter_get_basic(&value, &modem->cm_powered);
826
827                 DBG("%s ConnnectionManager Powered %d", modem->path,
828                         modem->cm_powered);
829
830                 if (modem->cm_powered == FALSE)
831                         cm_set_powered(modem);
832         }
833
834         return TRUE;
835 }
836
837 static void cm_properties_reply(struct modem_data *modem, DBusMessageIter *dict)
838 {
839         DBG("%s", modem->path);
840
841         while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
842                 DBusMessageIter entry, value;
843                 const char *key;
844
845                 dbus_message_iter_recurse(dict, &entry);
846                 dbus_message_iter_get_basic(&entry, &key);
847
848                 dbus_message_iter_next(&entry);
849                 dbus_message_iter_recurse(&entry, &value);
850
851                 if (g_str_equal(key, "Attached") == TRUE) {
852                         dbus_message_iter_get_basic(&value, &modem->attached);
853
854                         DBG("%s Attached %d", modem->path,
855                                 modem->attached);
856                 } else if (g_str_equal(key, "Powered") == TRUE) {
857                         dbus_message_iter_get_basic(&value, &modem->cm_powered);
858
859                         DBG("%s ConnnectionManager Powered %d", modem->path,
860                                 modem->cm_powered);
861
862                         if (modem->cm_powered == FALSE)
863                                 cm_set_powered(modem);
864                 }
865
866                 dbus_message_iter_next(dict);
867         }
868 }
869
870 static int cm_get_properties(struct modem_data *modem)
871 {
872         return get_properties(modem->path, OFONO_CM_INTERFACE,
873                                 cm_properties_reply, modem);
874 }
875
876 static void update_sim_imsi(struct modem_data *modem,
877                                 const char *imsi)
878 {
879         DBG("%s imsi %s", modem->path, imsi);
880
881         if (g_strcmp0(modem->imsi, imsi) == 0)
882                 return;
883
884         g_free(modem->imsi);
885         modem->imsi = g_strdup(imsi);
886 }
887
888 static gboolean sim_changed(DBusConnection *connection, DBusMessage *message,
889                                 void *user_data)
890 {
891         const char *path = dbus_message_get_path(message);
892         struct modem_data *modem;
893         DBusMessageIter iter, value;
894         const char *key;
895
896         modem = g_hash_table_lookup(modem_hash, path);
897         if (modem == NULL)
898                 return TRUE;
899
900         if (dbus_message_iter_init(message, &iter) == FALSE)
901                 return TRUE;
902
903         dbus_message_iter_get_basic(&iter, &key);
904
905         dbus_message_iter_next(&iter);
906         dbus_message_iter_recurse(&iter, &value);
907
908         if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
909                 char *imsi;
910
911                 dbus_message_iter_get_basic(&value, &imsi);
912
913                 update_sim_imsi(modem, imsi);
914
915                 if (modem->online == FALSE) {
916                         modem_set_online(modem);
917                 } else if (has_interface(modem->interfaces,
918                                                 OFONO_API_CM) == TRUE) {
919                         if (ready_to_create_device(modem) == TRUE)
920                                 create_device(modem);
921                 }
922         }
923
924         return TRUE;
925 }
926
927 static void sim_properties_reply(struct modem_data *modem,
928                                         DBusMessageIter *dict)
929 {
930         DBG("%s", modem->path);
931
932         while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
933                 DBusMessageIter entry, value;
934                 const char *key;
935
936                 dbus_message_iter_recurse(dict, &entry);
937                 dbus_message_iter_get_basic(&entry, &key);
938
939                 dbus_message_iter_next(&entry);
940                 dbus_message_iter_recurse(&entry, &value);
941
942                 if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
943                         char *imsi;
944
945                         dbus_message_iter_get_basic(&value, &imsi);
946
947                         update_sim_imsi(modem, imsi);
948
949                         if (modem->online == FALSE) {
950                                 modem_set_online(modem);
951                                 break;
952                         }
953
954                         if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) {
955                                 if (ready_to_create_device(modem) == TRUE)
956                                         create_device(modem);
957                                 if (modem->device != NULL) {
958                                         cm_get_properties(modem);
959                                         cm_get_contexts(modem);
960                                 }
961                         }
962                         return;
963                 }
964
965                 dbus_message_iter_next(dict);
966         }
967 }
968
969 static int sim_get_properties(struct modem_data *modem)
970 {
971         return get_properties(modem->path, OFONO_SIM_INTERFACE,
972                         sim_properties_reply, modem);
973 }
974
975 static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
976                                 void *user_data)
977 {
978         const char *path = dbus_message_get_path(message);
979         struct modem_data *modem;
980         DBusMessageIter iter, value;
981         const char *key;
982
983         modem = g_hash_table_lookup(modem_hash, path);
984         if (modem == NULL)
985                 return TRUE;
986
987         if (dbus_message_iter_init(message, &iter) == FALSE)
988                 return TRUE;
989
990         dbus_message_iter_get_basic(&iter, &key);
991
992         dbus_message_iter_next(&iter);
993         dbus_message_iter_recurse(&iter, &value);
994
995         if (g_str_equal(key, "Powered") == TRUE) {
996                 dbus_message_iter_get_basic(&value, &modem->powered);
997
998                 DBG("%s Powered %d", modem->path, modem->powered);
999
1000                 if (modem->powered == FALSE)
1001                         modem_set_powered(modem);
1002         } else if (g_str_equal(key, "Online") == TRUE) {
1003                 dbus_message_iter_get_basic(&value, &modem->online);
1004
1005                 DBG("%s Online %d", modem->path, modem->online);
1006
1007                 if (modem->online == FALSE)
1008                         return TRUE;
1009
1010                 if (has_interface(modem->interfaces, OFONO_API_CM) == FALSE)
1011                         return TRUE;
1012                 if (ready_to_create_device(modem) == TRUE)
1013                         create_device(modem);
1014                 if (modem->device != NULL) {
1015                         cm_get_properties(modem);
1016                         cm_get_contexts(modem);
1017                 }
1018         } else if (g_str_equal(key, "Interfaces") == TRUE) {
1019                 modem->interfaces = extract_interfaces(&value);
1020
1021                 DBG("%s Interfaces 0x%02x", modem->path,
1022                         modem->interfaces);
1023
1024                 if (has_interface(modem->interfaces, OFONO_API_SIM) == TRUE) {
1025                         if (modem->imsi == NULL &&
1026                                         modem->set_powered == FALSE) {
1027                                 /*
1028                                  * Only use do GetProperties() when
1029                                  * device has not been powered up.
1030                                  */
1031                                 sim_get_properties(modem);
1032                                 return TRUE;
1033                         }
1034                 }
1035
1036                 if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) {
1037                         if (ready_to_create_device(modem) == TRUE)
1038                                 create_device(modem);
1039                         if (modem->device != NULL) {
1040                                 cm_get_properties(modem);
1041                                 cm_get_contexts(modem);
1042                         }
1043                 } else {
1044                         if (modem->context != NULL) {
1045                                 remove_cm_context(modem,
1046                                                 modem->context->path);
1047                         }
1048
1049                         if (modem->device != NULL)
1050                                 destroy_device(modem);
1051                 }
1052         } else if (g_str_equal(key, "Serial") == TRUE) {
1053                 char *serial;
1054
1055                 dbus_message_iter_get_basic(&value, &serial);
1056
1057                 g_free(modem->serial);
1058                 modem->serial = g_strdup(serial);
1059
1060                 DBG("%s Serial %s", modem->path, modem->serial);
1061
1062                 if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) {
1063                         if (ready_to_create_device(modem) == TRUE)
1064                                 create_device(modem);
1065                         if (modem->device != NULL) {
1066                                 cm_get_properties(modem);
1067                                 cm_get_contexts(modem);
1068                         }
1069                 }
1070         }
1071
1072         return TRUE;
1073 }
1074
1075 static void add_modem(const char *path, DBusMessageIter *prop)
1076 {
1077         struct modem_data *modem;
1078
1079         DBG("%s", path);
1080
1081         modem = g_hash_table_lookup(modem_hash, path);
1082         if (modem != NULL) {
1083                 /*
1084                  * When oFono powers up we ask for the modems and oFono is
1085                  * reporting with modem_added signal the modems. Only
1086                  * handle them once.
1087                  */
1088                 return;
1089         }
1090
1091         modem = g_try_new0(struct modem_data, 1);
1092         if (modem == NULL)
1093                 return;
1094
1095         modem->path = g_strdup(path);
1096
1097         g_hash_table_insert(modem_hash, g_strdup(path), modem);
1098
1099         while (dbus_message_iter_get_arg_type(prop) == DBUS_TYPE_DICT_ENTRY) {
1100                 DBusMessageIter entry, value;
1101                 const char *key;
1102
1103                 dbus_message_iter_recurse(prop, &entry);
1104                 dbus_message_iter_get_basic(&entry, &key);
1105
1106                 dbus_message_iter_next(&entry);
1107                 dbus_message_iter_recurse(&entry, &value);
1108
1109                 if (g_str_equal(key, "Powered") == TRUE) {
1110                         dbus_message_iter_get_basic(&value, &modem->powered);
1111
1112                         DBG("%s Powered %d", modem->path, modem->powered);
1113                 } else if (g_str_equal(key, "Online") == TRUE) {
1114                         dbus_message_iter_get_basic(&value, &modem->online);
1115
1116                         DBG("%s Online %d", modem->path, modem->online);
1117                 } else if (g_str_equal(key, "Interfaces") == TRUE) {
1118                         modem->interfaces = extract_interfaces(&value);
1119
1120                         DBG("%s Interfaces 0x%02x", modem->path,
1121                                 modem->interfaces);
1122                 } else if (g_str_equal(key, "Serial") == TRUE) {
1123                         char *serial;
1124
1125                         dbus_message_iter_get_basic(&value, &serial);
1126                         modem->serial = g_strdup(serial);
1127
1128                         DBG("%s Serial %s", modem->path, modem->serial);
1129                 }
1130
1131                 dbus_message_iter_next(prop);
1132         }
1133
1134         if (modem->powered == FALSE) {
1135                 modem_set_powered(modem);
1136         } else if (has_interface(modem->interfaces, OFONO_API_SIM) == TRUE) {
1137                 sim_get_properties(modem);
1138         } else if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) {
1139                 if (ready_to_create_device(modem) == TRUE)
1140                         create_device(modem);
1141                 if (modem->device != NULL) {
1142                         cm_get_properties(modem);
1143                         cm_get_contexts(modem);
1144                 }
1145         }
1146 }
1147
1148 static void remove_modem(gpointer data)
1149 {
1150         struct modem_data *modem = data;
1151
1152         DBG("%s", modem->path);
1153
1154         if (modem->call_set_property != NULL)
1155                 dbus_pending_call_cancel(modem->call_set_property);
1156
1157         if (modem->call_get_properties != NULL)
1158                 dbus_pending_call_cancel(modem->call_get_properties);
1159
1160         if (modem->call_get_contexts != NULL)
1161                 dbus_pending_call_cancel(modem->call_get_contexts);
1162
1163         if (modem->device != NULL)
1164                 destroy_device(modem);
1165
1166         if (modem->context != NULL)
1167                 remove_cm_context(modem, modem->context->path);
1168
1169         g_free(modem->serial);
1170         g_free(modem->imsi);
1171         g_free(modem->path);
1172
1173         g_free(modem);
1174 }
1175
1176 static gboolean modem_added(DBusConnection *connection,
1177                                 DBusMessage *message, void *user_data)
1178 {
1179         DBusMessageIter iter, properties;
1180         const char *path;
1181
1182         DBG("");
1183
1184         if (dbus_message_iter_init(message, &iter) == FALSE)
1185                 return TRUE;
1186
1187         dbus_message_iter_get_basic(&iter, &path);
1188
1189         dbus_message_iter_next(&iter);
1190         dbus_message_iter_recurse(&iter, &properties);
1191
1192         add_modem(path, &properties);
1193
1194         return TRUE;
1195 }
1196
1197 static gboolean modem_removed(DBusConnection *connection,
1198                                 DBusMessage *message, void *user_data)
1199 {
1200         DBusMessageIter iter;
1201         const char *path;
1202
1203         DBG("");
1204
1205         if (dbus_message_iter_init(message, &iter) == FALSE)
1206                 return TRUE;
1207
1208         dbus_message_iter_get_basic(&iter, &path);
1209
1210         g_hash_table_remove(modem_hash, path);
1211
1212         return TRUE;
1213 }
1214
1215 static void manager_get_modems_reply(DBusPendingCall *call, void *user_data)
1216 {
1217         DBusMessage *reply;
1218         DBusError error;
1219         DBusMessageIter array, dict;
1220
1221         DBG("");
1222
1223         reply = dbus_pending_call_steal_reply(call);
1224
1225         dbus_error_init(&error);
1226
1227         if (dbus_set_error_from_message(&error, reply) == TRUE) {
1228                 connman_error("%s", error.message);
1229                 dbus_error_free(&error);
1230                 goto done;
1231         }
1232
1233         if (dbus_message_iter_init(reply, &array) == FALSE)
1234                 goto done;
1235
1236         dbus_message_iter_recurse(&array, &dict);
1237
1238         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
1239                 DBusMessageIter value, properties;
1240                 const char *path;
1241
1242                 dbus_message_iter_recurse(&dict, &value);
1243                 dbus_message_iter_get_basic(&value, &path);
1244
1245                 dbus_message_iter_next(&value);
1246                 dbus_message_iter_recurse(&value, &properties);
1247
1248                 add_modem(path, &properties);
1249
1250                 dbus_message_iter_next(&dict);
1251         }
1252
1253 done:
1254         dbus_message_unref(reply);
1255
1256         dbus_pending_call_unref(call);
1257 }
1258
1259 static int manager_get_modems(void)
1260 {
1261         DBusMessage *message;
1262         DBusPendingCall *call;
1263
1264         DBG("");
1265
1266         message = dbus_message_new_method_call(OFONO_SERVICE, "/",
1267                                         OFONO_MANAGER_INTERFACE, GET_MODEMS);
1268         if (message == NULL)
1269                 return -ENOMEM;
1270
1271         if (dbus_connection_send_with_reply(connection, message,
1272                                                &call, TIMEOUT) == FALSE) {
1273                 connman_error("Failed to call GetModems()");
1274                 dbus_message_unref(message);
1275                 return -EINVAL;
1276         }
1277
1278         if (call == NULL) {
1279                 connman_error("D-Bus connection not available");
1280                 dbus_message_unref(message);
1281                 return -EINVAL;
1282         }
1283
1284         dbus_pending_call_set_notify(call, manager_get_modems_reply,
1285                                         NULL, NULL);
1286
1287         dbus_message_unref(message);
1288
1289         return -EINPROGRESS;
1290 }
1291
1292 static void ofono_connect(DBusConnection *conn, void *user_data)
1293 {
1294         DBG("");
1295
1296         modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1297                                                 g_free, remove_modem);
1298         if (modem_hash == NULL)
1299                 return;
1300
1301         context_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1302                                                 g_free, NULL);
1303         if (context_hash == NULL) {
1304                 g_hash_table_destroy(modem_hash);
1305                 return;
1306         }
1307
1308         manager_get_modems();
1309 }
1310
1311 static void ofono_disconnect(DBusConnection *conn, void *user_data)
1312 {
1313         DBG("");
1314
1315         if (modem_hash == NULL || context_hash == NULL)
1316                 return;
1317
1318         g_hash_table_destroy(modem_hash);
1319         modem_hash = NULL;
1320
1321         g_hash_table_destroy(context_hash);
1322         context_hash = NULL;
1323 }
1324
1325 static int network_probe(struct connman_network *network)
1326 {
1327         DBG("network %p", network);
1328
1329         return 0;
1330 }
1331
1332 static void network_remove(struct connman_network *network)
1333 {
1334         DBG("network %p", network);
1335 }
1336
1337 static int network_connect(struct connman_network *network)
1338 {
1339         DBG("network %p", network);
1340
1341         return 0;
1342 }
1343
1344 static int network_disconnect(struct connman_network *network)
1345 {
1346         DBG("network %p", network);
1347
1348         return 0;
1349 }
1350
1351 static struct connman_network_driver network_driver = {
1352         .name           = "network",
1353         .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
1354         .probe          = network_probe,
1355         .remove         = network_remove,
1356         .connect        = network_connect,
1357         .disconnect     = network_disconnect,
1358 };
1359
1360 static int modem_probe(struct connman_device *device)
1361 {
1362         struct modem_data *modem = connman_device_get_data(device);
1363
1364         DBG("%s device %p", modem->path, device);
1365
1366         return 0;
1367 }
1368
1369 static void modem_remove(struct connman_device *device)
1370 {
1371         struct modem_data *modem = connman_device_get_data(device);
1372
1373         DBG("%s device %p", modem->path, device);
1374 }
1375
1376 static int modem_enable(struct connman_device *device)
1377 {
1378         struct modem_data *modem = connman_device_get_data(device);
1379
1380         DBG("%s device %p", modem->path, device);
1381
1382         return 0;
1383 }
1384
1385 static int modem_disable(struct connman_device *device)
1386 {
1387         struct modem_data *modem = connman_device_get_data(device);
1388
1389         DBG("%s device %p", modem->path, device);
1390
1391         return 0;
1392 }
1393
1394 static struct connman_device_driver modem_driver = {
1395         .name           = "modem",
1396         .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
1397         .probe          = modem_probe,
1398         .remove         = modem_remove,
1399         .enable         = modem_enable,
1400         .disable        = modem_disable,
1401 };
1402
1403 static guint watch;
1404 static guint modem_added_watch;
1405 static guint modem_removed_watch;
1406 static guint modem_watch;
1407 static guint cm_watch;
1408 static guint sim_watch;
1409 static guint context_added_watch;
1410 static guint context_removed_watch;
1411 static guint netreg_watch;
1412 static guint context_watch;
1413
1414 static int ofono_init(void)
1415 {
1416         int err;
1417
1418         DBG("");
1419
1420         connection = connman_dbus_get_connection();
1421         if (connection == NULL)
1422                 return -EIO;
1423
1424         watch = g_dbus_add_service_watch(connection,
1425                                         OFONO_SERVICE, ofono_connect,
1426                                         ofono_disconnect, NULL, NULL);
1427
1428         modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1429                                                 OFONO_MANAGER_INTERFACE,
1430                                                 MODEM_ADDED,
1431                                                 modem_added,
1432                                                 NULL, NULL);
1433
1434         modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1435                                                 OFONO_MANAGER_INTERFACE,
1436                                                 MODEM_REMOVED,
1437                                                 modem_removed,
1438                                                 NULL, NULL);
1439
1440         modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1441                                                 OFONO_MODEM_INTERFACE,
1442                                                 PROPERTY_CHANGED,
1443                                                 modem_changed,
1444                                                 NULL, NULL);
1445
1446         cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1447                                                 OFONO_CM_INTERFACE,
1448                                                 PROPERTY_CHANGED,
1449                                                 cm_changed,
1450                                                 NULL, NULL);
1451
1452         sim_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1453                                                 OFONO_SIM_INTERFACE,
1454                                                 PROPERTY_CHANGED,
1455                                                 sim_changed,
1456                                                 NULL, NULL);
1457
1458         context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1459                                                 OFONO_CM_INTERFACE,
1460                                                 CONTEXT_ADDED,
1461                                                 cm_context_added,
1462                                                 NULL, NULL);
1463
1464         context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1465                                                 OFONO_CM_INTERFACE,
1466                                                 CONTEXT_REMOVED,
1467                                                 cm_context_removed,
1468                                                 NULL, NULL);
1469
1470         context_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1471                                                 OFONO_CONTEXT_INTERFACE,
1472                                                 PROPERTY_CHANGED,
1473                                                 context_changed,
1474                                                 NULL, NULL);
1475
1476         netreg_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
1477                                                 OFONO_NETREG_INTERFACE,
1478                                                 PROPERTY_CHANGED,
1479                                                 netreg_changed,
1480                                                 NULL, NULL);
1481
1482
1483         if (watch == 0 || modem_added_watch == 0 || modem_removed_watch == 0 ||
1484                         modem_watch == 0 || cm_watch == 0 || sim_watch == 0 ||
1485                         context_added_watch == 0 ||
1486                         context_removed_watch == 0 ||
1487                         context_watch == 0 || netreg_watch == 0) {
1488                 err = -EIO;
1489                 goto remove;
1490         }
1491
1492         err = connman_network_driver_register(&network_driver);
1493         if (err < 0)
1494                 goto remove;
1495
1496         err = connman_device_driver_register(&modem_driver);
1497         if (err < 0) {
1498                 connman_network_driver_unregister(&network_driver);
1499                 goto remove;
1500         }
1501
1502         return 0;
1503
1504 remove:
1505         g_dbus_remove_watch(connection, netreg_watch);
1506         g_dbus_remove_watch(connection, context_watch);
1507         g_dbus_remove_watch(connection, context_removed_watch);
1508         g_dbus_remove_watch(connection, context_added_watch);
1509         g_dbus_remove_watch(connection, sim_watch);
1510         g_dbus_remove_watch(connection, cm_watch);
1511         g_dbus_remove_watch(connection, modem_watch);
1512         g_dbus_remove_watch(connection, modem_removed_watch);
1513         g_dbus_remove_watch(connection, modem_added_watch);
1514         g_dbus_remove_watch(connection, watch);
1515         dbus_connection_unref(connection);
1516
1517         return err;
1518 }
1519
1520 static void ofono_exit(void)
1521 {
1522         DBG("");
1523
1524         if (modem_hash != NULL) {
1525                 g_hash_table_destroy(modem_hash);
1526                 modem_hash = NULL;
1527         }
1528
1529         if (context_hash != NULL) {
1530                 g_hash_table_destroy(context_hash);
1531                 context_hash = NULL;
1532         }
1533
1534         connman_device_driver_unregister(&modem_driver);
1535         connman_network_driver_unregister(&network_driver);
1536
1537         g_dbus_remove_watch(connection, netreg_watch);
1538         g_dbus_remove_watch(connection, context_watch);
1539         g_dbus_remove_watch(connection, context_removed_watch);
1540         g_dbus_remove_watch(connection, context_added_watch);
1541         g_dbus_remove_watch(connection, sim_watch);
1542         g_dbus_remove_watch(connection, cm_watch);
1543         g_dbus_remove_watch(connection, modem_watch);
1544         g_dbus_remove_watch(connection, modem_added_watch);
1545         g_dbus_remove_watch(connection, modem_removed_watch);
1546         g_dbus_remove_watch(connection, watch);
1547
1548         dbus_connection_unref(connection);
1549 }
1550
1551 CONNMAN_PLUGIN_DEFINE(ofono, "oFono telephony plugin", VERSION,
1552                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ofono_init, ofono_exit)