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,
enum connman_session_reason {
CONNMAN_SESSION_REASON_UNKNOWN = 0,
CONNMAN_SESSION_REASON_CONNECT = 1,
- CONNMAN_SESSION_REASON_FREE_RIDE = 2,
- CONNMAN_SESSION_REASON_PERIODIC = 3,
+ CONNMAN_SESSION_REASON_DISCONNECT = 2,
+ CONNMAN_SESSION_REASON_FREE_RIDE = 3,
+ CONNMAN_SESSION_REASON_PERIODIC = 4,
};
enum connman_session_roaming_policy {
enum connman_service_state state;
const char *name;
struct connman_service *service;
- const char *ifname;
+ char *ifname;
const char *bearer;
};
unsigned int marker;
struct service_entry *entry;
+ enum connman_session_reason reason;
};
struct connman_session {
{
switch (reason) {
case CONNMAN_SESSION_REASON_UNKNOWN:
- break;
+ 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 CONNMAN_SERVICE_TYPE_BLUETOOTH;
else if (g_strcmp0(bearer, "3g") == 0)
return CONNMAN_SERVICE_TYPE_CELLULAR;
+ else if (g_strcmp0(bearer, "vpn") == 0)
+ return CONNMAN_SERVICE_TYPE_VPN;
else
return CONNMAN_SERVICE_TYPE_UNKNOWN;
}
return "bluetooth";
case CONNMAN_SERVICE_TYPE_CELLULAR:
return "3g";
+ 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 "";
}
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)
g_free(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);
-
- g_hash_table_remove(session_hash, session->session_path);
-
- return 0;
-}
-
-static void owner_disconnect(DBusConnection *conn, void *user_data)
+static connman_bool_t is_online(enum connman_service_state state)
{
- 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;
-
- DBG("session %p", session);
+ 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:
+ case CONNMAN_SERVICE_STATE_READY:
+ break;
+ case CONNMAN_SERVICE_STATE_ONLINE:
+ return TRUE;
+ }
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ return FALSE;
}
-static connman_bool_t is_connected(enum connman_service_state state)
+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:
+ case CONNMAN_SERVICE_STATE_READY:
+ return TRUE;
case CONNMAN_SERVICE_STATE_DISCONNECT:
case CONNMAN_SERVICE_STATE_FAILURE:
- break;
- case CONNMAN_SERVICE_STATE_READY:
case CONNMAN_SERVICE_STATE_ONLINE:
- return TRUE;
+ break;
}
return FALSE;
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 FALSE;
}
-static void test_and_disconnect(struct connman_session *session)
+static connman_bool_t explicit_disconnect(struct session_info *info)
{
- struct session_info *info = session->info;
-
if (info->entry == NULL)
- return;
+ return FALSE;
- DBG("session %p reason %s service %p state %d",
- session, reason2string(info->entry->reason),
+ DBG("reason %s service %p state %d",
+ reason2string(info->entry->reason),
info->entry->service, info->entry->state);
+ if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN)
+ return FALSE;
+
if (explicit_connect(info->entry->reason) == FALSE)
- goto out;
+ return FALSE;
+
+ if (__connman_service_session_dec(info->entry->service) == FALSE)
+ return FALSE;
- if (__connman_service_session_dec(info->entry->service) == TRUE)
- goto out;
+ if (ecall_info != NULL && ecall_info != info)
+ return FALSE;
- if (ecall_session != NULL && ecall_session != session)
- goto out;
+ return TRUE;
+}
- __connman_service_disconnect(info->entry->service);
+static gboolean call_disconnect(gpointer user_data)
+{
+ struct connman_service *service = user_data;
/*
* 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 gboolean call_connect(gpointer user_data)
+{
+ struct connman_service *service = user_data;
+
+ DBG("connect service %p", service);
+ __connman_service_connect(service);
+
+ return FALSE;
+}
+
+static void deselect_service(struct session_info *info)
+{
+ struct connman_service *service;
+ connman_bool_t disconnect, online;
+
+ DBG("");
+
+ if (info->entry == NULL)
+ return;
+
+ disconnect = explicit_disconnect(info);
+
+ online = is_connecting(info->entry->state) == TRUE ||
+ is_online(info->entry->state) == TRUE;
-out:
info->online = FALSE;
+ info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
+
+ service = info->entry->service;
info->entry = NULL;
+
+ DBG("disconnect %d online %d", disconnect, online);
+
+ if (disconnect == TRUE && online == TRUE)
+ g_timeout_add_seconds(0, call_disconnect, service);
+}
+
+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_online_service(struct session_info *info,
+ struct service_entry *entry)
+{
+ info->online = TRUE;
+
+ 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->online = FALSE;
+
+ info->entry = entry;
+ info->entry->reason = info->reason;
+
+ __connman_service_session_inc(info->entry->service);
+ g_timeout_add_seconds(0, call_connect, info->entry->service);
+}
+
+static void select_service(struct session_info *info,
+ struct service_entry *entry)
+{
+ DBG("service %p", entry->service);
+
+ if (is_online(entry->state) == TRUE)
+ select_online_service(info, entry);
+ else
+ select_offline_service(info, entry);
}
static void select_and_connect(struct connman_session *session,
struct session_info *info = session->info;
struct service_entry *entry = NULL;
GSequenceIter *iter;
- connman_bool_t do_connect = FALSE;
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) {
case CONNMAN_SERVICE_STATE_CONFIGURATION:
case CONNMAN_SERVICE_STATE_READY:
case CONNMAN_SERVICE_STATE_ONLINE:
- /* connecting or connected */
- break;
case CONNMAN_SERVICE_STATE_IDLE:
case CONNMAN_SERVICE_STATE_DISCONNECT:
- if (explicit_connect(reason) == TRUE)
- do_connect = TRUE;
- break;
+ select_service(info, entry);
+ return;
case CONNMAN_SERVICE_STATE_UNKNOWN:
case CONNMAN_SERVICE_STATE_FAILURE:
- entry = NULL;
break;
}
- if (entry != NULL)
- break;
-
iter = g_sequence_iter_next(iter);
}
-
- if (info->entry != NULL && info->entry != entry)
- test_and_disconnect(session);
-
- if (entry != NULL) {
- info->entry = entry;
- info->entry->reason = reason;
-
- if (explicit_connect(reason) == TRUE)
- __connman_service_session_inc(info->entry->service);
-
- if (do_connect == TRUE)
- __connman_service_connect(info->entry->service);
- else
- info->online = is_connected(entry->state);
- }
}
static void session_changed(struct connman_session *session,
* play a bit around. So we are going to improve it step by step.
*/
- DBG("session %p trigger %s", session, trigger2string(trigger));
+ DBG("session %p trigger %s reason %s", session, trigger2string(trigger),
+ reason2string(info->reason));
+
+ if (info->entry != NULL) {
+ info->online = is_online(info->entry->state);
+ if (info_last->online != info->online)
+ session->info_dirty = TRUE;
+ }
switch (trigger) {
case CONNMAN_SESSION_TRIGGER_UNKNOWN:
* This service is not part of this
* session anymore.
*/
- test_and_disconnect(session);
+ deselect_and_disconnect(session, info->reason);
}
}
break;
case CONNMAN_SESSION_TRIGGER_CONNECT:
if (info->online == TRUE) {
+ if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
+ break;
info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
__connman_service_session_inc(info->entry->service);
break;
break;
case CONNMAN_SESSION_TRIGGER_DISCONNECT:
- test_and_disconnect(session);
+ deselect_and_disconnect(session,
+ CONNMAN_SESSION_REASON_DISCONNECT);
break;
case CONNMAN_SESSION_TRIGGER_PERIODIC:
break;
case CONNMAN_SESSION_TRIGGER_SERVICE:
- if (info->online == TRUE)
+ if (info->entry != NULL &&
+ (is_connecting(info->entry->state) == TRUE ||
+ is_online(info->entry->state) == TRUE)) {
break;
+ }
- if (info->stay_connected == TRUE) {
- DBG("StayConnected");
- select_and_connect(session,
- CONNMAN_SESSION_REASON_CONNECT);
+ deselect_and_disconnect(session, info->reason);
- break;
+ if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE ||
+ info->stay_connected == TRUE) {
+ select_and_connect(session, info->reason);
}
- select_and_connect(session,
- CONNMAN_SESSION_REASON_FREE_RIDE);
-
break;
case CONNMAN_SESSION_TRIGGER_ECALL:
- if (info->online == FALSE && info->entry->service != NULL)
- test_and_disconnect(session);
+ if (info->online == FALSE && info->entry != NULL &&
+ info->entry->service != NULL) {
+ deselect_and_disconnect(session, info->reason);
+ }
break;
}
DBusMessage *msg, void *user_data)
{
struct connman_session *session = user_data;
+ struct session_info *info = session->info;
DBG("session %p", session);
- if (ecall_session != NULL && ecall_session != session)
+ if (ecall_info != NULL && ecall_info != info)
return __connman_error_failed(msg, EBUSY);
session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
DBusMessage *msg, void *user_data)
{
struct connman_session *session = user_data;
+ struct session_info *info = session->info;
DBG("session %p", session);
- if (ecall_session != NULL && ecall_session != session)
+ if (ecall_info != NULL && ecall_info != info)
return __connman_error_failed(msg, EBUSY);
session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
entry->state = state;
- entry->name = name;
+ 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 = "";
+ entry->ifname = g_strdup("");
type = connman_service_get_type(entry->service);
entry->bearer = service2bearer(type);
{
struct service_entry *entry = data;
+ g_free(entry->ifname);
+
g_free(entry);
}
struct session_info *info = session->info;
struct session_info *info_last = session->info_last;
- DBG("session %p ecall_session %p ecall %d -> %d", session,
- 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;
}
return __connman_error_invalid_arguments(msg);
}
+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;
+
+ DBG("session %p", session);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
static GDBusMethodTable session_methods[] = {
{ "Destroy", "", "", destroy_session },
{ "Connect", "", "", connect_session },
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
g_dbus_add_disconnect_watch(connection, session->owner,
owner_disconnect, session, NULL);
- session->service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+ session->service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, NULL);
info->online = FALSE;
update_allowed_bearers(session);
if (info->ecall == TRUE) {
- ecall_session = session;
+ ecall_info = info;
update_ecall_sessions(session);
}
{
DBG("enable %d", enable);
- if (sessionmode == enable)
- return;
-
sessionmode = enable;
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);
if (service_match(session, service) == FALSE)
continue;
+ entry = create_service_entry(service, name,
+ CONNMAN_SERVICE_STATE_IDLE);
+ if (entry == NULL)
+ continue;
+
iter_service_list =
g_sequence_insert_sorted(session->service_list,
- service, sort_services,
+ entry, sort_services,
session);
g_hash_table_replace(session->service_hash, service,
g_sequence_remove(iter);
- info->entry = NULL;
+ if (info->entry != NULL && info->entry->service == service)
+ info->entry = NULL;
session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
}
}
{
GHashTableIter iter;
gpointer key, value;
- struct connman_session *session;
- struct session_info *info, *info_last;
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;
- info_last = session->info_last;
+ struct connman_session *session = value;
+ GSequenceIter *service_iter;
- if (info->entry != NULL && info->entry->service == service) {
- info->entry->state = state;
- info->online = is_connected(info->entry->state);
- if (info_last->online != info->online)
- session->info_dirty = TRUE;
+ service_iter = g_hash_table_lookup(session->service_hash, service);
+ if (service_iter != NULL) {
+ struct service_entry *entry;
+
+ entry = g_sequence_get(service_iter);
+ entry->state = state;
}
session_changed(session,
g_hash_table_foreach(session_hash, release_session, NULL);
g_hash_table_destroy(session_hash);
+ session_hash = NULL;
dbus_connection_unref(connection);
}