service: Add services changed signal
[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         offlinemode_changed(enabled);
468
469         for (list = notifier_list; list; list = list->next) {
470                 struct connman_notifier *notifier = list->data;
471
472                 if (notifier->offline_mode)
473                         notifier->offline_mode(enabled);
474         }
475 }
476
477 static void notify_idle_state(connman_bool_t idle)
478 {
479         GSList *list;
480
481         DBG("idle %d", idle);
482
483         for (list = notifier_list; list; list = list->next) {
484                 struct connman_notifier *notifier = list->data;
485
486                 if (notifier->idle_state)
487                         notifier->idle_state(idle);
488         }
489 }
490
491 void __connman_notifier_service_state_changed(struct connman_service *service,
492                                         enum connman_service_state state)
493 {
494         GSList *list;
495         unsigned int old_size;
496         connman_bool_t found;
497
498         for (list = notifier_list; list; list = list->next) {
499                 struct connman_notifier *notifier = list->data;
500
501                 if (notifier->service_state_changed)
502                         notifier->service_state_changed(service, state);
503         }
504
505         old_size = g_hash_table_size(service_hash);
506         found = g_hash_table_lookup(service_hash, service) != NULL;
507
508         switch (state) {
509         case CONNMAN_SERVICE_STATE_UNKNOWN:
510         case CONNMAN_SERVICE_STATE_DISCONNECT:
511         case CONNMAN_SERVICE_STATE_IDLE:
512                 if (found == FALSE)
513                         break;
514
515                 g_hash_table_remove(service_hash, service);
516                 if (old_size == 1)
517                         notify_idle_state(TRUE);
518
519                 break;
520         case CONNMAN_SERVICE_STATE_ASSOCIATION:
521         case CONNMAN_SERVICE_STATE_CONFIGURATION:
522         case CONNMAN_SERVICE_STATE_READY:
523         case CONNMAN_SERVICE_STATE_ONLINE:
524         case CONNMAN_SERVICE_STATE_FAILURE:
525                 if (found == TRUE)
526                         break;
527
528                 g_hash_table_insert(service_hash, service, service);
529                 if (old_size == 0)
530                         notify_idle_state(FALSE);
531
532                 break;
533         }
534 }
535
536 void __connman_notifier_ipconfig_changed(struct connman_service *service,
537                                         struct connman_ipconfig *ipconfig)
538 {
539         GSList *list;
540
541         for (list = notifier_list; list; list = list->next) {
542                 struct connman_notifier *notifier = list->data;
543
544                 if (notifier->ipconfig_changed)
545                         notifier->ipconfig_changed(service, ipconfig);
546         }
547 }
548
549 static connman_bool_t technology_supported(enum connman_service_type type)
550 {
551         switch (type) {
552         case CONNMAN_SERVICE_TYPE_UNKNOWN:
553         case CONNMAN_SERVICE_TYPE_SYSTEM:
554         case CONNMAN_SERVICE_TYPE_GPS:
555         case CONNMAN_SERVICE_TYPE_VPN:
556         case CONNMAN_SERVICE_TYPE_GADGET:
557                 return FALSE;
558         case CONNMAN_SERVICE_TYPE_ETHERNET:
559         case CONNMAN_SERVICE_TYPE_WIFI:
560         case CONNMAN_SERVICE_TYPE_WIMAX:
561         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
562         case CONNMAN_SERVICE_TYPE_CELLULAR:
563                 break;
564         }
565
566         return TRUE;
567 }
568
569 connman_bool_t __connman_notifier_is_registered(enum connman_service_type type)
570 {
571         DBG("type %d", type);
572
573         if (technology_supported(type) == FALSE)
574                 return FALSE;
575
576         if (g_atomic_int_get(&registered[type]) > 0)
577                 return TRUE;
578
579         return FALSE;
580 }
581
582 connman_bool_t __connman_notifier_is_enabled(enum connman_service_type type)
583 {
584         DBG("type %d", type);
585
586         if (technology_supported(type) == FALSE)
587                 return FALSE;
588
589         if (g_atomic_int_get(&enabled[type]) > 0)
590                 return TRUE;
591
592         return FALSE;
593 }
594
595 int __connman_notifier_init(void)
596 {
597         DBG("");
598
599         connection = connman_dbus_get_connection();
600
601         service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
602                                                 NULL, NULL);
603
604
605         return 0;
606 }
607
608 void __connman_notifier_cleanup(void)
609 {
610         DBG("");
611
612         g_hash_table_destroy(service_hash);
613         service_hash = NULL;
614
615         dbus_connection_unref(connection);
616 }