Upgrade ofono to 1.11 and merge to 2.0alpha
[profile/ivi/ofono.git] / src / cdma-connman.c
1 /*
2  *
3  *  oFono - Open Source Telephony
4  *
5  *  Copyright (C) 2010-2011  Nokia Corporation and/or its subsidiary(-ies).
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
33 #include <net/if.h>
34
35 #include <glib.h>
36 #include <gdbus.h>
37
38 #include "ofono.h"
39 #include "common.h"
40
41 static GSList *g_drivers;
42
43 struct cdma_connman_settings {
44         char *interface;
45         gboolean static_ip;
46         char *ip;
47         char *netmask;
48         char *gateway;
49         char **dns;
50 };
51
52 struct ofono_cdma_connman {
53         ofono_bool_t powered;
54         ofono_bool_t dormant;
55         struct cdma_connman_settings *settings;
56         DBusMessage *pending;
57         const struct ofono_cdma_connman_driver *driver;
58         void *driver_data;
59         struct ofono_atom *atom;
60         char username[OFONO_CDMA_CONNMAN_MAX_USERNAME_LENGTH + 1];
61         char password[OFONO_CDMA_CONNMAN_MAX_PASSWORD_LENGTH + 1];
62 };
63
64 static void cdma_connman_settings_free(struct cdma_connman_settings *settings)
65 {
66         DBG("");
67
68         g_free(settings->interface);
69         g_free(settings->ip);
70         g_free(settings->netmask);
71         g_free(settings->gateway);
72         g_strfreev(settings->dns);
73
74         g_free(settings);
75 }
76
77 static void cdma_connman_ifupdown(const char *interface, ofono_bool_t active)
78 {
79         struct ifreq ifr;
80         int sk;
81
82         DBG("");
83
84         if (interface == NULL)
85                 return;
86
87         sk = socket(PF_INET, SOCK_DGRAM, 0);
88         if (sk < 0)
89                 return;
90
91         memset(&ifr, 0, sizeof(ifr));
92         strncpy(ifr.ifr_name, interface, IFNAMSIZ);
93
94         if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0)
95                 goto done;
96
97         if (active == TRUE) {
98                 if (ifr.ifr_flags & IFF_UP)
99                         goto done;
100                 ifr.ifr_flags |= IFF_UP;
101         } else {
102                 if (!(ifr.ifr_flags & IFF_UP))
103                         goto done;
104                 ifr.ifr_flags &= ~IFF_UP;
105         }
106
107         if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0)
108                 ofono_error("Failed to change interface flags");
109
110 done:
111         close(sk);
112 }
113
114 static void cdma_connman_settings_append_variant(
115                                         struct cdma_connman_settings *settings,
116                                         DBusMessageIter *iter)
117 {
118         DBusMessageIter variant;
119         DBusMessageIter array;
120         char typesig[5];
121         char arraysig[6];
122         const char *method;
123
124         DBG("");
125
126         arraysig[0] = DBUS_TYPE_ARRAY;
127         arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR;
128         arraysig[2] = typesig[1] = DBUS_TYPE_STRING;
129         arraysig[3] = typesig[2] = DBUS_TYPE_VARIANT;
130         arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR;
131         arraysig[5] = typesig[4] = '\0';
132
133         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
134                                                 arraysig, &variant);
135
136         dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
137                                                 typesig, &array);
138
139         if (settings == NULL)
140                 goto done;
141
142         ofono_dbus_dict_append(&array, "Interface",
143                                 DBUS_TYPE_STRING, &settings->interface);
144
145         if (settings->static_ip == TRUE)
146                 method = "static";
147         else
148                 method = "dhcp";
149
150         ofono_dbus_dict_append(&array, "Method", DBUS_TYPE_STRING, &method);
151
152         if (settings->ip)
153                 ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING,
154                                         &settings->ip);
155
156         if (settings->netmask)
157                 ofono_dbus_dict_append(&array, "Netmask", DBUS_TYPE_STRING,
158                                         &settings->netmask);
159
160         if (settings->gateway)
161                 ofono_dbus_dict_append(&array, "Gateway", DBUS_TYPE_STRING,
162                                         &settings->gateway);
163
164         if (settings->dns)
165                 ofono_dbus_dict_append_array(&array, "DomainNameServers",
166                                                 DBUS_TYPE_STRING,
167                                                 &settings->dns);
168
169 done:
170         dbus_message_iter_close_container(&variant, &array);
171
172         dbus_message_iter_close_container(iter, &variant);
173 }
174
175 static void cdma_connman_settings_signal(struct ofono_cdma_connman *cm)
176 {
177         DBusConnection *conn = ofono_dbus_get_connection();
178         const char *path;
179         DBusMessage *signal;
180         DBusMessageIter iter;
181         const char *prop = "Settings";
182
183         DBG("");
184
185         path = __ofono_atom_get_path(cm->atom);
186
187         signal = dbus_message_new_signal(path,
188                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
189                                 "PropertyChanged");
190         if (signal == NULL)
191                 return;
192
193         dbus_message_iter_init_append(signal, &iter);
194
195         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &prop);
196
197         cdma_connman_settings_append_variant(cm->settings, &iter);
198
199         g_dbus_send_message(conn, signal);
200 }
201
202 static void cdma_connman_settings_update(struct ofono_cdma_connman *cm,
203                                         const char *interface,
204                                         ofono_bool_t static_ip,
205                                         const char *ip, const char *netmask,
206                                         const char *gateway, const char **dns)
207 {
208         DBG("");
209
210         if (cm->settings)
211                 cdma_connman_settings_free(cm->settings);
212
213         cm->settings = g_try_new0(struct cdma_connman_settings, 1);
214         if (cm->settings == NULL)
215                 return;
216
217         cm->settings->interface = g_strdup(interface);
218         cm->settings->static_ip = static_ip;
219         cm->settings->ip = g_strdup(ip);
220         cm->settings->netmask = g_strdup(netmask);
221         cm->settings->gateway = g_strdup(gateway);
222         cm->settings->dns = g_strdupv((char **)dns);
223
224         cdma_connman_ifupdown(interface, TRUE);
225
226         cdma_connman_settings_signal(cm);
227 }
228
229 static void cdma_connman_settings_reset(struct ofono_cdma_connman *cm)
230 {
231         char *interface;
232
233         DBG("");
234
235         if (cm->settings == NULL)
236                 return;
237
238         interface = cm->settings->interface;
239         cm->settings->interface = NULL;
240
241         cdma_connman_settings_free(cm->settings);
242         cm->settings = NULL;
243
244         cdma_connman_settings_signal(cm);
245
246         cdma_connman_ifupdown(interface, FALSE);
247
248         g_free(interface);
249 }
250
251 static void activate_callback(const struct ofono_error *error,
252                                 const char *interface,
253                                 ofono_bool_t static_ip,
254                                 const char *ip, const char *netmask,
255                                 const char *gateway, const char **dns,
256                                 void *data)
257 {
258         DBusConnection *conn = ofono_dbus_get_connection();
259         struct ofono_cdma_connman *cm = data;
260         dbus_bool_t value;
261         const char *path;
262
263         DBG("%p %s", cm, interface);
264
265         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
266                 DBG("Activating packet data service failed with error: %s",
267                                 telephony_error_to_str(error));
268                 __ofono_dbus_pending_reply(&cm->pending,
269                                         __ofono_error_failed(cm->pending));
270                 return;
271         }
272
273         cm->powered = TRUE;
274         __ofono_dbus_pending_reply(&cm->pending,
275                                 dbus_message_new_method_return(cm->pending));
276
277         /*
278          * If we don't have the interface, don't bother emitting any settings,
279          * as nobody can make use of them
280          */
281         if (interface != NULL)
282                 cdma_connman_settings_update(cm, interface, static_ip,
283                                                 ip, netmask, gateway, dns);
284
285         path = __ofono_atom_get_path(cm->atom);
286         value = cm->powered;
287         ofono_dbus_signal_property_changed(conn, path,
288                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
289                                 "Powered", DBUS_TYPE_BOOLEAN, &value);
290 }
291
292 static void deactivate_callback(const struct ofono_error *error, void *data)
293 {
294         DBusConnection *conn = ofono_dbus_get_connection();
295         struct ofono_cdma_connman *cm = data;
296         dbus_bool_t value;
297         const char *path;
298
299         DBG("");
300
301         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
302                 DBG("Deactivating packet data service failed with error: %s",
303                                 telephony_error_to_str(error));
304                 __ofono_dbus_pending_reply(&cm->pending,
305                                         __ofono_error_failed(cm->pending));
306                 return;
307         }
308
309         cm->powered = FALSE;
310         __ofono_dbus_pending_reply(&cm->pending,
311                                 dbus_message_new_method_return(cm->pending));
312
313         cdma_connman_settings_reset(cm);
314
315         path = __ofono_atom_get_path(cm->atom);
316         value = cm->powered;
317         ofono_dbus_signal_property_changed(conn, path,
318                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
319                                 "Powered", DBUS_TYPE_BOOLEAN, &value);
320 }
321
322 static void cdma_connman_settings_append_properties(
323                                                 struct ofono_cdma_connman *cm,
324                                                 DBusMessageIter *dict)
325 {
326         DBusMessageIter entry;
327         const char *key = "Settings";
328
329         DBG("");
330
331         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
332                                                 NULL, &entry);
333
334         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
335
336         cdma_connman_settings_append_variant(cm->settings, &entry);
337
338         dbus_message_iter_close_container(dict, &entry);
339 }
340
341 static ofono_bool_t network_registered(struct ofono_cdma_connman *cm)
342 {
343         int status;
344         struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom);
345         struct ofono_cdma_netreg *cdma_netreg;
346
347         cdma_netreg = __ofono_atom_find(OFONO_ATOM_TYPE_CDMA_NETREG, modem);
348         if (cdma_netreg == NULL)
349                 return FALSE;
350
351         status = ofono_cdma_netreg_get_status(cdma_netreg);
352
353         switch (status) {
354         case NETWORK_REGISTRATION_STATUS_REGISTERED:
355         case NETWORK_REGISTRATION_STATUS_ROAMING:
356                 return TRUE;
357         default:
358                 break;
359         }
360
361         return FALSE;
362 }
363
364 static DBusMessage *cdma_connman_get_properties(DBusConnection *conn,
365                                                 DBusMessage *msg, void *data)
366 {
367         struct ofono_cdma_connman *cm = data;
368         DBusMessage *reply;
369         DBusMessageIter iter;
370         DBusMessageIter dict;
371         dbus_bool_t value;
372
373         DBG("");
374
375         reply = dbus_message_new_method_return(msg);
376         if (reply == NULL)
377                 return NULL;
378
379         dbus_message_iter_init_append(reply, &iter);
380
381         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
382                                         OFONO_PROPERTIES_ARRAY_SIGNATURE,
383                                         &dict);
384
385         value = cm->powered;
386         ofono_dbus_dict_append(&dict, "Powered", DBUS_TYPE_BOOLEAN, &value);
387
388         value = cm->dormant;
389         ofono_dbus_dict_append(&dict, "Dormant", DBUS_TYPE_BOOLEAN, &value);
390
391         if (cm->settings)
392                 cdma_connman_settings_append_properties(cm, &dict);
393
394         dbus_message_iter_close_container(&iter, &dict);
395
396         return reply;
397 }
398
399 static DBusMessage *cdma_connman_set_username(struct ofono_cdma_connman *cm,
400                                         DBusConnection *conn, DBusMessage *msg,
401                                         const char *username)
402 {
403         const char *path;
404
405         if (strlen(username) > OFONO_CDMA_CONNMAN_MAX_USERNAME_LENGTH)
406                 return __ofono_error_invalid_format(msg);
407
408         if (g_str_equal(username, cm->username))
409                 return dbus_message_new_method_return(msg);
410
411         strcpy(cm->username, username);
412
413         g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
414
415         path = __ofono_atom_get_path(cm->atom);
416         ofono_dbus_signal_property_changed(conn, path,
417                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
418                                 "Username", DBUS_TYPE_STRING, &username);
419
420         return NULL;
421 }
422
423 static DBusMessage *cdma_connman_set_password(struct ofono_cdma_connman *cm,
424                                         DBusConnection *conn, DBusMessage *msg,
425                                         const char *password)
426 {
427         const char *path;
428
429         if (strlen(password) > OFONO_CDMA_CONNMAN_MAX_PASSWORD_LENGTH)
430                 return __ofono_error_invalid_format(msg);
431
432         if (g_str_equal(password, cm->password))
433                 return dbus_message_new_method_return(msg);
434
435         strcpy(cm->password, password);
436
437         g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
438
439         path = __ofono_atom_get_path(cm->atom);
440         ofono_dbus_signal_property_changed(conn, path,
441                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
442                                 "Password", DBUS_TYPE_STRING, &password);
443
444         return NULL;
445 }
446
447 static DBusMessage *cdma_connman_set_property(DBusConnection *conn,
448                                         DBusMessage *msg, void *data)
449 {
450         struct ofono_cdma_connman *cm = data;
451         DBusMessageIter iter;
452         DBusMessageIter var;
453         const char *property;
454         dbus_bool_t value;
455         const char *str;
456
457         DBG("");
458
459         if (cm->pending)
460                 return __ofono_error_busy(msg);
461
462         if (!dbus_message_iter_init(msg, &iter))
463                 return __ofono_error_invalid_args(msg);
464
465         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
466                 return __ofono_error_invalid_args(msg);
467
468         dbus_message_iter_get_basic(&iter, &property);
469         dbus_message_iter_next(&iter);
470
471         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
472                 return __ofono_error_invalid_args(msg);
473
474         dbus_message_iter_recurse(&iter, &var);
475
476         if (!strcmp(property, "Powered")) {
477                 if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
478                         return __ofono_error_invalid_args(msg);
479
480                 dbus_message_iter_get_basic(&var, &value);
481
482                 if (cm->powered == (ofono_bool_t) value)
483                         return dbus_message_new_method_return(msg);
484
485                 if (cm->driver == NULL || cm->driver->activate == NULL ||
486                                 cm->driver->deactivate == NULL)
487                         return __ofono_error_not_implemented(msg);
488
489                 if (network_registered(cm) == FALSE)
490                         return __ofono_error_not_registered(msg);
491
492                 cm->pending = dbus_message_ref(msg);
493
494                 if (value)
495                         cm->driver->activate(cm, cm->username, cm->password,
496                                                 activate_callback, cm);
497                 else
498                         cm->driver->deactivate(cm, deactivate_callback, cm);
499
500                 return NULL;
501         } else if (!strcmp(property, "Username")) {
502                 if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
503                         return __ofono_error_invalid_args(msg);
504
505                 dbus_message_iter_get_basic(&var, &str);
506                 return cdma_connman_set_username(cm, conn, msg, str);
507         } else if (!strcmp(property, "Password")) {
508                 if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
509                         return __ofono_error_invalid_args(msg);
510
511                 dbus_message_iter_get_basic(&var, &str);
512                 return cdma_connman_set_password(cm, conn, msg, str);
513         }
514
515         /* TODO: Dormant property. Not yet supported. */
516
517         return __ofono_error_invalid_args(msg);
518 }
519
520 static const GDBusMethodTable cdma_connman_methods[] = {
521         { GDBUS_METHOD("GetProperties",
522                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
523                         cdma_connman_get_properties) },
524         { GDBUS_ASYNC_METHOD("SetProperty",
525                         GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
526                         NULL, cdma_connman_set_property) },
527         { }
528 };
529
530 static const GDBusSignalTable cdma_connman_signals[] = {
531         { GDBUS_SIGNAL("PropertyChanged",
532                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
533         { }
534 };
535
536 int ofono_cdma_connman_driver_register(
537                                 const struct ofono_cdma_connman_driver *d)
538 {
539         DBG("driver: %p, name: %s", d, d->name);
540
541         if (d->probe == NULL)
542                 return -EINVAL;
543
544         g_drivers = g_slist_prepend(g_drivers, (void *) d);
545
546         return 0;
547 }
548
549 void ofono_cdma_connman_driver_unregister(
550                                 const struct ofono_cdma_connman_driver *d)
551 {
552         DBG("driver: %p, name: %s", d, d->name);
553
554         g_drivers = g_slist_remove(g_drivers, (void *) d);
555 }
556
557 void ofono_cdma_connman_deactivated(struct ofono_cdma_connman *cm)
558 {
559         DBusConnection *conn = ofono_dbus_get_connection();
560         ofono_bool_t value;
561         const char *path;
562
563         if (cm == NULL)
564                 return;
565
566         cdma_connman_settings_reset(cm);
567         cm->powered = FALSE;
568         value = cm->powered;
569         path = __ofono_atom_get_path(cm->atom);
570
571         ofono_dbus_signal_property_changed(conn, path,
572                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
573                                 "Powered", DBUS_TYPE_BOOLEAN, &value);
574 }
575
576 void ofono_cdma_connman_dormant_notify(struct ofono_cdma_connman *cm,
577                                         ofono_bool_t dormant)
578 {
579         DBusConnection *conn = ofono_dbus_get_connection();
580         const char *path;
581
582         if (cm == NULL)
583                 return;
584
585         cm->dormant = dormant;
586         path = __ofono_atom_get_path(cm->atom);
587
588         ofono_dbus_signal_property_changed(conn, path,
589                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
590                                 "Dormant", DBUS_TYPE_BOOLEAN, &dormant);
591 }
592
593 static void cdma_connman_unregister(struct ofono_atom *atom)
594 {
595         DBusConnection *conn = ofono_dbus_get_connection();
596         struct ofono_modem *modem = __ofono_atom_get_modem(atom);
597         const char *path = __ofono_atom_get_path(atom);
598
599         DBG("");
600
601         g_dbus_unregister_interface(conn, path,
602                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE);
603         ofono_modem_remove_interface(modem,
604                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE);
605 }
606
607 static void cdma_connman_remove(struct ofono_atom *atom)
608 {
609         struct ofono_cdma_connman *cm = __ofono_atom_get_data(atom);
610
611         DBG("atom: %p", atom);
612
613         if (cm == NULL)
614                 return;
615
616         if (cm->driver && cm->driver->remove)
617                 cm->driver->remove(cm);
618
619         g_free(cm);
620 }
621
622 struct ofono_cdma_connman *ofono_cdma_connman_create(
623                                                 struct ofono_modem *modem,
624                                                 unsigned int vendor,
625                                                 const char *driver,
626                                                 void *data)
627 {
628         struct ofono_cdma_connman *cm;
629         GSList *l;
630
631         DBG("");
632
633         if (driver == NULL)
634                 return NULL;
635
636         cm = g_try_new0(struct ofono_cdma_connman, 1);
637         if (cm == NULL)
638                 return NULL;
639
640         cm->atom = __ofono_modem_add_atom(modem,
641                                         OFONO_ATOM_TYPE_CDMA_CONNMAN,
642                                         cdma_connman_remove, cm);
643
644         for (l = g_drivers; l; l = l->next) {
645                 const struct ofono_cdma_connman_driver *drv = l->data;
646
647                 if (g_strcmp0(drv->name, driver))
648                         continue;
649
650                 if (drv->probe(cm, vendor, data) < 0)
651                         continue;
652
653                 cm->driver = drv;
654                 break;
655         }
656
657         return cm;
658 }
659
660 void ofono_cdma_connman_register(struct ofono_cdma_connman *cm)
661 {
662         DBusConnection *conn = ofono_dbus_get_connection();
663         struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom);
664         const char *path = __ofono_atom_get_path(cm->atom);
665
666         DBG("");
667
668         if (!g_dbus_register_interface(conn, path,
669                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
670                                 cdma_connman_methods, cdma_connman_signals,
671                                 NULL, cm, NULL)) {
672                 ofono_error("Could not create %s interface",
673                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE);
674                 return;
675         }
676
677         ofono_modem_add_interface(modem,
678                                 OFONO_CDMA_CONNECTION_MANAGER_INTERFACE);
679
680         __ofono_atom_register(cm->atom, cdma_connman_unregister);
681 }
682
683 void ofono_cdma_connman_remove(struct ofono_cdma_connman *cm)
684 {
685         __ofono_atom_free(cm->atom);
686 }
687
688 void ofono_cdma_connman_set_data(struct ofono_cdma_connman *cm, void *data)
689 {
690         cm->driver_data = data;
691 }
692
693 void *ofono_cdma_connman_get_data(struct ofono_cdma_connman *cm)
694 {
695         return cm->driver_data;
696 }