notify: Add state_idle()
[platform/upstream/connman.git] / src / notifier.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 <gdbus.h>
27
28 #include "connman.h"
29
30 static DBusConnection *connection = NULL;
31
32 static GSList *notifier_list = NULL;
33 static GHashTable *service_hash = NULL;
34
35 static gint compare_priority(gconstpointer a, gconstpointer b)
36 {
37         const struct connman_notifier *notifier1 = a;
38         const struct connman_notifier *notifier2 = b;
39
40         return notifier2->priority - notifier1->priority;
41 }
42
43 /**
44  * connman_notifier_register:
45  * @notifier: notifier module
46  *
47  * Register a new notifier module
48  *
49  * Returns: %0 on success
50  */
51 int connman_notifier_register(struct connman_notifier *notifier)
52 {
53         DBG("notifier %p name %s", notifier, notifier->name);
54
55         notifier_list = g_slist_insert_sorted(notifier_list, notifier,
56                                                         compare_priority);
57
58         return 0;
59 }
60
61 /**
62  * connman_notifier_unregister:
63  * @notifier: notifier module
64  *
65  * Remove a previously registered notifier module
66  */
67 void connman_notifier_unregister(struct connman_notifier *notifier)
68 {
69         DBG("notifier %p name %s", notifier, notifier->name);
70
71         notifier_list = g_slist_remove(notifier_list, notifier);
72 }
73
74 #define MAX_TECHNOLOGIES 10
75
76 static volatile gint registered[MAX_TECHNOLOGIES];
77 static volatile gint enabled[MAX_TECHNOLOGIES];
78 static volatile gint connected[MAX_TECHNOLOGIES];
79
80 void __connman_notifier_list_registered(DBusMessageIter *iter, void *user_data)
81 {
82         int i;
83
84         for (i = 0; i < MAX_TECHNOLOGIES; i++) {
85                 const char *type = __connman_service_type2string(i);
86
87                 if (type == NULL)
88                         continue;
89
90                 if (g_atomic_int_get(&registered[i]) > 0)
91                         dbus_message_iter_append_basic(iter,
92                                                 DBUS_TYPE_STRING, &type);
93         }
94 }
95
96 void __connman_notifier_list_enabled(DBusMessageIter *iter, void *user_data)
97 {
98         int i;
99
100         for (i = 0; i < MAX_TECHNOLOGIES; i++) {
101                 const char *type = __connman_service_type2string(i);
102
103                 if (type == NULL)
104                         continue;
105
106                 if (g_atomic_int_get(&enabled[i]) > 0)
107                         dbus_message_iter_append_basic(iter,
108                                                 DBUS_TYPE_STRING, &type);
109         }
110 }
111
112 void __connman_notifier_list_connected(DBusMessageIter *iter, void *user_data)
113 {
114         int i;
115
116         for (i = 0; i < MAX_TECHNOLOGIES; i++) {
117                 const char *type = __connman_service_type2string(i);
118
119                 if (type == NULL)
120                         continue;
121
122                 if (g_atomic_int_get(&connected[i]) > 0)
123                         dbus_message_iter_append_basic(iter,
124                                                 DBUS_TYPE_STRING, &type);
125         }
126 }
127
128 static void technology_registered(enum connman_service_type type,
129                                                 connman_bool_t registered)
130 {
131         DBG("type %d registered %d", type, registered);
132
133         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
134                 CONNMAN_MANAGER_INTERFACE, "AvailableTechnologies",
135                 DBUS_TYPE_STRING, __connman_notifier_list_registered, NULL);
136 }
137
138 static void technology_enabled(enum connman_service_type type,
139                                                 connman_bool_t enabled)
140 {
141         GSList *list;
142
143         DBG("type %d enabled %d", type, enabled);
144
145         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
146                 CONNMAN_MANAGER_INTERFACE, "EnabledTechnologies",
147                 DBUS_TYPE_STRING, __connman_notifier_list_enabled, NULL);
148
149         for (list = notifier_list; list; list = list->next) {
150                 struct connman_notifier *notifier = list->data;
151
152                 if (notifier->service_enabled)
153                         notifier->service_enabled(type, enabled);
154         }
155 }
156
157 unsigned int __connman_notifier_count_connected(void)
158 {
159         unsigned int i, count = 0;
160
161         for (i = 0; i < MAX_TECHNOLOGIES; i++) {
162                 if (g_atomic_int_get(&connected[i]) > 0)
163                         count++;
164         }
165
166         return count;
167 }
168
169 const char *__connman_notifier_get_state(void)
170 {
171         unsigned int count = __connman_notifier_count_connected();
172
173         if (count > 0)
174                 return "online";
175
176         return "offline";
177 }
178
179 static void state_changed(void)
180 {
181         unsigned int count = __connman_notifier_count_connected();
182         char *state = "offline";
183         DBusMessage *signal;
184
185         if (count > 1)
186                 return;
187
188         if (count > 0)
189                 state = "online";
190
191         connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
192                                 CONNMAN_MANAGER_INTERFACE, "State",
193                                                 DBUS_TYPE_STRING, &state);
194
195         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
196                                 CONNMAN_MANAGER_INTERFACE, "StateChanged");
197         if (signal == NULL)
198                 return;
199
200         dbus_message_append_args(signal, DBUS_TYPE_STRING, &state,
201                                                         DBUS_TYPE_INVALID);
202
203         g_dbus_send_message(connection, signal);
204 }
205
206 static void technology_connected(enum connman_service_type type,
207                                                 connman_bool_t connected)
208 {
209         DBG("type %d connected %d", type, connected);
210
211         connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
212                 CONNMAN_MANAGER_INTERFACE, "ConnectedTechnologies",
213                 DBUS_TYPE_STRING, __connman_notifier_list_connected, NULL);
214
215         state_changed();
216 }
217
218 void __connman_notifier_register(enum connman_service_type type)
219 {
220         DBG("type %d", type);
221
222         switch (type) {
223         case CONNMAN_SERVICE_TYPE_UNKNOWN:
224         case CONNMAN_SERVICE_TYPE_SYSTEM:
225         case CONNMAN_SERVICE_TYPE_GPS:
226         case CONNMAN_SERVICE_TYPE_VPN:
227         case CONNMAN_SERVICE_TYPE_GADGET:
228                 return;
229         case CONNMAN_SERVICE_TYPE_ETHERNET:
230         case CONNMAN_SERVICE_TYPE_WIFI:
231         case CONNMAN_SERVICE_TYPE_WIMAX:
232         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
233         case CONNMAN_SERVICE_TYPE_CELLULAR:
234                 break;
235         }
236
237         if (g_atomic_int_exchange_and_add(&registered[type], 1) == 0)
238                 technology_registered(type, TRUE);
239 }
240
241 void __connman_notifier_unregister(enum connman_service_type type)
242 {
243         DBG("type %d", type);
244
245         if (g_atomic_int_get(&registered[type]) == 0) {
246                 connman_error("notifier unregister underflow");
247                 return;
248         }
249
250         switch (type) {
251         case CONNMAN_SERVICE_TYPE_UNKNOWN:
252         case CONNMAN_SERVICE_TYPE_SYSTEM:
253         case CONNMAN_SERVICE_TYPE_GPS:
254         case CONNMAN_SERVICE_TYPE_VPN:
255         case CONNMAN_SERVICE_TYPE_GADGET:
256                 return;
257         case CONNMAN_SERVICE_TYPE_ETHERNET:
258         case CONNMAN_SERVICE_TYPE_WIFI:
259         case CONNMAN_SERVICE_TYPE_WIMAX:
260         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
261         case CONNMAN_SERVICE_TYPE_CELLULAR:
262                 break;
263         }
264
265         if (g_atomic_int_dec_and_test(&registered[type]) == TRUE)
266                 technology_registered(type, FALSE);
267 }
268
269 void __connman_notifier_enable(enum connman_service_type type)
270 {
271         DBG("type %d", type);
272
273         switch (type) {
274         case CONNMAN_SERVICE_TYPE_UNKNOWN:
275         case CONNMAN_SERVICE_TYPE_SYSTEM:
276         case CONNMAN_SERVICE_TYPE_GPS:
277         case CONNMAN_SERVICE_TYPE_VPN:
278         case CONNMAN_SERVICE_TYPE_GADGET:
279                 return;
280         case CONNMAN_SERVICE_TYPE_ETHERNET:
281         case CONNMAN_SERVICE_TYPE_WIFI:
282         case CONNMAN_SERVICE_TYPE_WIMAX:
283         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
284         case CONNMAN_SERVICE_TYPE_CELLULAR:
285                 break;
286         }
287
288         if (g_atomic_int_exchange_and_add(&enabled[type], 1) == 0)
289                 technology_enabled(type, TRUE);
290 }
291
292 void __connman_notifier_disable(enum connman_service_type type)
293 {
294         DBG("type %d", type);
295
296         if (g_atomic_int_get(&enabled[type]) == 0) {
297                 connman_error("notifier disable underflow");
298                 return;
299         }
300
301         switch (type) {
302         case CONNMAN_SERVICE_TYPE_UNKNOWN:
303         case CONNMAN_SERVICE_TYPE_SYSTEM:
304         case CONNMAN_SERVICE_TYPE_GPS:
305         case CONNMAN_SERVICE_TYPE_VPN:
306         case CONNMAN_SERVICE_TYPE_GADGET:
307                 return;
308         case CONNMAN_SERVICE_TYPE_ETHERNET:
309         case CONNMAN_SERVICE_TYPE_WIFI:
310         case CONNMAN_SERVICE_TYPE_WIMAX:
311         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
312         case CONNMAN_SERVICE_TYPE_CELLULAR:
313                 break;
314         }
315
316         if (g_atomic_int_dec_and_test(&enabled[type]) == TRUE)
317                 technology_enabled(type, FALSE);
318 }
319
320 void __connman_notifier_connect(enum connman_service_type type)
321 {
322         DBG("type %d", type);
323
324         switch (type) {
325         case CONNMAN_SERVICE_TYPE_UNKNOWN:
326         case CONNMAN_SERVICE_TYPE_SYSTEM:
327         case CONNMAN_SERVICE_TYPE_GPS:
328         case CONNMAN_SERVICE_TYPE_VPN:
329         case CONNMAN_SERVICE_TYPE_GADGET:
330                 return;
331         case CONNMAN_SERVICE_TYPE_ETHERNET:
332         case CONNMAN_SERVICE_TYPE_WIFI:
333         case CONNMAN_SERVICE_TYPE_WIMAX:
334         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
335         case CONNMAN_SERVICE_TYPE_CELLULAR:
336                 break;
337         }
338
339         if (g_atomic_int_exchange_and_add(&connected[type], 1) == 0)
340                 technology_connected(type, TRUE);
341 }
342
343 void __connman_notifier_disconnect(enum connman_service_type type)
344 {
345         DBG("type %d", type);
346
347         if (g_atomic_int_get(&connected[type]) == 0) {
348                 connman_error("notifier disconnect underflow");
349                 return;
350         }
351
352         switch (type) {
353         case CONNMAN_SERVICE_TYPE_UNKNOWN:
354         case CONNMAN_SERVICE_TYPE_SYSTEM:
355         case CONNMAN_SERVICE_TYPE_GPS:
356         case CONNMAN_SERVICE_TYPE_VPN:
357         case CONNMAN_SERVICE_TYPE_GADGET:
358                 return;
359         case CONNMAN_SERVICE_TYPE_ETHERNET:
360         case CONNMAN_SERVICE_TYPE_WIFI:
361         case CONNMAN_SERVICE_TYPE_WIMAX:
362         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
363         case CONNMAN_SERVICE_TYPE_CELLULAR:
364                 break;
365         }
366
367         if (g_atomic_int_dec_and_test(&connected[type]) == TRUE)
368                 technology_connected(type, FALSE);
369 }
370
371 static void technology_default(enum connman_service_type type)
372 {
373         const char *str;
374
375         str = __connman_service_type2string(type);
376         if (str == NULL)
377                 str = "";
378
379         connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
380                         CONNMAN_MANAGER_INTERFACE, "DefaultTechnology",
381                                                 DBUS_TYPE_STRING, &str);
382 }
383
384 void __connman_notifier_default_changed(struct connman_service *service)
385 {
386         enum connman_service_type type = connman_service_get_type(service);
387         char *interface;
388         GSList *list;
389
390         technology_default(type);
391
392         interface = connman_service_get_interface(service);
393         __connman_tethering_update_interface(interface);
394         g_free(interface);
395
396         for (list = notifier_list; list; list = list->next) {
397                 struct connman_notifier *notifier = list->data;
398
399                 if (notifier->default_changed)
400                         notifier->default_changed(service);
401         }
402 }
403
404 void __connman_notifier_service_add(struct connman_service *service,
405                                         const char *name)
406 {
407         GSList *list;
408
409         for (list = notifier_list; list; list = list->next) {
410                 struct connman_notifier *notifier = list->data;
411
412                 if (notifier->service_add)
413                         notifier->service_add(service, name);
414         }
415 }
416
417 void __connman_notifier_service_remove(struct connman_service *service)
418 {
419         GSList *list;
420
421         if (g_hash_table_lookup(service_hash, service) != NULL) {
422                 /*
423                  * This is a tempory check for consistency. It can be
424                  * removed when there are no reports for the following
425                  * error message.
426                  */
427                 connman_error("Service state machine inconsistency detected.");
428
429                 g_hash_table_remove(service_hash, service);
430         }
431
432         for (list = notifier_list; list; list = list->next) {
433                 struct connman_notifier *notifier = list->data;
434
435                 if (notifier->service_remove)
436                         notifier->service_remove(service);
437         }
438 }
439
440 void __connman_notifier_proxy_changed(struct connman_service *service)
441 {
442         GSList *list;
443
444         for (list = notifier_list; list; list = list->next) {
445                 struct connman_notifier *notifier = list->data;
446
447                 if (notifier->proxy_changed)
448                         notifier->proxy_changed(service);
449         }
450 }
451
452 static void offlinemode_changed(dbus_bool_t enabled)
453 {
454         DBG("enabled %d", enabled);
455
456         connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
457                                 CONNMAN_MANAGER_INTERFACE, "OfflineMode",
458                                                 DBUS_TYPE_BOOLEAN, &enabled);
459 }
460
461 void __connman_notifier_offlinemode(connman_bool_t enabled)
462 {
463         GSList *list;
464
465         DBG("enabled %d", enabled);
466
467         __connman_profile_changed(FALSE);
468
469         offlinemode_changed(enabled);
470
471         for (list = notifier_list; list; list = list->next) {
472                 struct connman_notifier *notifier = list->data;
473
474                 if (notifier->offline_mode)
475                         notifier->offline_mode(enabled);
476         }
477 }
478
479 static void notify_idle_state(connman_bool_t idle)
480 {
481         GSList *list;
482
483         DBG("idle %d", idle);
484
485         for (list = notifier_list; list; list = list->next) {
486                 struct connman_notifier *notifier = list->data;
487
488                 if (notifier->idle_state)
489                         notifier->idle_state(idle);
490         }
491 }
492
493 void __connman_notifier_service_state_changed(struct connman_service *service,
494                                         enum connman_service_state state)
495 {
496         GSList *list;
497         unsigned int old_size;
498         connman_bool_t found;
499
500         for (list = notifier_list; list; list = list->next) {
501                 struct connman_notifier *notifier = list->data;
502
503                 if (notifier->service_state_changed)
504                         notifier->service_state_changed(service, state);
505         }
506
507         old_size = g_hash_table_size(service_hash);
508         found = g_hash_table_lookup(service_hash, service) != NULL;
509
510         switch (state) {
511         case CONNMAN_SERVICE_STATE_UNKNOWN:
512         case CONNMAN_SERVICE_STATE_DISCONNECT:
513         case CONNMAN_SERVICE_STATE_IDLE:
514                 if (found == FALSE)
515                         break;
516
517                 g_hash_table_remove(service_hash, service);
518                 if (old_size == 1)
519                         notify_idle_state(TRUE);
520
521                 break;
522         case CONNMAN_SERVICE_STATE_ASSOCIATION:
523         case CONNMAN_SERVICE_STATE_CONFIGURATION:
524         case CONNMAN_SERVICE_STATE_READY:
525         case CONNMAN_SERVICE_STATE_ONLINE:
526         case CONNMAN_SERVICE_STATE_FAILURE:
527                 if (found == TRUE)
528                         break;
529
530                 g_hash_table_insert(service_hash, service, service);
531                 if (old_size == 0)
532                         notify_idle_state(FALSE);
533
534                 break;
535         }
536 }
537
538 void __connman_notifier_ipconfig_changed(struct connman_service *service,
539                                         struct connman_ipconfig *ipconfig)
540 {
541         GSList *list;
542
543         for (list = notifier_list; list; list = list->next) {
544                 struct connman_notifier *notifier = list->data;
545
546                 if (notifier->ipconfig_changed)
547                         notifier->ipconfig_changed(service, ipconfig);
548         }
549 }
550
551 static connman_bool_t technology_supported(enum connman_service_type type)
552 {
553         switch (type) {
554         case CONNMAN_SERVICE_TYPE_UNKNOWN:
555         case CONNMAN_SERVICE_TYPE_SYSTEM:
556         case CONNMAN_SERVICE_TYPE_GPS:
557         case CONNMAN_SERVICE_TYPE_VPN:
558         case CONNMAN_SERVICE_TYPE_GADGET:
559                 return FALSE;
560         case CONNMAN_SERVICE_TYPE_ETHERNET:
561         case CONNMAN_SERVICE_TYPE_WIFI:
562         case CONNMAN_SERVICE_TYPE_WIMAX:
563         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
564         case CONNMAN_SERVICE_TYPE_CELLULAR:
565                 break;
566         }
567
568         return TRUE;
569 }
570
571 connman_bool_t __connman_notifier_is_registered(enum connman_service_type type)
572 {
573         DBG("type %d", type);
574
575         if (technology_supported(type) == FALSE)
576                 return FALSE;
577
578         if (g_atomic_int_get(&registered[type]) > 0)
579                 return TRUE;
580
581         return FALSE;
582 }
583
584 connman_bool_t __connman_notifier_is_enabled(enum connman_service_type type)
585 {
586         DBG("type %d", type);
587
588         if (technology_supported(type) == FALSE)
589                 return FALSE;
590
591         if (g_atomic_int_get(&enabled[type]) > 0)
592                 return TRUE;
593
594         return FALSE;
595 }
596
597 int __connman_notifier_init(void)
598 {
599         DBG("");
600
601         connection = connman_dbus_get_connection();
602
603         service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
604                                                 NULL, NULL);
605
606
607         return 0;
608 }
609
610 void __connman_notifier_cleanup(void)
611 {
612         DBG("");
613
614         g_hash_table_destroy(service_hash);
615         service_hash = NULL;
616
617         dbus_connection_unref(connection);
618 }