technology: Save/Load offlinemode
[platform/upstream/connman.git] / src / technology.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <string.h>
28
29 #include <gdbus.h>
30
31 #include "connman.h"
32
33 static DBusConnection *connection;
34
35 static GSList *technology_list = NULL;
36
37 static connman_bool_t global_offlinemode;
38
39 struct connman_rfkill {
40         unsigned int index;
41         enum connman_service_type type;
42         connman_bool_t softblock;
43         connman_bool_t hardblock;
44 };
45
46 enum connman_technology_state {
47         CONNMAN_TECHNOLOGY_STATE_UNKNOWN   = 0,
48         CONNMAN_TECHNOLOGY_STATE_OFFLINE   = 1,
49         CONNMAN_TECHNOLOGY_STATE_ENABLED   = 2,
50         CONNMAN_TECHNOLOGY_STATE_CONNECTED = 3,
51 };
52
53 struct connman_technology {
54         gint refcount;
55         enum connman_service_type type;
56         enum connman_technology_state state;
57         char *path;
58         GHashTable *rfkill_list;
59         GSList *device_list;
60         gint enabled;
61         char *regdom;
62
63         connman_bool_t tethering;
64         char *tethering_ident;
65         char *tethering_passphrase;
66
67         connman_bool_t enable_persistent; /* Save the tech state */
68
69         struct connman_technology_driver *driver;
70         void *driver_data;
71
72         DBusMessage *pending_reply;
73         guint pending_timeout;
74 };
75
76 static GSList *driver_list = NULL;
77
78 static gint compare_priority(gconstpointer a, gconstpointer b)
79 {
80         const struct connman_technology_driver *driver1 = a;
81         const struct connman_technology_driver *driver2 = b;
82
83         return driver2->priority - driver1->priority;
84 }
85
86 /**
87  * connman_technology_driver_register:
88  * @driver: Technology driver definition
89  *
90  * Register a new technology driver
91  *
92  * Returns: %0 on success
93  */
94 int connman_technology_driver_register(struct connman_technology_driver *driver)
95 {
96         GSList *list;
97         struct connman_technology *technology;
98
99         DBG("driver %p name %s", driver, driver->name);
100
101         driver_list = g_slist_insert_sorted(driver_list, driver,
102                                                         compare_priority);
103
104         for (list = technology_list; list; list = list->next) {
105                 technology = list->data;
106
107                 if (technology->driver != NULL)
108                         continue;
109
110                 if (technology->type == driver->type)
111                         technology->driver = driver;
112         }
113
114         return 0;
115 }
116
117 /**
118  * connman_technology_driver_unregister:
119  * @driver: Technology driver definition
120  *
121  * Remove a previously registered technology driver
122  */
123 void connman_technology_driver_unregister(struct connman_technology_driver *driver)
124 {
125         GSList *list;
126         struct connman_technology *technology;
127
128         DBG("driver %p name %s", driver, driver->name);
129
130         for (list = technology_list; list; list = list->next) {
131                 technology = list->data;
132
133                 if (technology->driver == NULL)
134                         continue;
135
136                 if (technology->type == driver->type) {
137                         technology->driver->remove(technology);
138                         technology->driver = NULL;
139                 }
140         }
141
142         driver_list = g_slist_remove(driver_list, driver);
143 }
144
145 static void tethering_changed(struct connman_technology *technology)
146 {
147         connman_bool_t tethering = technology->tethering;
148
149         connman_dbus_property_changed_basic(technology->path,
150                                 CONNMAN_TECHNOLOGY_INTERFACE, "Tethering",
151                                                 DBUS_TYPE_BOOLEAN, &tethering);
152 }
153
154 void connman_technology_tethering_notify(struct connman_technology *technology,
155                                                         connman_bool_t enabled)
156 {
157         DBG("technology %p enabled %u", technology, enabled);
158
159         if (technology->tethering == enabled)
160                 return;
161
162         technology->tethering = enabled;
163
164         tethering_changed(technology);
165
166         if (enabled == TRUE)
167                 __connman_tethering_set_enabled();
168         else
169                 __connman_tethering_set_disabled();
170 }
171
172 static int set_tethering(struct connman_technology *technology,
173                                 const char *bridge, connman_bool_t enabled)
174 {
175         const char *ident, *passphrase;
176
177         ident = technology->tethering_ident;
178         passphrase = technology->tethering_passphrase;
179
180         if (technology->driver == NULL ||
181                         technology->driver->set_tethering == NULL)
182                 return -EOPNOTSUPP;
183
184         if (technology->type == CONNMAN_SERVICE_TYPE_WIFI &&
185             (ident == NULL || passphrase == NULL))
186                 return -EINVAL;
187
188         return technology->driver->set_tethering(technology, ident, passphrase,
189                                                         bridge, enabled);
190 }
191
192 void connman_technology_regdom_notify(struct connman_technology *technology,
193                                                         const char *alpha2)
194 {
195         DBG("");
196
197         if (alpha2 == NULL)
198                 connman_error("Failed to set regulatory domain");
199         else
200                 DBG("Regulatory domain set to %s", alpha2);
201
202         g_free(technology->regdom);
203         technology->regdom = g_strdup(alpha2);
204 }
205
206 int connman_technology_set_regdom(const char *alpha2)
207 {
208         GSList *list;
209
210         for (list = technology_list; list; list = list->next) {
211                 struct connman_technology *technology = list->data;
212
213                 if (technology->driver == NULL)
214                         continue;
215
216                 if (technology->driver->set_regdom)
217                         technology->driver->set_regdom(technology, alpha2);
218         }
219
220         return 0;
221 }
222
223 static void free_rfkill(gpointer data)
224 {
225         struct connman_rfkill *rfkill = data;
226
227         g_free(rfkill);
228 }
229
230 void __connman_technology_list(DBusMessageIter *iter, void *user_data)
231 {
232         GSList *list;
233
234         for (list = technology_list; list; list = list->next) {
235                 struct connman_technology *technology = list->data;
236
237                 if (technology->path == NULL)
238                         continue;
239
240                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
241                                                         &technology->path);
242         }
243 }
244
245 static void technologies_changed(void)
246 {
247         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
248                         CONNMAN_MANAGER_INTERFACE, "Technologies",
249                         DBUS_TYPE_OBJECT_PATH, __connman_technology_list, NULL);
250 }
251
252 static const char *state2string(enum connman_technology_state state)
253 {
254         switch (state) {
255         case CONNMAN_TECHNOLOGY_STATE_UNKNOWN:
256                 break;
257         case CONNMAN_TECHNOLOGY_STATE_OFFLINE:
258                 return "offline";
259         case CONNMAN_TECHNOLOGY_STATE_ENABLED:
260                 return "enabled";
261         case CONNMAN_TECHNOLOGY_STATE_CONNECTED:
262                 return "connected";
263         }
264
265         return NULL;
266 }
267
268 static void state_changed(struct connman_technology *technology)
269 {
270         const char *str;
271
272         str = state2string(technology->state);
273         if (str == NULL)
274                 return;
275
276         connman_dbus_property_changed_basic(technology->path,
277                                 CONNMAN_TECHNOLOGY_INTERFACE, "State",
278                                                 DBUS_TYPE_STRING, &str);
279 }
280
281 static const char *get_name(enum connman_service_type type)
282 {
283         switch (type) {
284         case CONNMAN_SERVICE_TYPE_UNKNOWN:
285         case CONNMAN_SERVICE_TYPE_SYSTEM:
286         case CONNMAN_SERVICE_TYPE_GPS:
287         case CONNMAN_SERVICE_TYPE_VPN:
288         case CONNMAN_SERVICE_TYPE_GADGET:
289                 break;
290         case CONNMAN_SERVICE_TYPE_ETHERNET:
291                 return "Wired";
292         case CONNMAN_SERVICE_TYPE_WIFI:
293                 return "WiFi";
294         case CONNMAN_SERVICE_TYPE_WIMAX:
295                 return "WiMAX";
296         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
297                 return "Bluetooth";
298         case CONNMAN_SERVICE_TYPE_CELLULAR:
299                 return "3G";
300         }
301
302         return NULL;
303 }
304
305 connman_bool_t __connman_technology_get_offlinemode(void)
306 {
307         return global_offlinemode;
308 }
309
310 static int connman_technology_save_offlinemode()
311 {
312         GKeyFile *keyfile;
313
314         keyfile = __connman_storage_open_profile("default");
315         if (keyfile == NULL)
316                 return -EIO;
317
318         g_key_file_set_boolean(keyfile, "global",
319                                         "OfflineMode", global_offlinemode);
320
321         __connman_storage_close_profile("default", keyfile, TRUE);
322
323         return 0;
324 }
325
326 static connman_bool_t connman_technology_load_offlinemode()
327 {
328         GKeyFile *keyfile;
329         GError *error = NULL;
330         connman_bool_t offlinemode;
331
332         /* If there is a error, we enable offlinemode */
333         keyfile = __connman_storage_open_profile("default");
334         if (keyfile == NULL)
335                 return TRUE;
336
337         offlinemode = g_key_file_get_boolean(keyfile, "global",
338                                                 "OfflineMode", &error);
339         if (error != NULL) {
340                 offlinemode = TRUE;
341                 g_clear_error(&error);
342         }
343         __connman_storage_close_profile("default", keyfile, FALSE);
344
345         return offlinemode;
346 }
347
348 static DBusMessage *get_properties(DBusConnection *conn,
349                                         DBusMessage *message, void *user_data)
350 {
351         struct connman_technology *technology = user_data;
352         DBusMessage *reply;
353         DBusMessageIter array, dict;
354         const char *str;
355
356         reply = dbus_message_new_method_return(message);
357         if (reply == NULL)
358                 return NULL;
359
360         dbus_message_iter_init_append(reply, &array);
361
362         connman_dbus_dict_open(&array, &dict);
363
364         str = state2string(technology->state);
365         if (str != NULL)
366                 connman_dbus_dict_append_basic(&dict, "State",
367                                                 DBUS_TYPE_STRING, &str);
368
369         str = get_name(technology->type);
370         if (str != NULL)
371                 connman_dbus_dict_append_basic(&dict, "Name",
372                                                 DBUS_TYPE_STRING, &str);
373
374         str = __connman_service_type2string(technology->type);
375         if (str != NULL)
376                 connman_dbus_dict_append_basic(&dict, "Type",
377                                                 DBUS_TYPE_STRING, &str);
378
379         connman_dbus_dict_append_basic(&dict, "Tethering",
380                                         DBUS_TYPE_BOOLEAN,
381                                         &technology->tethering);
382
383         if (technology->tethering_ident != NULL)
384                 connman_dbus_dict_append_basic(&dict, "TetheringIdentifier",
385                                                 DBUS_TYPE_STRING,
386                                                 &technology->tethering_ident);
387
388         if (technology->tethering_passphrase != NULL)
389                 connman_dbus_dict_append_basic(&dict, "TetheringPassphrase",
390                                                 DBUS_TYPE_STRING,
391                                                 &technology->tethering_passphrase);
392
393         connman_dbus_dict_close(&array, &dict);
394
395         return reply;
396 }
397
398 static DBusMessage *set_property(DBusConnection *conn,
399                                         DBusMessage *msg, void *data)
400 {
401         struct connman_technology *technology = data;
402         DBusMessageIter iter, value;
403         const char *name;
404         int type;
405
406         DBG("conn %p", conn);
407
408         if (dbus_message_iter_init(msg, &iter) == FALSE)
409                 return __connman_error_invalid_arguments(msg);
410
411         dbus_message_iter_get_basic(&iter, &name);
412         dbus_message_iter_next(&iter);
413         dbus_message_iter_recurse(&iter, &value);
414
415         type = dbus_message_iter_get_arg_type(&value);
416
417         DBG("property %s", name);
418
419         if (g_str_equal(name, "Tethering") == TRUE) {
420                 int err;
421                 connman_bool_t tethering;
422                 const char *bridge;
423
424                 if (type != DBUS_TYPE_BOOLEAN)
425                         return __connman_error_invalid_arguments(msg);
426
427                 dbus_message_iter_get_basic(&value, &tethering);
428
429                 if (technology->tethering == tethering)
430                         return __connman_error_in_progress(msg);
431
432                 bridge = __connman_tethering_get_bridge();
433                 if (bridge == NULL)
434                         return __connman_error_not_supported(msg);
435
436                 err = set_tethering(technology, bridge, tethering);
437                 if (err < 0)
438                         return __connman_error_failed(msg, -err);
439
440         } else if (g_str_equal(name, "TetheringIdentifier") == TRUE) {
441                 const char *str;
442
443                 dbus_message_iter_get_basic(&value, &str);
444
445                 if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
446                         return __connman_error_not_supported(msg);
447
448                 technology->tethering_ident = g_strdup(str);
449         } else if (g_str_equal(name, "TetheringPassphrase") == TRUE) {
450                 const char *str;
451
452                 dbus_message_iter_get_basic(&value, &str);
453
454                 if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
455                         return __connman_error_not_supported(msg);
456
457                 if (strlen(str) < 8)
458                         return __connman_error_invalid_arguments(msg);
459
460                 technology->tethering_passphrase = g_strdup(str);
461         } else
462                 return __connman_error_invalid_property(msg);
463
464         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
465 }
466
467 static GDBusMethodTable technology_methods[] = {
468         { "GetProperties", "",   "a{sv}", get_properties },
469         { "SetProperty",   "sv", "",      set_property   },
470         { },
471 };
472
473 static GDBusSignalTable technology_signals[] = {
474         { "PropertyChanged", "sv" },
475         { },
476 };
477
478 static struct connman_technology *technology_find(enum connman_service_type type)
479 {
480         GSList *list;
481
482         DBG("type %d", type);
483
484         for (list = technology_list; list; list = list->next) {
485                 struct connman_technology *technology = list->data;
486
487                 if (technology->type == type)
488                         return technology;
489         }
490
491         return NULL;
492 }
493
494 static struct connman_technology *technology_get(enum connman_service_type type)
495 {
496         struct connman_technology *technology;
497         const char *str;
498         GSList *list;
499
500         DBG("type %d", type);
501
502         technology = technology_find(type);
503         if (technology != NULL) {
504                 g_atomic_int_inc(&technology->refcount);
505                 goto done;
506         }
507
508         str = __connman_service_type2string(type);
509         if (str == NULL)
510                 return NULL;
511
512         technology = g_try_new0(struct connman_technology, 1);
513         if (technology == NULL)
514                 return NULL;
515
516         technology->refcount = 1;
517
518         technology->type = type;
519         technology->path = g_strdup_printf("%s/technology/%s",
520                                                         CONNMAN_PATH, str);
521
522         technology->rfkill_list = g_hash_table_new_full(g_int_hash, g_int_equal,
523                                                         NULL, free_rfkill);
524         technology->device_list = NULL;
525
526         technology->pending_reply = NULL;
527         technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
528
529         __connman_storage_load_technology(technology);
530
531         if (g_dbus_register_interface(connection, technology->path,
532                                         CONNMAN_TECHNOLOGY_INTERFACE,
533                                         technology_methods, technology_signals,
534                                         NULL, technology, NULL) == FALSE) {
535                 connman_error("Failed to register %s", technology->path);
536                 g_free(technology);
537                 return NULL;
538         }
539
540         technology_list = g_slist_append(technology_list, technology);
541
542         technologies_changed();
543
544         if (technology->driver != NULL)
545                 goto done;
546
547         for (list = driver_list; list; list = list->next) {
548                 struct connman_technology_driver *driver = list->data;
549
550                 DBG("driver %p name %s", driver, driver->name);
551
552                 if (driver->type != technology->type)
553                         continue;
554
555                 if (driver->probe(technology) == 0) {
556                         technology->driver = driver;
557                         break;
558                 }
559         }
560
561 done:
562         DBG("technology %p", technology);
563
564         return technology;
565 }
566
567 static void technology_put(struct connman_technology *technology)
568 {
569         DBG("technology %p", technology);
570
571         if (g_atomic_int_dec_and_test(&technology->refcount) == FALSE)
572                 return;
573
574         if (technology->driver) {
575                 technology->driver->remove(technology);
576                 technology->driver = NULL;
577         }
578
579         technology_list = g_slist_remove(technology_list, technology);
580
581         technologies_changed();
582
583         g_dbus_unregister_interface(connection, technology->path,
584                                                 CONNMAN_TECHNOLOGY_INTERFACE);
585
586         g_slist_free(technology->device_list);
587         g_hash_table_destroy(technology->rfkill_list);
588
589         g_free(technology->path);
590         g_free(technology->regdom);
591         g_free(technology);
592 }
593
594 void __connman_technology_add_interface(enum connman_service_type type,
595                                 int index, const char *name, const char *ident)
596 {
597         struct connman_technology *technology;
598
599         switch (type) {
600         case CONNMAN_SERVICE_TYPE_UNKNOWN:
601         case CONNMAN_SERVICE_TYPE_SYSTEM:
602                 return;
603         case CONNMAN_SERVICE_TYPE_ETHERNET:
604         case CONNMAN_SERVICE_TYPE_WIFI:
605         case CONNMAN_SERVICE_TYPE_WIMAX:
606         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
607         case CONNMAN_SERVICE_TYPE_CELLULAR:
608         case CONNMAN_SERVICE_TYPE_GPS:
609         case CONNMAN_SERVICE_TYPE_VPN:
610         case CONNMAN_SERVICE_TYPE_GADGET:
611                 break;
612         }
613
614         connman_info("Create interface %s [ %s ]", name,
615                                 __connman_service_type2string(type));
616
617         technology = technology_get(type);
618
619         if (technology == NULL || technology->driver == NULL
620                         || technology->driver->add_interface == NULL)
621                 return;
622
623         technology->driver->add_interface(technology,
624                                         index, name, ident);
625 }
626
627 void __connman_technology_remove_interface(enum connman_service_type type,
628                                 int index, const char *name, const char *ident)
629 {
630         struct connman_technology *technology;
631
632         switch (type) {
633         case CONNMAN_SERVICE_TYPE_UNKNOWN:
634         case CONNMAN_SERVICE_TYPE_SYSTEM:
635                 return;
636         case CONNMAN_SERVICE_TYPE_ETHERNET:
637         case CONNMAN_SERVICE_TYPE_WIFI:
638         case CONNMAN_SERVICE_TYPE_WIMAX:
639         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
640         case CONNMAN_SERVICE_TYPE_CELLULAR:
641         case CONNMAN_SERVICE_TYPE_GPS:
642         case CONNMAN_SERVICE_TYPE_VPN:
643         case CONNMAN_SERVICE_TYPE_GADGET:
644                 break;
645         }
646
647         connman_info("Remove interface %s [ %s ]", name,
648                                 __connman_service_type2string(type));
649
650         technology = technology_find(type);
651
652         if (technology == NULL || technology->driver == NULL)
653                 return;
654
655         if (technology->driver->remove_interface)
656                 technology->driver->remove_interface(technology, index);
657
658         technology_put(technology);
659 }
660
661 int __connman_technology_add_device(struct connman_device *device)
662 {
663         struct connman_technology *technology;
664         enum connman_service_type type;
665
666         DBG("device %p", device);
667
668         type = __connman_device_get_service_type(device);
669         __connman_notifier_register(type);
670
671         technology = technology_get(type);
672         if (technology == NULL)
673                 return -ENXIO;
674
675         if (technology->enable_persistent && !global_offlinemode)
676                 __connman_device_enable(device);
677         /* if technology persistent state is offline */
678         if (!technology->enable_persistent)
679                 __connman_device_disable(device);
680
681         technology->device_list = g_slist_append(technology->device_list,
682                                                                 device);
683
684         return 0;
685 }
686
687 int __connman_technology_remove_device(struct connman_device *device)
688 {
689         struct connman_technology *technology;
690         enum connman_service_type type;
691
692         DBG("device %p", device);
693
694         type = __connman_device_get_service_type(device);
695         __connman_notifier_unregister(type);
696
697         technology = technology_find(type);
698         if (technology == NULL)
699                 return -ENXIO;
700
701         technology->device_list = g_slist_remove(technology->device_list,
702                                                                 device);
703         if (technology->device_list == NULL) {
704                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
705                 state_changed(technology);
706         }
707
708         return 0;
709 }
710
711 static gboolean technology_pending_reply(gpointer user_data)
712 {
713         struct connman_technology *technology = user_data;
714         DBusMessage *reply;
715
716         /* Power request timedout, send ETIMEDOUT. */
717         if (technology->pending_reply != NULL) {
718                 reply = __connman_error_failed(technology->pending_reply, ETIMEDOUT);
719                 if (reply != NULL)
720                         g_dbus_send_message(connection, reply);
721
722                 dbus_message_unref(technology->pending_reply);
723                 technology->pending_reply = NULL;
724                 technology->pending_timeout = 0;
725         }
726
727         return FALSE;
728 }
729
730 int __connman_technology_enabled(enum connman_service_type type)
731 {
732         struct connman_technology *technology;
733
734         technology = technology_find(type);
735         if (technology == NULL)
736                 return -ENXIO;
737
738         if (g_atomic_int_exchange_and_add(&technology->enabled, 1) == 0) {
739                 __connman_notifier_enable(type);
740                 technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED;
741                 state_changed(technology);
742         }
743
744         if (technology->pending_reply != NULL) {
745                 g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
746                 dbus_message_unref(technology->pending_reply);
747                 technology->pending_reply = NULL;
748                 technology->pending_timeout = 0;
749         }
750
751         return 0;
752 }
753
754 int __connman_technology_enable(enum connman_service_type type, DBusMessage *msg)
755 {
756         struct connman_technology *technology;
757         GSList *list;
758         int err = 0;
759         int ret = -ENODEV;
760         DBusMessage *reply;
761
762         DBG("type %d enable", type);
763
764         technology = technology_find(type);
765         if (technology == NULL) {
766                 err = -ENXIO;
767                 goto done;
768         }
769
770         if (technology->pending_reply != NULL) {
771                 err = -EBUSY;
772                 goto done;
773         }
774
775         if (msg != NULL) {
776                 technology->pending_reply = dbus_message_ref(msg);
777                 /*
778                  * This is a bit of a trick. When msg is not NULL it means
779                  * thats technology_enable was invoked from the manager API. Hence we save
780                  * the state here.
781                  */
782                 technology->enable_persistent = TRUE;
783                 __connman_storage_save_technology(technology);
784         }
785
786         __connman_rfkill_block(technology->type, FALSE);
787
788         for (list = technology->device_list; list; list = list->next) {
789                 struct connman_device *device = list->data;
790
791                 err = __connman_device_enable(device);
792                 /*
793                  * err = 0 : Device was enabled right away.
794                  * If atleast one device gets enabled, we consider
795                  * the technology to be enabled.
796                  */
797                 if (err == 0)
798                         ret = 0;
799         }
800
801 done:
802         if (ret == 0)
803                 return ret;
804
805         if (msg != NULL) {
806                 if (err == -EINPROGRESS)
807                         technology->pending_timeout = g_timeout_add_seconds(10,
808                                         technology_pending_reply, technology);
809                 else {
810                         reply = __connman_error_failed(msg, -err);
811                         if (reply != NULL)
812                                 g_dbus_send_message(connection, reply);
813                 }
814         }
815
816         return err;
817 }
818
819 int __connman_technology_disabled(enum connman_service_type type)
820 {
821         struct connman_technology *technology;
822
823         technology = technology_find(type);
824         if (technology == NULL)
825                 return -ENXIO;
826
827         if (technology->pending_reply != NULL) {
828                 g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
829                 dbus_message_unref(technology->pending_reply);
830                 technology->pending_reply = NULL;
831         }
832
833         if (g_atomic_int_dec_and_test(&technology->enabled) == TRUE) {
834                 __connman_notifier_disable(type);
835                 technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
836                 state_changed(technology);
837         }
838
839         return 0;
840 }
841
842 int __connman_technology_disable(enum connman_service_type type, DBusMessage *msg)
843 {
844         struct connman_technology *technology;
845         GSList *list;
846         int err = 0;
847         int ret = -ENODEV;
848         DBusMessage *reply;
849
850         DBG("type %d disable", type);
851
852         technology = technology_find(type);
853         if (technology == NULL) {
854                 err = -ENXIO;
855                 goto done;
856         }
857
858         if (technology->pending_reply != NULL) {
859                 err = -EBUSY;
860                 goto done;
861         }
862
863         if (msg != NULL) {
864                 technology->pending_reply = dbus_message_ref(msg);
865                 technology->enable_persistent = FALSE;
866                 __connman_storage_save_technology(technology);
867         }
868
869         __connman_rfkill_block(technology->type, TRUE);
870
871         for (list = technology->device_list; list; list = list->next) {
872                 struct connman_device *device = list->data;
873
874                 err = __connman_device_disable(device);
875                 if (err == 0)
876                         ret = 0;
877         }
878
879 done:
880         if (ret == 0)
881                 return ret;
882
883         if (msg != NULL) {
884                 if (err == -EINPROGRESS)
885                         technology->pending_timeout = g_timeout_add_seconds(10,
886                                         technology_pending_reply, technology);
887                 else {
888                         reply = __connman_error_failed(msg, -err);
889                         if (reply != NULL)
890                                 g_dbus_send_message(connection, reply);
891                 }
892         }
893
894         return err;
895 }
896
897 int __connman_technology_set_offlinemode(connman_bool_t offlinemode)
898 {
899         GSList *list;
900         int err = -EINVAL;
901
902         if (global_offlinemode == offlinemode)
903                 return 0;
904
905         DBG("offlinemode %s", offlinemode ? "On" : "Off");
906
907         /*
908          * This is a bit tricky. When you set offlinemode, there is no
909          * way to differentiate between attempting offline mode and
910          * resuming offlinemode from last saved profile. We need that
911          * information in rfkill_update, otherwise it falls back on the
912          * technology's persistent state. Hence we set the offline mode here
913          * but save it & call the notifier only if its successful.
914          */
915
916         global_offlinemode = offlinemode;
917
918         /* Traverse technology list, enable/disable each technology. */
919         for (list = technology_list; list; list = list->next) {
920                 struct connman_technology *technology = list->data;
921
922                 if (offlinemode)
923                         err = __connman_technology_disable(technology->type, NULL);
924
925                 if (!offlinemode && technology->enable_persistent)
926                         err = __connman_technology_enable(technology->type, NULL);
927         }
928
929         if (err == 0 || err == -EINPROGRESS || err == -EALREADY) {
930                 connman_technology_save_offlinemode();
931                 __connman_notifier_offlinemode(offlinemode);
932         } else
933                 global_offlinemode = connman_technology_load_offlinemode();
934
935         return err;
936 }
937
938 int __connman_technology_add_rfkill(unsigned int index,
939                                         enum connman_service_type type,
940                                                 connman_bool_t softblock,
941                                                 connman_bool_t hardblock)
942 {
943         struct connman_technology *technology;
944         struct connman_rfkill *rfkill;
945
946         DBG("index %u type %d soft %u hard %u", index, type,
947                                                         softblock, hardblock);
948
949         technology = technology_get(type);
950         if (technology == NULL)
951                 return -ENXIO;
952
953         rfkill = g_try_new0(struct connman_rfkill, 1);
954         if (rfkill == NULL)
955                 return -ENOMEM;
956
957         rfkill->index = index;
958         rfkill->type = type;
959         rfkill->softblock = softblock;
960         rfkill->hardblock = hardblock;
961
962         g_hash_table_replace(technology->rfkill_list, &rfkill->index, rfkill);
963
964         if (hardblock) {
965                 DBG("%s is switched off.", get_name(type));
966                 return 0;
967         }
968
969         /*
970          * If Offline mode is on, we softblock the device if it isnt already.
971          * If Offline mode is off, we rely on the persistent state of tech.
972          */
973         if (global_offlinemode) {
974                 if (!softblock)
975                         return __connman_rfkill_block(type, TRUE);
976         } else {
977                 if (technology->enable_persistent && softblock)
978                         return __connman_rfkill_block(type, FALSE);
979                 /* if technology persistent state is offline */
980                 if (!technology->enable_persistent && !softblock)
981                         return __connman_rfkill_block(type, TRUE);
982         }
983
984         return 0;
985 }
986
987 int __connman_technology_update_rfkill(unsigned int index,
988                                         enum connman_service_type type,
989                                                 connman_bool_t softblock,
990                                                 connman_bool_t hardblock)
991 {
992         struct connman_technology *technology;
993         struct connman_rfkill *rfkill;
994
995         DBG("index %u soft %u hard %u", index, softblock, hardblock);
996
997         technology = technology_find(type);
998         if (technology == NULL)
999                 return -ENXIO;
1000
1001         rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
1002         if (rfkill == NULL)
1003                 return -ENXIO;
1004
1005         if (rfkill->softblock == softblock &&
1006                 rfkill->hardblock == hardblock)
1007                 return 0;
1008
1009         rfkill->softblock = softblock;
1010         rfkill->hardblock = hardblock;
1011
1012         if (hardblock) {
1013                 DBG("%s is switched off.", get_name(type));
1014                 return 0;
1015         }
1016
1017         if (!global_offlinemode) {
1018                 if (technology->enable_persistent && softblock)
1019                         return __connman_rfkill_block(type, FALSE);
1020                 if (!technology->enable_persistent && !softblock)
1021                         return __connman_rfkill_block(type, TRUE);
1022         }
1023
1024         return 0;
1025 }
1026
1027 int __connman_technology_remove_rfkill(unsigned int index,
1028                                         enum connman_service_type type)
1029 {
1030         struct connman_technology *technology;
1031         struct connman_rfkill *rfkill;
1032
1033         DBG("index %u", index);
1034
1035         technology = technology_find(type);
1036         if (technology == NULL)
1037                 return -ENXIO;
1038
1039         rfkill = g_hash_table_lookup(technology->rfkill_list, &index);
1040         if (rfkill == NULL)
1041                 return -ENXIO;
1042
1043         g_hash_table_remove(technology->rfkill_list, &index);
1044
1045         technology_put(technology);
1046
1047         return 0;
1048 }
1049
1050 static int technology_load(struct connman_technology *technology)
1051 {
1052         GKeyFile *keyfile;
1053         gchar *identifier;
1054         GError *error = NULL;
1055         connman_bool_t enable;
1056
1057         DBG("technology %p", technology);
1058
1059         keyfile = __connman_storage_open_profile("default");
1060         if (keyfile == NULL)
1061                 return 0;
1062
1063         identifier = g_strdup_printf("%s", get_name(technology->type));
1064         if (identifier == NULL)
1065                 goto done;
1066
1067         enable = g_key_file_get_boolean(keyfile, identifier, "Enable", &error);
1068         if (error == NULL)
1069                 technology->enable_persistent = enable;
1070         else
1071                 technology->enable_persistent = FALSE;
1072
1073         g_clear_error(&error);
1074 done:
1075         g_free(identifier);
1076
1077         __connman_storage_close_profile("default", keyfile, FALSE);
1078
1079         return 0;
1080 }
1081
1082 static int technology_save(struct connman_technology *technology)
1083 {
1084         GKeyFile *keyfile;
1085         gchar *identifier;
1086
1087         DBG("technology %p", technology);
1088
1089         keyfile = __connman_storage_open_profile("default");
1090         if (keyfile == NULL)
1091                 return 0;
1092
1093         identifier = g_strdup_printf("%s", get_name(technology->type));
1094         if (identifier == NULL)
1095                 goto done;
1096
1097         g_key_file_set_boolean(keyfile, identifier, "Enable",
1098                                 technology->enable_persistent);
1099
1100 done:
1101         g_free(identifier);
1102
1103         __connman_storage_close_profile("default", keyfile, TRUE);
1104
1105         return 0;
1106 }
1107
1108 static struct connman_storage tech_storage = {
1109         .name           = "technology",
1110         .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
1111         .tech_load      = technology_load,
1112         .tech_save      = technology_save,
1113 };
1114
1115 int __connman_technology_init(void)
1116 {
1117         DBG("");
1118
1119         connection = connman_dbus_get_connection();
1120
1121         global_offlinemode = connman_technology_load_offlinemode();
1122
1123         return connman_storage_register(&tech_storage);
1124 }
1125
1126 void __connman_technology_cleanup(void)
1127 {
1128         DBG("");
1129
1130         dbus_connection_unref(connection);
1131
1132         connman_storage_unregister(&tech_storage);
1133 }