X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fsession.c;h=a5db7ad9f842381e1e58c75a96a516c74657b27f;hb=39c1cfe1013d5731f64407881ddf3dc16942063a;hp=4d41ffcd53757bd58a57c6c21b81a28855bb8e10;hpb=0ca2425ba20b4b9bd5b08fdff17614d564afc151;p=framework%2Fconnectivity%2Fconnman.git diff --git a/src/session.c b/src/session.c index 4d41ffc..a5db7ad 100644 --- a/src/session.c +++ b/src/session.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2010 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. * Copyright (C) 2011 BWM CarIT GmbH. All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -24,6 +24,8 @@ #include #endif +#include + #include #include "connman.h" @@ -31,7 +33,37 @@ static DBusConnection *connection; static GHashTable *session_hash; static connman_bool_t sessionmode; -static struct connman_session *ecall_session; +static struct session_info *ecall_info; + +enum connman_session_trigger { + CONNMAN_SESSION_TRIGGER_UNKNOWN = 0, + CONNMAN_SESSION_TRIGGER_SETTING = 1, + CONNMAN_SESSION_TRIGGER_CONNECT = 2, + CONNMAN_SESSION_TRIGGER_DISCONNECT = 3, + CONNMAN_SESSION_TRIGGER_PERIODIC = 4, + CONNMAN_SESSION_TRIGGER_SERVICE = 5, + CONNMAN_SESSION_TRIGGER_ECALL = 6, +}; + +enum connman_session_reason { + CONNMAN_SESSION_REASON_UNKNOWN = 0, + CONNMAN_SESSION_REASON_CONNECT = 1, + CONNMAN_SESSION_REASON_DISCONNECT = 2, + CONNMAN_SESSION_REASON_FREE_RIDE = 3, + CONNMAN_SESSION_REASON_PERIODIC = 4, +}; + +enum connman_session_state { + CONNMAN_SESSION_STATE_DISCONNECTED = 0, + CONNMAN_SESSION_STATE_CONNECTED = 1, + CONNMAN_SESSION_STATE_ONLINE = 2, +}; + +enum connman_session_type { + CONNMAN_SESSION_TYPE_ANY = 0, + CONNMAN_SESSION_TYPE_LOCAL = 1, + CONNMAN_SESSION_TYPE_INTERNET = 2, +}; enum connman_session_roaming_policy { CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN = 0, @@ -42,12 +74,20 @@ enum connman_session_roaming_policy { CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL = 5, }; -struct session_info { - char *bearer; +struct service_entry { + /* track why this service was selected */ + enum connman_session_reason reason; + enum connman_service_state state; const char *name; + struct connman_service *service; char *ifname; - connman_bool_t connect; - connman_bool_t online; + const char *bearer; + GSList *pending_timeouts; +}; + +struct session_info { + enum connman_session_state state; + enum connman_session_type type; connman_bool_t priority; GSList *allowed_bearers; connman_bool_t avoid_handover; @@ -58,7 +98,8 @@ struct session_info { enum connman_session_roaming_policy roaming_policy; unsigned int marker; - struct connman_service *service; + struct service_entry *entry; + enum connman_session_reason reason; }; struct connman_session { @@ -68,11 +109,11 @@ struct connman_session { guint notify_watch; connman_bool_t append_all; - connman_bool_t info_dirty; - struct session_info info; - struct session_info info_last; + struct session_info *info; + struct session_info *info_last; GSequence *service_list; + GHashTable *service_hash; }; struct bearer_info { @@ -81,11 +122,89 @@ struct bearer_info { enum connman_service_type service_type; }; +static const char *trigger2string(enum connman_session_trigger trigger) +{ + switch (trigger) { + case CONNMAN_SESSION_TRIGGER_UNKNOWN: + break; + case CONNMAN_SESSION_TRIGGER_SETTING: + return "setting"; + case CONNMAN_SESSION_TRIGGER_CONNECT: + return "connect"; + case CONNMAN_SESSION_TRIGGER_DISCONNECT: + return "disconnect"; + case CONNMAN_SESSION_TRIGGER_PERIODIC: + return "periodic"; + case CONNMAN_SESSION_TRIGGER_SERVICE: + return "service"; + case CONNMAN_SESSION_TRIGGER_ECALL: + return "ecall"; + } + + return NULL; +} + +static const char *reason2string(enum connman_session_reason reason) +{ + switch (reason) { + case CONNMAN_SESSION_REASON_UNKNOWN: + return "unknown"; + case CONNMAN_SESSION_REASON_CONNECT: + return "connect"; + case CONNMAN_SESSION_REASON_DISCONNECT: + return "disconnect"; + case CONNMAN_SESSION_REASON_FREE_RIDE: + return "free-ride"; + case CONNMAN_SESSION_REASON_PERIODIC: + return "periodic"; + } + + return NULL; +} + +static const char *state2string(enum connman_session_state state) +{ + switch (state) { + case CONNMAN_SESSION_STATE_DISCONNECTED: + return "disconnected"; + case CONNMAN_SESSION_STATE_CONNECTED: + return "connected"; + case CONNMAN_SESSION_STATE_ONLINE: + return "online"; + } + + return NULL; +} + +static const char *type2string(enum connman_session_type type) +{ + switch (type) { + case CONNMAN_SESSION_TYPE_ANY: + return ""; + case CONNMAN_SESSION_TYPE_LOCAL: + return "local"; + case CONNMAN_SESSION_TYPE_INTERNET: + return "internet"; + } + + return NULL; +} + +static enum connman_session_type string2type(const char *type) +{ + if (g_strcmp0(type, "local") == 0) + return CONNMAN_SESSION_TYPE_LOCAL; + else if (g_strcmp0(type, "internet") == 0) + return CONNMAN_SESSION_TYPE_INTERNET; + + return CONNMAN_SESSION_TYPE_ANY; +} + static const char *roamingpolicy2string(enum connman_session_roaming_policy policy) { switch (policy) { case CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN: - break; + return "unknown"; case CONNMAN_SESSION_ROAMING_POLICY_DEFAULT: return "default"; case CONNMAN_SESSION_ROAMING_POLICY_ALWAYS: @@ -130,8 +249,10 @@ static enum connman_service_type bearer2service(const char *bearer) return CONNMAN_SERVICE_TYPE_WIMAX; else if (g_strcmp0(bearer, "bluetooth") == 0) return CONNMAN_SERVICE_TYPE_BLUETOOTH; - else if (g_strcmp0(bearer, "3g") == 0) + else if (g_strcmp0(bearer, "cellular") == 0) return CONNMAN_SERVICE_TYPE_CELLULAR; + else if (g_strcmp0(bearer, "vpn") == 0) + return CONNMAN_SERVICE_TYPE_VPN; else return CONNMAN_SERVICE_TYPE_UNKNOWN; } @@ -148,11 +269,12 @@ static char *service2bearer(enum connman_service_type type) case CONNMAN_SERVICE_TYPE_BLUETOOTH: return "bluetooth"; case CONNMAN_SERVICE_TYPE_CELLULAR: - return "3g"; + return "cellular"; + case CONNMAN_SERVICE_TYPE_VPN: + return "vpn"; case CONNMAN_SERVICE_TYPE_UNKNOWN: case CONNMAN_SERVICE_TYPE_SYSTEM: case CONNMAN_SERVICE_TYPE_GPS: - case CONNMAN_SERVICE_TYPE_VPN: case CONNMAN_SERVICE_TYPE_GADGET: return ""; } @@ -160,36 +282,6 @@ static char *service2bearer(enum connman_service_type type) return ""; } -static char *session2bearer(struct connman_session *session) -{ - struct session_info *info = &session->info; - GSList *list; - struct bearer_info *bearer_info; - enum connman_service_type type; - - if (info->service == NULL) - return ""; - - type = connman_service_get_type(info->service); - - for (list = info->allowed_bearers; - list != NULL; list = list->next) { - bearer_info = list->data; - - if (bearer_info->match_all) - return service2bearer(type); - - if (bearer_info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN) - return bearer_info->name; - - if (bearer_info->service_type == type) - return service2bearer(type); - } - - return ""; - -} - static void cleanup_bearer_info(gpointer data, gpointer user_data) { struct bearer_info *info = data; @@ -280,6 +372,11 @@ static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data) if (service == NULL) return; + if (__connman_service_is_connected_state(service, + CONNMAN_IPCONFIG_TYPE_IPV4) == FALSE) { + return; + } + ipconfig_ipv4 = __connman_service_get_ip4config(service); if (ipconfig_ipv4 == NULL) return; @@ -295,6 +392,11 @@ static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data) if (service == NULL) return; + if (__connman_service_is_connected_state(service, + CONNMAN_IPCONFIG_TYPE_IPV6) == FALSE) { + return; + } + ipconfig_ipv4 = __connman_service_get_ip4config(service); ipconfig_ipv6 = __connman_service_get_ip6config(service); if (ipconfig_ipv6 == NULL) @@ -306,51 +408,67 @@ static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data) static void append_notify(DBusMessageIter *dict, struct connman_session *session) { - struct session_info *info = &session->info; - struct session_info *info_last = &session->info_last; + struct session_info *info = session->info; + struct session_info *info_last = session->info_last; const char *policy; + struct connman_service *service; + const char *name, *ifname, *bearer; if (session->append_all == TRUE || - info->bearer != info_last->bearer) { - connman_dbus_dict_append_basic(dict, "Bearer", + info->state != info_last->state) { + const char *state = state2string(info->state); + + connman_dbus_dict_append_basic(dict, "State", DBUS_TYPE_STRING, - &info->bearer); - info_last->bearer = info->bearer; + &state); + info_last->state = info->state; } if (session->append_all == TRUE || - info->online != info_last->online) { - connman_dbus_dict_append_basic(dict, "Online", - DBUS_TYPE_BOOLEAN, - &info->online); - info_last->online = info->online; - } + info->entry != info_last->entry) { + if (info->entry == NULL) { + name = ""; + ifname = ""; + service = NULL; + bearer = ""; + } else { + name = info->entry->name; + ifname = info->entry->ifname; + service = info->entry->service; + bearer = info->entry->bearer; + } - if (session->append_all == TRUE || - info->name != info_last->name) { connman_dbus_dict_append_basic(dict, "Name", DBUS_TYPE_STRING, - &info->name); - info_last->name = info->name; - } + &name); - if (session->append_all == TRUE || - info->service != info_last->service) { connman_dbus_dict_append_dict(dict, "IPv4", append_ipconfig_ipv4, - info->service); + service); connman_dbus_dict_append_dict(dict, "IPv6", append_ipconfig_ipv6, - info->service); + service); connman_dbus_dict_append_basic(dict, "Interface", DBUS_TYPE_STRING, - &info->ifname); + &ifname); - info_last->service = info->service; + connman_dbus_dict_append_basic(dict, "Bearer", + DBUS_TYPE_STRING, + &bearer); + + info_last->entry = info->entry; } + if (session->append_all == TRUE || info->type != info_last->type) { + const char *type = type2string(info->type); + + connman_dbus_dict_append_basic(dict, "ConnectionType", + DBUS_TYPE_STRING, + &type); + info_last->type = info->type; + } if (session->append_all == TRUE || info->priority != info_last->priority) { @@ -427,16 +545,68 @@ static void append_notify(DBusMessageIter *dict, } session->append_all = FALSE; - session->info_dirty = FALSE; } -static int session_notify(struct connman_session *session) +static connman_bool_t is_type_matching_state(enum connman_session_state *state, + enum connman_session_type type) { + switch (type) { + case CONNMAN_SESSION_TYPE_ANY: + return TRUE; + case CONNMAN_SESSION_TYPE_LOCAL: + if (*state >= CONNMAN_SESSION_STATE_CONNECTED) { + *state = CONNMAN_SESSION_STATE_CONNECTED; + return TRUE; + } + + break; + case CONNMAN_SESSION_TYPE_INTERNET: + if (*state == CONNMAN_SESSION_STATE_ONLINE) + return TRUE; + break; + } + + return FALSE; +} + +static connman_bool_t compute_notifiable_changes(struct connman_session *session) +{ + struct session_info *info_last = session->info_last; + struct session_info *info = session->info; + + if (session->append_all == TRUE) + return TRUE; + + if (info->state != info_last->state) + return TRUE; + + if (info->entry != info_last->entry && + info->state >= CONNMAN_SESSION_STATE_CONNECTED) + return TRUE; + + if (info->periodic_connect != info_last->periodic_connect || + info->allowed_bearers != info_last->allowed_bearers || + info->avoid_handover != info_last->avoid_handover || + info->stay_connected != info_last->stay_connected || + info->roaming_policy != info_last->roaming_policy || + info->idle_timeout != info_last->idle_timeout || + info->priority != info_last->priority || + info->marker != info_last->marker || + info->ecall != info_last->ecall || + info->type != info_last->type) + return TRUE; + + return FALSE; +} + +static gboolean session_notify(gpointer user_data) +{ + struct connman_session *session = user_data; DBusMessage *msg; DBusMessageIter array, dict; - if (session->info_dirty == FALSE) - return 0; + if (compute_notifiable_changes(session) == FALSE) + return FALSE; DBG("session %p owner %s notify_path %s", session, session->owner, session->notify_path); @@ -445,7 +615,7 @@ static int session_notify(struct connman_session *session) CONNMAN_NOTIFICATION_INTERFACE, "Update"); if (msg == NULL) - return -ENOMEM; + return FALSE; dbus_message_iter_init_append(msg, &array); connman_dbus_dict_open(&array, &dict); @@ -456,33 +626,31 @@ static int session_notify(struct connman_session *session) g_dbus_send_message(connection, msg); - session->info_dirty = FALSE; - - return 0; + return FALSE; } static void ipconfig_ipv4_changed(struct connman_session *session) { - struct session_info *info = &session->info; + struct session_info *info = session->info; connman_dbus_setting_changed_dict(session->owner, session->notify_path, "IPv4", append_ipconfig_ipv4, - info->service); + info->entry->service); } static void ipconfig_ipv6_changed(struct connman_session *session) { - struct session_info *info = &session->info; + struct session_info *info = session->info; connman_dbus_setting_changed_dict(session->owner, session->notify_path, "IPv6", append_ipconfig_ipv6, - info->service); + info->entry->service); } static connman_bool_t service_type_match(struct connman_session *session, struct connman_service *service) { - struct session_info *info = &session->info; + struct session_info *info = session->info; GSList *list; for (list = info->allowed_bearers; @@ -520,7 +688,7 @@ static int service_type_weight(enum connman_service_type type) * 1. Ethernet * 2. Bluetooth * 3. WiFi/WiMAX - * 4. GSM/UTMS/3G + * 4. Cellular */ switch (type) { @@ -548,7 +716,7 @@ static gint sort_allowed_bearers(struct connman_service *service_a, struct connman_service *service_b, struct connman_session *session) { - struct session_info *info = &session->info; + struct session_info *info = session->info; GSList *list; enum connman_service_type type_a, type_b; int weight_a, weight_b; @@ -596,208 +764,564 @@ static gint sort_allowed_bearers(struct connman_service *service_a, static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data) { - struct connman_service *service_a = (void *)a; - struct connman_service *service_b = (void *)b; + struct service_entry *entry_a = (void *)a; + struct service_entry *entry_b = (void *)b; struct connman_session *session = user_data; - return sort_allowed_bearers(service_a, service_b, session); + return sort_allowed_bearers(entry_a->service, entry_b->service, + session); } static void cleanup_session(gpointer user_data) { struct connman_session *session = user_data; - struct session_info *info = &session->info; + struct session_info *info = session->info; DBG("remove %s", session->session_path); + g_hash_table_destroy(session->service_hash); g_sequence_free(session->service_list); + if (info->entry != NULL && + info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) { + __connman_service_disconnect(info->entry->service); + } + g_slist_foreach(info->allowed_bearers, cleanup_bearer_info, NULL); g_slist_free(info->allowed_bearers); g_free(session->owner); g_free(session->session_path); g_free(session->notify_path); + g_free(session->info); + g_free(session->info_last); g_free(session); } -static void release_session(gpointer key, gpointer value, gpointer user_data) +static enum connman_session_state service_to_session_state(enum connman_service_state state) { - struct connman_session *session = value; - DBusMessage *message; + switch (state) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_DISCONNECT: + case CONNMAN_SERVICE_STATE_FAILURE: + break; + case CONNMAN_SERVICE_STATE_READY: + return CONNMAN_SESSION_STATE_CONNECTED; + case CONNMAN_SERVICE_STATE_ONLINE: + return CONNMAN_SESSION_STATE_ONLINE; + } - DBG("owner %s path %s", session->owner, session->notify_path); + return CONNMAN_SESSION_STATE_DISCONNECTED; +} - if (session->notify_watch > 0) - g_dbus_remove_watch(connection, session->notify_watch); +static connman_bool_t is_connected(enum connman_service_state state) +{ + switch (state) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_DISCONNECT: + case CONNMAN_SERVICE_STATE_FAILURE: + break; + case CONNMAN_SERVICE_STATE_READY: + case CONNMAN_SERVICE_STATE_ONLINE: + return TRUE; + } - g_dbus_unregister_interface(connection, session->session_path, - CONNMAN_SESSION_INTERFACE); + return FALSE; +} - message = dbus_message_new_method_call(session->owner, - session->notify_path, - CONNMAN_NOTIFICATION_INTERFACE, - "Release"); - if (message == NULL) - return; +static connman_bool_t is_connecting(enum connman_service_state state) +{ + switch (state) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + break; + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + return TRUE; + case CONNMAN_SERVICE_STATE_DISCONNECT: + case CONNMAN_SERVICE_STATE_FAILURE: + case CONNMAN_SERVICE_STATE_READY: + case CONNMAN_SERVICE_STATE_ONLINE: + break; + } - dbus_message_set_no_reply(message, TRUE); + return FALSE; +} - g_dbus_send_message(connection, message); +static connman_bool_t explicit_connect(enum connman_session_reason reason) +{ + switch (reason) { + case CONNMAN_SESSION_REASON_UNKNOWN: + case CONNMAN_SESSION_REASON_FREE_RIDE: + case CONNMAN_SESSION_REASON_DISCONNECT: + break; + case CONNMAN_SESSION_REASON_CONNECT: + case CONNMAN_SESSION_REASON_PERIODIC: + return TRUE; + } + + return FALSE; } -static int session_disconnect(struct connman_session *session) +static connman_bool_t explicit_disconnect(struct session_info *info) { - DBG("session %p, %s", session, session->owner); + if (info->entry == NULL) + return FALSE; - if (session->notify_watch > 0) - g_dbus_remove_watch(connection, session->notify_watch); + DBG("reason %s service %p state %d", + reason2string(info->entry->reason), + info->entry->service, info->entry->state); - g_dbus_unregister_interface(connection, session->session_path, - CONNMAN_SESSION_INTERFACE); + if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN) + return FALSE; - g_hash_table_remove(session_hash, session->session_path); + if (explicit_connect(info->entry->reason) == FALSE) + return FALSE; - return 0; + if (__connman_service_session_dec(info->entry->service) == FALSE) + return FALSE; + + if (ecall_info != NULL && ecall_info != info) + return FALSE; + + return TRUE; } -static void owner_disconnect(DBusConnection *conn, void *user_data) +struct pending_data { + unsigned int timeout; + struct service_entry *entry; + gboolean (*cb)(gpointer); +}; + +static void pending_timeout_free(gpointer data, gpointer user_data) { - struct connman_session *session = user_data; + struct pending_data *pending = data; - DBG("session %p, %s died", session, session->owner); + DBG("pending %p timeout %d", pending, pending->timeout); + g_source_remove(pending->timeout); + g_free(pending); +} - session_disconnect(session); +static void pending_timeout_remove_all(struct service_entry *entry) +{ + DBG(""); + + g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL); + g_slist_free(entry->pending_timeouts); + entry->pending_timeouts = NULL; } -static DBusMessage *destroy_session(DBusConnection *conn, - DBusMessage *msg, void *user_data) +static gboolean pending_timeout_cb(gpointer data) { - struct connman_session *session = user_data; + struct pending_data *pending = data; + struct service_entry *entry = pending->entry; + gboolean ret; + + DBG("pending %p timeout %d", pending, pending->timeout); + + ret = pending->cb(pending->entry); + if (ret == FALSE) { + entry->pending_timeouts = + g_slist_remove(entry->pending_timeouts, + pending); + g_free(pending); + } + return ret; +} - DBG("session %p", session); +static connman_bool_t pending_timeout_add(unsigned int seconds, + gboolean (*cb)(gpointer), + struct service_entry *entry) +{ + struct pending_data *pending = g_try_new0(struct pending_data, 1); - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); + if (pending == NULL || cb == NULL || entry == NULL) { + g_free(pending); + return FALSE; + } + + pending->cb = cb; + pending->entry = entry; + pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb, + pending); + entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts, + pending); + + DBG("pending %p entry %p timeout id %d", pending, entry, + pending->timeout); + + return TRUE; } -static gboolean session_changed_connect(gpointer user_data) +static gboolean call_disconnect(gpointer user_data) { - struct connman_session *session = user_data; - struct session_info *info = &session->info; + struct service_entry *entry = user_data; + struct connman_service *service = entry->service; - __connman_service_connect(info->service); + /* + * TODO: We should mark this entry as pending work. In case + * disconnect fails we just unassign this session from the + * service and can't do anything later on it + */ + DBG("disconnect service %p", service); + __connman_service_disconnect(service); return FALSE; } -static void update_service(struct connman_session *session) +static gboolean call_connect(gpointer user_data) { - struct session_info *info = &session->info; - int idx; + struct service_entry *entry = user_data; + struct connman_service *service = entry->service; - if (info->service != NULL) { - info->bearer = session2bearer(session); - info->online = __connman_service_is_connected(info->service); - info->name = __connman_service_get_name(info->service); - idx = __connman_service_get_index(info->service); - info->ifname = connman_inet_ifname(idx); + DBG("connect service %p", service); + __connman_service_connect(service); - } else { - info->bearer = ""; - info->online = FALSE; - info->name = ""; - info->ifname = ""; + return FALSE; +} + +static void deselect_service(struct session_info *info) +{ + struct service_entry *entry; + connman_bool_t disconnect, connected; + + DBG(""); + + if (info->entry == NULL) + return; + + disconnect = explicit_disconnect(info); + + connected = is_connecting(info->entry->state) == TRUE || + is_connected(info->entry->state) == TRUE; + + info->state = CONNMAN_SESSION_STATE_DISCONNECTED; + info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN; + + entry = info->entry; + info->entry = NULL; + + DBG("disconnect %d connected %d", disconnect, connected); + + if (disconnect == TRUE && connected == TRUE) + pending_timeout_add(0, call_disconnect, entry); +} + +static void deselect_and_disconnect(struct connman_session *session, + enum connman_session_reason reason) +{ + struct session_info *info = session->info; + + deselect_service(info); + + info->reason = reason; +} + +static void select_connected_service(struct session_info *info, + struct service_entry *entry) +{ + enum connman_session_state state; + + state = service_to_session_state(entry->state); + if (is_type_matching_state(&state, info->type) == FALSE) + return; + + info->state = state; + + info->entry = entry; + info->entry->reason = info->reason; + + if (explicit_connect(info->reason) == FALSE) + return; + + __connman_service_session_inc(info->entry->service); +} + +static void select_offline_service(struct session_info *info, + struct service_entry *entry) +{ + if (explicit_connect(info->reason) == FALSE) + return; + + info->state = service_to_session_state(entry->state); + + info->entry = entry; + info->entry->reason = info->reason; + + __connman_service_session_inc(info->entry->service); + pending_timeout_add(0, call_connect, entry); +} + +static void select_service(struct session_info *info, + struct service_entry *entry) +{ + DBG("service %p", entry->service); + + if (is_connected(entry->state) == TRUE) + select_connected_service(info, entry); + else + select_offline_service(info, entry); +} + +static void select_and_connect(struct connman_session *session, + enum connman_session_reason reason) +{ + struct session_info *info = session->info; + struct service_entry *entry = NULL; + GSequenceIter *iter; + + DBG("session %p reason %s", session, reason2string(reason)); + + info->reason = reason; + + iter = g_sequence_get_begin_iter(session->service_list); + + while (g_sequence_iter_is_end(iter) == FALSE) { + entry = g_sequence_get(iter); + + switch (entry->state) { + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_READY: + case CONNMAN_SERVICE_STATE_ONLINE: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_DISCONNECT: + select_service(info, entry); + return; + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_FAILURE: + break; + } + + iter = g_sequence_iter_next(iter); } } -static void session_changed(struct connman_session *session) +static struct service_entry *create_service_entry(struct connman_service *service, + const char *name, + enum connman_service_state state) +{ + struct service_entry *entry; + enum connman_service_type type; + int idx; + + entry = g_try_new0(struct service_entry, 1); + if (entry == NULL) + return entry; + + entry->reason = CONNMAN_SESSION_REASON_UNKNOWN; + entry->state = state; + if (name != NULL) + entry->name = name; + else + entry->name = ""; + entry->service = service; + + idx = __connman_service_get_index(entry->service); + entry->ifname = connman_inet_ifname(idx); + if (entry->ifname == NULL) + entry->ifname = g_strdup(""); + + type = connman_service_get_type(entry->service); + entry->bearer = service2bearer(type); + + return entry; +} + +static void destroy_service_entry(gpointer data) +{ + struct service_entry *entry = data; + + pending_timeout_remove_all(entry); + g_free(entry->ifname); + + g_free(entry); +} + +static void populate_service_list(struct connman_session *session) { - struct session_info *info = &session->info; - struct session_info *info_last = &session->info_last; - struct connman_service *service = NULL; - GSourceFunc callback = NULL; + struct service_entry *entry; GSequenceIter *iter; + session->service_hash = + g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, NULL); + session->service_list = __connman_service_get_list(session, + service_match, + create_service_entry, + destroy_service_entry); + + g_sequence_sort(session->service_list, sort_services, session); + + iter = g_sequence_get_begin_iter(session->service_list); + + while (g_sequence_iter_is_end(iter) == FALSE) { + entry = g_sequence_get(iter); + + DBG("service %p type %s name %s", entry->service, + service2bearer(connman_service_get_type(entry->service)), + entry->name); + + g_hash_table_replace(session->service_hash, + entry->service, iter); + + iter = g_sequence_iter_next(iter); + } +} + +static void session_changed(struct connman_session *session, + enum connman_session_trigger trigger) +{ + struct session_info *info = session->info; + struct session_info *info_last = session->info_last; + GSequenceIter *service_iter = NULL, *service_iter_last = NULL; + GSequence *service_list_last; + GHashTable *service_hash_last; + /* * TODO: This only a placeholder for the 'real' algorithm to * play a bit around. So we are going to improve it step by step. */ - if (info->ecall == TRUE && session != ecall_session) - goto out; + DBG("session %p trigger %s reason %s", session, trigger2string(trigger), + reason2string(info->reason)); + + if (info->entry != NULL) { + enum connman_session_state state; - if (info->connect == FALSE) { - if (info->service != NULL) - __connman_service_disconnect(info->service); - info->service = NULL; - goto out; + state = service_to_session_state(info->entry->state); + + if (is_type_matching_state(&state, info->type) == TRUE) + info->state = state; } - iter = g_sequence_get_begin_iter(session->service_list); + switch (trigger) { + case CONNMAN_SESSION_TRIGGER_UNKNOWN: + DBG("ignore session changed event"); + return; + case CONNMAN_SESSION_TRIGGER_SETTING: + if (info->allowed_bearers != info_last->allowed_bearers) { - while (g_sequence_iter_is_end(iter) == FALSE) { - service = g_sequence_get(iter); + service_hash_last = session->service_hash; + service_list_last = session->service_list; + + populate_service_list(session); + + if (info->entry != NULL) { + service_iter_last = g_hash_table_lookup( + service_hash_last, + info->entry->service); + service_iter = g_hash_table_lookup( + session->service_hash, + info->entry->service); + } - if (__connman_service_is_connecting(service) == TRUE || - __connman_service_is_connected(service) == TRUE) { + if (service_iter == NULL && service_iter_last != NULL) { + /* + * The currently selected service is + * not part of this session anymore. + */ + deselect_and_disconnect(session, info->reason); + } + + g_hash_table_remove_all(service_hash_last); + g_sequence_free(service_list_last); + } + + if (info->type != info_last->type) { + if (info->state >= CONNMAN_SESSION_STATE_CONNECTED && + is_type_matching_state(&info->state, + info->type) == FALSE) + deselect_and_disconnect(session, info->reason); + } + + if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) { + select_and_connect(session, + CONNMAN_SESSION_REASON_FREE_RIDE); + } + + break; + case CONNMAN_SESSION_TRIGGER_CONNECT: + if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) { + if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) + break; + info->entry->reason = CONNMAN_SESSION_REASON_CONNECT; + __connman_service_session_inc(info->entry->service); break; } - if (__connman_service_is_idle(service) == TRUE && - info->connect == TRUE) { - callback = session_changed_connect; + if (info->entry != NULL && + is_connecting(info->entry->state) == TRUE) { break; } - service = NULL; + select_and_connect(session, + CONNMAN_SESSION_REASON_CONNECT); - iter = g_sequence_iter_next(iter); - } + break; + case CONNMAN_SESSION_TRIGGER_DISCONNECT: + deselect_and_disconnect(session, + CONNMAN_SESSION_REASON_DISCONNECT); - if (info->service != NULL && info->service != service) { - __connman_service_disconnect(info->service); - info->service = NULL; - } + break; + case CONNMAN_SESSION_TRIGGER_PERIODIC: + if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) { + info->entry->reason = CONNMAN_SESSION_REASON_PERIODIC; + __connman_service_session_inc(info->entry->service); + break; + } - if (service != NULL) { - info->service = service; + select_and_connect(session, + CONNMAN_SESSION_REASON_PERIODIC); - if (callback != NULL) - callback(session); - } + break; + case CONNMAN_SESSION_TRIGGER_SERVICE: + if (info->entry != NULL && + (is_connecting(info->entry->state) == TRUE || + is_connected(info->entry->state) == TRUE)) { + break; + } -out: - if (info->service != info_last->service) - update_service(session); -} + deselect_and_disconnect(session, info->reason); -static gboolean session_cb(gpointer user_data) -{ - struct connman_session *session = user_data; + if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE || + info->stay_connected == TRUE) { + select_and_connect(session, info->reason); + } - session_changed(session); - session_notify(session); + break; + case CONNMAN_SESSION_TRIGGER_ECALL: + if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED && + info->entry != NULL && + info->entry->service != NULL) { + deselect_and_disconnect(session, info->reason); + } - return FALSE; + break; + } + + session_notify(session); } static DBusMessage *connect_session(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct connman_session *session = user_data; - struct session_info *info = &session->info; + struct session_info *info = session->info; DBG("session %p", session); - info->connect = TRUE; - - if (ecall_session != NULL && ecall_session != session) + if (ecall_info != NULL && ecall_info != info) return __connman_error_failed(msg, EBUSY); - session->info_dirty = TRUE; - - g_timeout_add_seconds(0, session_cb, session); + session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -806,47 +1330,21 @@ static DBusMessage *disconnect_session(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct connman_session *session = user_data; - struct session_info *info = &session->info; + struct session_info *info = session->info; DBG("session %p", session); - info->connect = FALSE; - - if (ecall_session != NULL && ecall_session != session) + if (ecall_info != NULL && ecall_info != info) return __connman_error_failed(msg, EBUSY); - session->info_dirty = TRUE; - - g_timeout_add_seconds(0, session_cb, session); + session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } -static void print_name(gpointer data, gpointer user_data) -{ - struct connman_service *service = data; - - DBG("service %p type %s name %s", service, - service2bearer(connman_service_get_type(service)), - __connman_service_get_name(service)); -} - -static void update_allowed_bearers(struct connman_session *session) -{ - if (session->service_list != NULL) - g_sequence_free(session->service_list); - - session->service_list = __connman_service_get_list(session, - service_match); - g_sequence_sort(session->service_list, sort_services, session); - g_sequence_foreach(session->service_list, print_name, NULL); - - session->info_dirty = TRUE; -} - static void update_ecall_sessions(struct connman_session *session) { - struct session_info *info = &session->info; + struct session_info *info = session->info; struct connman_session *session_iter; GHashTableIter iter; gpointer key, value; @@ -859,38 +1357,36 @@ static void update_ecall_sessions(struct connman_session *session) if (session_iter == session) continue; - session_iter->info.ecall = info->ecall; - session_iter->info_dirty = TRUE; + session_iter->info->ecall = info->ecall; - g_timeout_add_seconds(0, session_cb, session_iter); + session_changed(session_iter, CONNMAN_SESSION_TRIGGER_ECALL); } } static void update_ecall(struct connman_session *session) { - struct session_info *info = &session->info; - struct session_info *info_last = &session->info_last; + struct session_info *info = session->info; + struct session_info *info_last = session->info_last; - DBG("ecall_session %p ecall %d -> %d", ecall_session, - info_last->ecall, info->ecall); + DBG("session %p ecall_info %p ecall %d -> %d", session, + ecall_info, info_last->ecall, info->ecall); - if (ecall_session == NULL) { + if (ecall_info == NULL) { if (!(info_last->ecall == FALSE && info->ecall == TRUE)) goto err; - ecall_session = session; - } else if (ecall_session == session) { + ecall_info = info; + } else if (ecall_info == info) { if (!(info_last->ecall == TRUE && info->ecall == FALSE)) goto err; - ecall_session = NULL; + ecall_info = NULL; } else { goto err; } update_ecall_sessions(session); - session->info_dirty = TRUE; return; err: @@ -902,10 +1398,10 @@ static DBusMessage *change_session(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct connman_session *session = user_data; - struct session_info *info = &session->info; - struct session_info *info_last = &session->info_last; + struct session_info *info = session->info; DBusMessageIter iter, value; const char *name; + const char *val; GSList *allowed_bearers; DBG("session %p", session); @@ -933,8 +1429,6 @@ static DBusMessage *change_session(DBusConnection *conn, } info->allowed_bearers = allowed_bearers; - - update_allowed_bearers(session); } else { goto err; } @@ -943,21 +1437,12 @@ static DBusMessage *change_session(DBusConnection *conn, if (g_str_equal(name, "Priority") == TRUE) { dbus_message_iter_get_basic(&value, &info->priority); - - if (info_last->priority != info->priority) - session->info_dirty = TRUE; } else if (g_str_equal(name, "AvoidHandover") == TRUE) { dbus_message_iter_get_basic(&value, &info->avoid_handover); - - if (info_last->avoid_handover != info->avoid_handover) - session->info_dirty = TRUE; - } else if (g_str_equal(name, "StayConnected") == TRUE) { + } else if (g_str_equal(name, "StayConnected") == TRUE) { dbus_message_iter_get_basic(&value, &info->stay_connected); - - if (info_last->stay_connected != info->stay_connected) - session->info_dirty = TRUE; } else if (g_str_equal(name, "EmergencyCall") == TRUE) { dbus_message_iter_get_basic(&value, &info->ecall); @@ -971,28 +1456,21 @@ static DBusMessage *change_session(DBusConnection *conn, if (g_str_equal(name, "PeriodicConnect") == TRUE) { dbus_message_iter_get_basic(&value, &info->periodic_connect); - - if (info_last->periodic_connect != info->periodic_connect) - session->info_dirty = TRUE; } else if (g_str_equal(name, "IdleTimeout") == TRUE) { dbus_message_iter_get_basic(&value, &info->idle_timeout); - - if (info_last->idle_timeout != info->idle_timeout) - session->info_dirty = TRUE; } else { goto err; } break; case DBUS_TYPE_STRING: - if (g_str_equal(name, "RoamingPolicy") == TRUE) { - const char *val; + if (g_str_equal(name, "ConnectionType") == TRUE) { + dbus_message_iter_get_basic(&value, &val); + info->type = string2type(val); + } else if (g_str_equal(name, "RoamingPolicy") == TRUE) { dbus_message_iter_get_basic(&value, &val); info->roaming_policy = string2roamingpolicy(val); - - if (info_last->roaming_policy != info->roaming_policy) - session->info_dirty = TRUE; } else { goto err; } @@ -1001,8 +1479,7 @@ static DBusMessage *change_session(DBusConnection *conn, goto err; } - if (session->info_dirty == TRUE) - session_cb(session); + session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); @@ -1010,11 +1487,82 @@ err: return __connman_error_invalid_arguments(msg); } -static GDBusMethodTable session_methods[] = { - { "Destroy", "", "", destroy_session }, - { "Connect", "", "", connect_session }, - { "Disconnect", "", "", disconnect_session }, - { "Change", "sv", "", change_session }, +static void release_session(gpointer key, gpointer value, gpointer user_data) +{ + struct connman_session *session = value; + DBusMessage *message; + + DBG("owner %s path %s", session->owner, session->notify_path); + + if (session->notify_watch > 0) + g_dbus_remove_watch(connection, session->notify_watch); + + g_dbus_unregister_interface(connection, session->session_path, + CONNMAN_SESSION_INTERFACE); + + message = dbus_message_new_method_call(session->owner, + session->notify_path, + CONNMAN_NOTIFICATION_INTERFACE, + "Release"); + if (message == NULL) + return; + + dbus_message_set_no_reply(message, TRUE); + + g_dbus_send_message(connection, message); +} + +static int session_disconnect(struct connman_session *session) +{ + DBG("session %p, %s", session, session->owner); + + if (session->notify_watch > 0) + g_dbus_remove_watch(connection, session->notify_watch); + + g_dbus_unregister_interface(connection, session->session_path, + CONNMAN_SESSION_INTERFACE); + + deselect_and_disconnect(session, + CONNMAN_SESSION_REASON_DISCONNECT); + + g_hash_table_remove(session_hash, session->session_path); + + return 0; +} + +static void owner_disconnect(DBusConnection *conn, void *user_data) +{ + struct connman_session *session = user_data; + + DBG("session %p, %s died", session, session->owner); + + session_disconnect(session); +} + +static DBusMessage *destroy_session(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct connman_session *session = user_data; + struct session_info *info = session->info; + + DBG("session %p", session); + + if (ecall_info != NULL && ecall_info != info) + return __connman_error_failed(msg, EBUSY); + + session_disconnect(session); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static const GDBusMethodTable session_methods[] = { + { GDBUS_METHOD("Destroy", NULL, NULL, destroy_session) }, + { GDBUS_METHOD("Connect", NULL, NULL, connect_session) }, + { GDBUS_METHOD("Disconnect", NULL, NULL, + disconnect_session ) }, + { GDBUS_METHOD("Change", + GDBUS_ARGS({ "name", "s" }, { "value", "v" }), + NULL, change_session) }, { }, }; @@ -1023,9 +1571,10 @@ int __connman_session_create(DBusMessage *msg) const char *owner, *notify_path; char *session_path = NULL; DBusMessageIter iter, array; - struct connman_session *session; + struct connman_session *session = NULL; struct session_info *info, *info_last; + enum connman_session_type type = CONNMAN_SESSION_TYPE_ANY; connman_bool_t priority = FALSE, avoid_handover = FALSE; connman_bool_t stay_connected = FALSE, ecall = FALSE; enum connman_session_roaming_policy roaming_policy = @@ -1040,7 +1589,7 @@ int __connman_session_create(DBusMessage *msg) DBG("owner %s", owner); - if (ecall_session != NULL) { + if (ecall_info != NULL) { /* * If there is an emergency call already going on, * ignore session creation attempt @@ -1100,7 +1649,10 @@ int __connman_session_create(DBusMessage *msg) } break; case DBUS_TYPE_STRING: - if (g_str_equal(key, "RoamingPolicy") == TRUE) { + if (g_str_equal(key, "ConnectionType") == TRUE) { + dbus_message_iter_get_basic(&value, &val); + type = string2type(val); + } else if (g_str_equal(key, "RoamingPolicy") == TRUE) { dbus_message_iter_get_basic(&value, &val); roaming_policy = string2roamingpolicy(val); } else { @@ -1126,6 +1678,7 @@ int __connman_session_create(DBusMessage *msg) session = g_hash_table_lookup(session_hash, session_path); if (session != NULL) { + session = NULL; err = -EEXIST; goto err; } @@ -1136,8 +1689,20 @@ int __connman_session_create(DBusMessage *msg) goto err; } - info = &session->info; - info_last = &session->info_last; + session->info = g_try_new0(struct session_info, 1); + if (session->info == NULL) { + err = -ENOMEM; + goto err; + } + + session->info_last = g_try_new0(struct session_info, 1); + if (session->info_last == NULL) { + err = -ENOMEM; + goto err; + } + + info = session->info; + info_last = session->info_last; session->owner = g_strdup(owner); session->session_path = session_path; @@ -1146,8 +1711,8 @@ int __connman_session_create(DBusMessage *msg) g_dbus_add_disconnect_watch(connection, session->owner, owner_disconnect, session, NULL); - info->bearer = ""; - info->online = FALSE; + info->state = CONNMAN_SESSION_STATE_DISCONNECTED; + info->type = type; info->priority = priority; info->avoid_handover = avoid_handover; info->stay_connected = stay_connected; @@ -1155,7 +1720,7 @@ int __connman_session_create(DBusMessage *msg) info->idle_timeout = idle_timeout; info->ecall = ecall; info->roaming_policy = roaming_policy; - info->service = NULL; + info->entry = NULL; info->marker = 0; if (allowed_bearers == NULL) { @@ -1191,34 +1756,41 @@ int __connman_session_create(DBusMessage *msg) DBUS_TYPE_INVALID); - update_allowed_bearers(session); - update_service(session); + populate_service_list(session); if (info->ecall == TRUE) { - ecall_session = session; + ecall_info = info; update_ecall_sessions(session); } - info_last->bearer = info->bearer; - info_last->online = info->priority; + info_last->state = info->state; + info_last->priority = info->priority; info_last->avoid_handover = info->avoid_handover; info_last->stay_connected = info->stay_connected; info_last->periodic_connect = info->periodic_connect; info_last->idle_timeout = info->idle_timeout; info_last->ecall = info->ecall; info_last->roaming_policy = info->roaming_policy; - info_last->service = info->service; + info_last->entry = info->entry; info_last->marker = info->marker; info_last->allowed_bearers = info->allowed_bearers; - session->info_dirty = TRUE; session->append_all = TRUE; - g_timeout_add_seconds(0, session_cb, session); + session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING); return 0; err: connman_error("Failed to create session"); + + if (session != NULL) { + if (session->info_last != NULL) + g_free(session->info_last); + if (session->info != NULL) + g_free(session->info); + g_free(session); + } + g_free(session_path); g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL); @@ -1262,20 +1834,26 @@ void __connman_session_set_mode(connman_bool_t enable) { DBG("enable %d", enable); - if (sessionmode == enable) - return; + if (sessionmode != enable) { + sessionmode = enable; - sessionmode = enable; + connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH, + CONNMAN_MANAGER_INTERFACE, "SessionMode", + DBUS_TYPE_BOOLEAN, &sessionmode); + } if (sessionmode == TRUE) __connman_service_disconnect_all(); } -static void service_add(struct connman_service *service) +static void service_add(struct connman_service *service, + const char *name) { GHashTableIter iter; + GSequenceIter *iter_service_list; gpointer key, value; struct connman_session *session; + struct service_entry *entry; DBG("service %p", service); @@ -1287,20 +1865,21 @@ static void service_add(struct connman_service *service) if (service_match(session, service) == FALSE) continue; - g_sequence_insert_sorted(session->service_list, service, - sort_services, session); + entry = create_service_entry(service, name, + CONNMAN_SERVICE_STATE_IDLE); + if (entry == NULL) + continue; - session_cb(session); - } -} + iter_service_list = + g_sequence_insert_sorted(session->service_list, + entry, sort_services, + session); -static gint service_in_session(gconstpointer a, gconstpointer b, - gpointer user_data) -{ - if (a == b) - return 0; + g_hash_table_replace(session->service_hash, service, + iter_service_list); - return -1; + session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE); + } } static void service_remove(struct connman_service *service) @@ -1308,35 +1887,27 @@ static void service_remove(struct connman_service *service) GHashTableIter iter; gpointer key, value; - GSequenceIter *seq_iter; struct connman_session *session; struct session_info *info; - struct connman_service *found_service; DBG("service %p", service); g_hash_table_iter_init(&iter, session_hash); while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { + GSequenceIter *iter; session = value; - info = &session->info; + info = session->info; - if (session->service_list == NULL) + iter = g_hash_table_lookup(session->service_hash, service); + if (iter == NULL) continue; - seq_iter = g_sequence_search(session->service_list, service, - service_in_session, NULL); - if (g_sequence_iter_is_end(seq_iter) == TRUE) - continue; - - g_sequence_remove(seq_iter); - - found_service = g_sequence_get(seq_iter); - if (found_service != info->service) - continue; + g_sequence_remove(iter); - info->service = NULL; - session_cb(session); + if (info->entry != NULL && info->entry->service == service) + info->entry = NULL; + session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE); } } @@ -1345,26 +1916,25 @@ static void service_state_changed(struct connman_service *service, { GHashTableIter iter; gpointer key, value; - struct connman_session *session; - struct session_info *info; - connman_bool_t online; DBG("service %p state %d", service, state); g_hash_table_iter_init(&iter, session_hash); while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - session = value; - info = &session->info; + struct connman_session *session = value; + GSequenceIter *service_iter; - if (info->service == service) { - online = __connman_service_is_connected(service); - if (info->online == online) - continue; + service_iter = g_hash_table_lookup(session->service_hash, service); + if (service_iter != NULL) { + struct service_entry *entry; - info->online = online; - session_cb(session); + entry = g_sequence_get(service_iter); + entry->state = state; } + + session_changed(session, + CONNMAN_SESSION_TRIGGER_SERVICE); } } @@ -1385,9 +1955,12 @@ static void ipconfig_changed(struct connman_service *service, while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { session = value; - info = &session->info; + info = session->info; + + if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) + continue; - if (info->service == service) { + if (info->entry != NULL && info->entry->service == service) { if (type == CONNMAN_IPCONFIG_TYPE_IPV4) ipconfig_ipv4_changed(session); else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) @@ -1438,6 +2011,7 @@ void __connman_session_cleanup(void) g_hash_table_foreach(session_hash, release_session, NULL); g_hash_table_destroy(session_hash); + session_hash = NULL; dbus_connection_unref(connection); }