From: Jose Blanquicet <jose.blanquicet-melendez(a)magnetimarelli.com>
A ConnMan service is a WiFi network identified by the SSID, security (none, wep,
psk and ieee8021x) and mode (managed, adhoc, ap). This WiFi network could be
composed by multiple BSSs but it is shown as a single service by ConnMan. The
properties of that network correspond to the best BSS (Currently, only the
signal strength). The best BSS is the one with the highest signal strength. Now,
the issue is that ConnMan is selecting the best BSS by itself and it could cause
a misalignment with wpa_s; in particular when roaming. For instance, if ConnMan
updates the best BSS because there is a BSS with higher signal strength than the
current one but wpa_s takes too much time to roaming or even worse it never does
the roaming because such feature is not enabled in wpa_s; on both cases, ConnMan
will incorrectly start propagating the signal strength of the its best BSS but
actually we are associated to another one. Therefore, in these cases, the signal
strength the user will see for that network comes from the BSS with the highest
signal not from the one he are actually associated.
This patch makes ConnMan memorize the WiFi network it gets associated
(current_network). Then, from this point forward, the best BSS of that network
will only be updated if wpa_s signals a disassociation or we got associated with
another BSS (roaming).
With this patch, ConnMan is now able to distinguish at gsupplicant level which
is the BSS it is actually associated to because it will be always the best BSS
of the associated network. This is useful for future developments.
---
gsupplicant/supplicant.c | 118 ++++++++++++++++++++++++++++++++---------------
1 file changed, 81 insertions(+), 37 deletions(-)
diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c
index 2d5055769152..4f79012252e5 100644
--- a/gsupplicant/supplicant.c
+++ b/gsupplicant/supplicant.c
@@ -189,6 +189,7 @@ struct _GSupplicantInterface {
GHashTable *bss_mapping;
void *data;
const char *pending_peer_path;
+ GSupplicantNetwork *current_network;
struct added_network_information network_info;
};
@@ -1628,7 +1629,12 @@ done:
network->wps_capabilities |= bss->wps_capabilities;
}
- if (bss->signal > network->signal) {
+ /*
+ * Do not change best BSS if we are connected. It will be done through
+ * CurrentBSS property in case of misalignment with wpa_s or roaming.
+ */
+ if (network != interface->current_network &&
+ bss->signal > network->signal) {
network->signal = bss->signal;
network->best_bss = bss;
callback_network_changed(network, "Signal");
@@ -2088,6 +2094,75 @@ static void update_network_signal(GSupplicantNetwork *network)
SUPPLICANT_DBG("New network signal %d", network->signal);
}
+static void interface_current_bss(GSupplicantInterface *interface,
+ DBusMessageIter *iter)
+{
+ GSupplicantNetwork *network;
+ struct g_supplicant_bss *bss;
+ const char *path;
+
+ dbus_message_iter_get_basic(iter, &path);
+ if (g_strcmp0(path, "/") == 0) {
+ interface->current_network = NULL;
+ return;
+ }
+
+ interface_bss_added_without_keys(iter, interface);
+
+ network = g_hash_table_lookup(interface->bss_mapping, path);
+ if (!network)
+ return;
+
+ bss = g_hash_table_lookup(network->bss_table, path);
+ if (!bss)
+ return;
+
+ interface->current_network = network;
+
+ if (bss != network->best_bss) {
+ /*
+ * This is the case where either wpa_s got associated
+ * to a BSS different than the one ConnMan considers
+ * the best, or we are roaming.
+ */
+ SUPPLICANT_DBG("Update best BSS for %s", network->name);
+
+ network->best_bss = bss;
+
+ if (network->signal != bss->signal) {
+ SUPPLICANT_DBG("New network signal %d dBm",
+ bss->signal);
+
+ network->signal = bss->signal;
+ callback_network_changed(network, "Signal");
+ }
+ }
+
+ /*
+ * wpa_s could notify about CurrentBSS in any state once
+ * it got associated. It is not sure such notification will
+ * arrive together with transition to ASSOCIATED state.
+ * In fact, for networks with security WEP or OPEN, it
+ * always arrives together with transition to COMPLETED.
+ */
+ switch (interface->state) {
+ case G_SUPPLICANT_STATE_UNKNOWN:
+ case G_SUPPLICANT_STATE_DISABLED:
+ case G_SUPPLICANT_STATE_DISCONNECTED:
+ case G_SUPPLICANT_STATE_INACTIVE:
+ case G_SUPPLICANT_STATE_SCANNING:
+ case G_SUPPLICANT_STATE_AUTHENTICATING:
+ case G_SUPPLICANT_STATE_ASSOCIATING:
+ return;
+ case G_SUPPLICANT_STATE_ASSOCIATED:
+ case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
+ case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
+ case G_SUPPLICANT_STATE_COMPLETED:
+ callback_network_associated(network);
+ break;
+ }
+}
+
static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
{
GSupplicantInterface *interface = user_data;
@@ -2270,42 +2345,7 @@ static void interface_property(const char *key, DBusMessageIter
*iter,
g_strdup(interface->ifname), g_strdup(str));
}
} else if (g_strcmp0(key, "CurrentBSS") == 0) {
- GSupplicantNetwork *network;
- const char *path = NULL;
-
- dbus_message_iter_get_basic(iter, &path);
- if (g_strcmp0(path, "/") == 0)
- return;
-
- interface_bss_added_without_keys(iter, interface);
-
- network = g_hash_table_lookup(interface->bss_mapping, path);
- if (!network)
- return;
-
- /*
- * wpa_s could notify about CurrentBSS in any state once
- * it got associated. It is not sure such notification will
- * arrive together with transition to ASSOCIATED state.
- * In fact, for networks with security WEP or OPEN, it
- * always arrives together with transition to COMPLETED.
- */
- switch (interface->state) {
- case G_SUPPLICANT_STATE_UNKNOWN:
- case G_SUPPLICANT_STATE_DISABLED:
- case G_SUPPLICANT_STATE_DISCONNECTED:
- case G_SUPPLICANT_STATE_INACTIVE:
- case G_SUPPLICANT_STATE_SCANNING:
- case G_SUPPLICANT_STATE_AUTHENTICATING:
- case G_SUPPLICANT_STATE_ASSOCIATING:
- return;
- case G_SUPPLICANT_STATE_ASSOCIATED:
- case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
- case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
- case G_SUPPLICANT_STATE_COMPLETED:
- callback_network_associated(network);
- break;
- }
+ interface_current_bss(interface, iter);
} else if (g_strcmp0(key, "CurrentNetwork") == 0) {
interface_network_added(iter, interface);
} else if (g_strcmp0(key, "BSSs") == 0) {
@@ -2741,6 +2781,10 @@ static void signal_bss_changed(const char *path, DBusMessageIter
*iter)
return;
}
+ /* Consider only property changes of the connected BSS */
+ if (network == interface->current_network && bss != network->best_bss)
+ return;
+
if (bss->signal == network->signal)
return;
--
1.9.1