return NULL;
}
+void _connection_handle_ref(connection_handle_s *handle)
+{
+ CONNECTION_LOG(CONNECTION_INFO, "%p ref %d",
+ handle, handle->refcount + 1);
+
+ __sync_fetch_and_add(&handle->refcount, 1);
+}
+
+void _connection_handle_unref(connection_handle_s *handle)
+{
+ CONNECTION_LOG(CONNECTION_INFO, "%p ref %d",
+ handle, handle->refcount - 1);
+
+ if (__sync_fetch_and_sub(&handle->refcount, 1) != 1)
+ return;
+
+ conn_handle_list = g_slist_remove(conn_handle_list, handle);
+ _connection_libnet_deinit(handle, (conn_handle_list == NULL));
+
+ g_free(handle);
+}
+
+static void __connection_dummy_type_changed_cb(connection_type_e type, void* user_data)
+{
+ CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
+}
+
static void __connection_set_type_changed_callback(connection_handle_s *conn_handle,
void *callback, void *user_data)
{
conn_handle->type_changed_user_data = user_data;
- conn_handle->type_changed_callback = callback;
+ if (callback != NULL)
+ conn_handle->type_changed_callback = callback;
+ else
+ conn_handle->type_changed_callback = __connection_dummy_type_changed_cb;
+}
+
+static void __connection_dummy_address_changed_cb(const char* ipv4_address,
+ const char* ipv6_address, void* user_data)
+{
+ CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
}
static void __connection_set_ip_changed_callback(connection_handle_s *conn_handle,
void *callback, void *user_data)
{
conn_handle->ip_changed_user_data = user_data;
- conn_handle->ip_changed_callback = callback;
+ if (callback != NULL)
+ conn_handle->ip_changed_callback = callback;
+ else
+ conn_handle->ip_changed_callback = __connection_dummy_address_changed_cb;
}
static void __connection_set_proxy_changed_callback(connection_handle_s *conn_handle,
void *callback, void *user_data)
{
conn_handle->proxy_changed_user_data = user_data;
- conn_handle->proxy_changed_callback = callback;
+ if (callback != NULL)
+ conn_handle->proxy_changed_callback = callback;
+ else
+ conn_handle->proxy_changed_callback = __connection_dummy_address_changed_cb;
+}
+
+static void __connection_dummy_internet_state_changed_cb(connection_internet_state_e state,
+ void* user_data)
+{
+ CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
}
static void __connection_set_internet_state_changed_callback(connection_handle_s *conn_handle,
void *callback, void *user_data)
{
conn_handle->internet_state_changed_user_data = user_data;
- conn_handle->internet_state_changed_callback = callback;
+ if (callback != NULL)
+ conn_handle->internet_state_changed_callback = callback;
+ else
+ conn_handle->internet_state_changed_callback = __connection_dummy_internet_state_changed_cb;
+}
+
+static void __connection_dummy_ethernet_cable_state_changed_cb(connection_ethernet_cable_state_e state,
+ void* user_data)
+{
+ CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
}
static void __connection_set_ethernet_cable_state_changed_cb(connection_handle_s *conn_handle,
void *callback, void *user_data)
{
- conn_handle->ethernet_cable_state_changed_callback = callback;
conn_handle->ethernet_cable_state_changed_user_data = user_data;
+ if (callback != NULL)
+ conn_handle->ethernet_cable_state_changed_callback = callback;
+ else
+ conn_handle->ethernet_cable_state_changed_callback = __connection_dummy_ethernet_cable_state_changed_cb;
+}
+
+static void __connection_dummy_set_default_cb(connection_error_e result, void* user_data)
+{
+ CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
}
static void __connection_set_default_cellular_service_profile_callback(connection_handle_s *conn_handle,
void *callback, void *user_data)
{
- conn_handle->set_default_callback = callback;
conn_handle->set_default_user_data = user_data;
+ if (callback != NULL)
+ conn_handle->set_default_callback = callback;
+ else
+ conn_handle->set_default_callback = __connection_dummy_set_default_cb;
+}
+
+static void __connection_dummy_opened_cb(connection_error_e result, void* user_data)
+{
+ CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
}
static void __connection_open_profile_set_callback(connection_handle_s *conn_handle,
void *callback, void *user_data)
{
- conn_handle->opened_callback = callback;
conn_handle->opened_user_data = user_data;
+ if (callback != NULL)
+ conn_handle->opened_callback = callback;
+ else
+ conn_handle->opened_callback = __connection_dummy_opened_cb;
+}
+
+static void __connection_dummy_closed_cb(connection_error_e result, void* user_data)
+{
+ CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
}
static void __connection_close_profile_set_callback(connection_handle_s *conn_handle,
void *callback, void *user_data)
{
- conn_handle->closed_callback = callback;
conn_handle->closed_user_data = user_data;
+ if (callback != NULL)
+ conn_handle->closed_callback = callback;
+ else
+ conn_handle->closed_callback = __connection_dummy_closed_cb;
}
static void __connection_reset_profile_set_callback(connection_handle_s *conn_handle,
void *callback, void *user_data)
{
- conn_handle->reset_callback = callback;
conn_handle->reset_user_data = user_data;
+ if (callback != NULL)
+ conn_handle->reset_callback = callback;
+ else
+ conn_handle->reset_callback = __connection_dummy_opened_cb;
}
//LCOV_EXCL_STOP
}
conn_handle_list = g_slist_prepend(conn_handle_list, *connection);
+ _connection_handle_ref(*connection);
CONN_UNLOCK;
return CONNECTION_ERROR_NONE;
__connection_set_internet_state_changed_callback(connection, NULL, NULL);
__connection_set_ethernet_cable_state_changed_cb(connection, NULL, NULL);
- conn_handle_list = g_slist_remove(conn_handle_list, connection);
- _connection_libnet_deinit(connection, (conn_handle_list == NULL));
-
- g_free(connection);
+ _connection_handle_unref(connection);
CONN_UNLOCK;
return CONNECTION_ERROR_NONE;
static void __libnet_state_changed_cb(char *profile_name, connection_profile_state_e state)
{
+ CONN_LOCK;
struct _profile_cb_s *cb_info;
- if (profile_name == NULL)
+ if (profile_name == NULL) {
+ CONN_UNLOCK;
return;
+ }
cb_info = g_hash_table_lookup(profile_cb_table, profile_name);
- if (cb_info == NULL)
+ if (cb_info == NULL) {
+ CONN_UNLOCK;
return;
+ }
- if (cb_info->state == state)
+ if (cb_info->state == state) {
+ CONN_UNLOCK;
return;
+ }
cb_info->state = state;
- if (state < 0 || cb_info->callback == NULL)
+ if (state < 0 || cb_info->callback == NULL) {
+ CONN_UNLOCK;
return;
+ }
+ CONN_UNLOCK;
cb_info->callback(cb_info->state, cb_info->user_data);
}
if (is_requested) {
if (conn_handle->opened_callback) {
+
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->opened_callback(result,
conn_handle->opened_user_data);
+ CONN_LOCK;
conn_handle->opened_callback = NULL;
conn_handle->opened_user_data = NULL;
+ _connection_handle_unref(conn_handle);
}
}
case NET_ERR_ACTIVE_CONNECTION_EXISTS:
CONNECTION_LOG(CONNECTION_INFO, "Successfully open connection");
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
__libnet_state_changed_cb(event_cb->profile_name, CONNECTION_PROFILE_STATE_CONNECTED);
+
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
CONN_UNLOCK;
+
return;
default:
CONNECTION_LOG(CONNECTION_ERROR, "Failed to open connection[%s]",
__libnet_convert_cp_error_type_to_string(result));
}
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
__libnet_state_changed_cb(event_cb->profile_name, CONNECTION_PROFILE_STATE_DISCONNECTED);
- break;
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
+ CONN_UNLOCK;
+
+ return;
case NET_EVENT_CLOSE_RSP:
is_requested = true;
/* fall through */
if (is_requested) {
if (conn_handle->closed_callback) {
+
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->closed_callback(result,
conn_handle->closed_user_data);
+ CONN_LOCK;
conn_handle->closed_callback = NULL;
conn_handle->closed_user_data = NULL;
+ _connection_handle_unref(conn_handle);
}
}
case NET_ERR_NONE:
CONNECTION_LOG(CONNECTION_INFO, "Successfully closed connection");
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
__libnet_state_changed_cb(event_cb->profile_name, CONNECTION_PROFILE_STATE_DISCONNECTED);
+
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
CONN_UNLOCK;
+
return;
default:
CONNECTION_LOG(CONNECTION_ERROR, "Failed to close connection[%s]",
CONNECTION_LOG(CONNECTION_INFO, "state: %s", __libnet_convert_cp_state_to_string(cp_state));
SECURE_CONNECTION_LOG(CONNECTION_INFO, "profile name: %s", event_cb->profile_name);
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
__libnet_state_changed_cb(event_cb->profile_name, cp_state);
- break;
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
+ CONN_UNLOCK;
+
+ return;
case NET_EVENT_CELLULAR_SET_DEFAULT_RSP:
result = __libnet_convert_to_cp_error_type(event_cb->Error);
CONNECTION_LOG(CONNECTION_INFO, "Got set default profile RSP %d", result);
if (conn_handle->set_default_callback) {
+
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->set_default_callback(result,
conn_handle->set_default_user_data);
+ CONN_LOCK;
conn_handle->set_default_callback = NULL;
conn_handle->set_default_user_data = NULL;
+ _connection_handle_unref(conn_handle);
}
break;
case NET_EVENT_CELLULAR_RESET_DEFAULT_RSP:
result = __libnet_convert_to_cp_error_type(event_cb->Error);
CONNECTION_LOG(CONNECTION_INFO, "Got reset default profile RSP %d", result);
if (conn_handle->reset_callback) {
+
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->reset_callback(result,
conn_handle->reset_user_data);
+ CONN_LOCK;
conn_handle->reset_callback = NULL;
conn_handle->reset_user_data = NULL;
+ _connection_handle_unref(conn_handle);
}
break;
case NET_EVENT_ETHERNET_CABLE_ATTACHED:
CONNECTION_LOG(CONNECTION_INFO, "Got Ethernet cable Attached Indication\n");
if (conn_handle->ethernet_cable_state_changed_callback) {
+
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->ethernet_cable_state_changed_callback(CONNECTION_ETHERNET_CABLE_ATTACHED,
conn_handle->ethernet_cable_state_changed_user_data);
+
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
+ CONN_UNLOCK;
+
+ return;
}
break;
case NET_EVENT_ETHERNET_CABLE_DETACHED:
CONNECTION_LOG(CONNECTION_INFO, "Got Ethernet cable detached Indication\n");
if (conn_handle->ethernet_cable_state_changed_callback) {
+
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->ethernet_cable_state_changed_callback(CONNECTION_ETHERNET_CABLE_DETACHED,
conn_handle->ethernet_cable_state_changed_user_data);
+
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
+ CONN_UNLOCK;
+
+ return;
}
break;
case NET_EVENT_NETWORK_TYPE_CHANGED:
break;
}
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->type_changed_callback(type,
conn_handle->type_changed_user_data);
+
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
+ CONN_UNLOCK;
+
+ return;
}
break;
case NET_EVENT_IPV4_ADDRESS_CHANGED:
CONNECTION_LOG(CONNECTION_ERROR, //LCOV_EXCL_LINE
"vconf_get_str(VCONFKEY_NETWORK_IP6) failed");
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->ip_changed_callback(ipv4_addr, ipv6_addr,
conn_handle->ip_changed_user_data);
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
+ CONN_UNLOCK;
+
g_free(ipv4_addr);
g_free(ipv6_addr);
+ return;
}
break;
case NET_EVENT_IPV6_ADDRESS_CHANGED:
CONNECTION_LOG(CONNECTION_ERROR, //LCOV_EXCL_LINE
"vconf_get_str(VCONFKEY_NETWORK_IP) failed");
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->ip_changed_callback(ipv4_addr, ipv6_addr,
conn_handle->ip_changed_user_data);
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
+ CONN_UNLOCK;
+
g_free(ipv4_addr);
g_free(ipv6_addr);
+ return;
}
break;
case NET_EVENT_PROXY_ADDRESS_CHANGED:
char *proxy_addr = (char *)event_cb->data;
if (conn_handle->proxy_changed_callback) {
+
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->proxy_changed_callback(proxy_addr, NULL,
conn_handle->proxy_changed_user_data);
+
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
+ CONN_UNLOCK;
+
+ return;
}
break;
case NET_EVENT_INTERNET_ONLINE_IND:
rv = net_get_active_net_info(conn_handle->network_info_handle, &active_profile);
if (rv == NET_ERR_NO_SERVICE && event_cb->event == NET_EVENT_INTERNET_OFFLINE_IND) {
+
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->internet_state_changed_callback(CONNECTION_INTERNET_STATE_OFFLINE,
conn_handle->internet_state_changed_user_data); //LCOV_EXCL_LINE
- break;
+
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
+ CONN_UNLOCK;
+
+ return;
} else if (rv == NET_ERR_ACCESS_DENIED) {
CONNECTION_LOG(CONNECTION_ERROR, "Access denied"); //LCOV_EXCL_LINE
break;
if (event_cb->event == NET_EVENT_INTERNET_ONLINE_IND) {
if (active_profile.profile_state == NET_STATE_TYPE_ONLINE &&
- active_profile.profile_type == *device_type)
+ active_profile.profile_type == *device_type) {
+
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->internet_state_changed_callback(CONNECTION_INTERNET_STATE_ONLINE,
conn_handle->internet_state_changed_user_data);
+
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
+ CONN_UNLOCK;
+
+ return;
+ }
} else {
- if (active_profile.profile_state != NET_STATE_TYPE_ONLINE)
+ if (active_profile.profile_state != NET_STATE_TYPE_ONLINE) {
+
+ _connection_handle_ref(conn_handle);
+ CONN_UNLOCK;
+
conn_handle->internet_state_changed_callback(CONNECTION_INTERNET_STATE_OFFLINE,
conn_handle->internet_state_changed_user_data);
+
+ CONN_LOCK;
+ _connection_handle_unref(conn_handle);
+ CONN_UNLOCK;
+
+ return;
+ }
}
}
break;
}
//LCOV_EXCL_STOP
+static void __connection_dummy_profile_state_changed_cb(connection_profile_state_e state,
+ void *user_data)
+{
+ CONNECTION_LOG(CONNECTION_INFO, "Dummy callback");
+}
+
+static int __profile_cb_table_value_destroy(gpointer data)
+{
+ struct _profile_cb_s *profile_cb_data = (struct _profile_cb_s *)data;
+
+ g_free(profile_cb_data);
+
+ return G_SOURCE_REMOVE;
+}
+
int _connection_libnet_init(connection_handle_s *conn_handle)
{
int rv;
return rv;
if (profile_cb_table == NULL)
- profile_cb_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+ profile_cb_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
if (!in_container && access(CONTAINER_FILE, F_OK) == 0)
in_container = TRUE;
bool _connection_libnet_remove_from_profile_cb_list(connection_profile_h profile)
{
net_profile_info_t *profile_info = profile;
+ struct _profile_cb_s *profile_cb_info;
+
+ profile_cb_info = g_hash_table_lookup(profile_cb_table, profile_info->profile_name);
+ if (profile_cb_info == NULL)
+ return false;
- if (g_hash_table_remove(profile_cb_table, profile_info->profile_name) == TRUE)
- return true;
+ g_hash_table_remove(profile_cb_table, profile_info->profile_name);
- return false; //LCOV_EXCL_LINE
+ /**
+ * Intentional:
+ * Marking callback to dummy callback.
+ *
+ * Reason:
+ * To avoid race condition between calling profile_state_changed_cb()
+ * and _connection_libnet_remove_from_profile_cb_list()
+ * in multi-threaded environment.
+ */
+ profile_cb_info->callback = __connection_dummy_profile_state_changed_cb;
+ profile_cb_info->user_data = NULL;
+ profile_cb_info->state = -1;
+
+ /* Timer to free hash table entry */
+ g_timeout_add(200, __profile_cb_table_value_destroy, profile_cb_info);
+
+ return true;
}
int _connection_libnet_set_statistics(connection_handle_s *conn_handle,