diff options
author | Zeeshan Ali <zeenix@collabora.co.uk> | 2018-12-21 17:15:03 +0100 |
---|---|---|
committer | Zeeshan Ali <zeenix@collabora.co.uk> | 2018-12-22 15:58:58 +0100 |
commit | 8e1ed80a0b583a84da0817383247f53854061ebe (patch) | |
tree | b76ac7c5ef7e1f12979f6b95fbe51614ae542bd6 | |
parent | f871ce1d2cff33244667dd49a2d510cc66cd171e (diff) | |
download | geoclue-8e1ed80a0b583a84da0817383247f53854061ebe.tar.gz |
wifi: Tell wpa_supplicant to keep scanning
It seems by default, wpa_supplicant won't keep a list of BSS around but
only if it has done a scan recently. This means we currently end up with
very bad accuracy since we only send out the ESSID of the currently
connected WiFi (if any).
To fix this, we'll now tell wpa_supplicant to start a scan when the source is
started. We keep doing that continuously if accuracy is street level or higher,
but wait 5 minutues between scans if requested accuracy is lower than that.
Fixes #91.
-rw-r--r-- | src/gclue-wifi.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/src/gclue-wifi.c b/src/gclue-wifi.c index cf8b04e..8f4b90c 100644 --- a/src/gclue-wifi.c +++ b/src/gclue-wifi.c @@ -28,6 +28,11 @@ #include "gclue-error.h" #include "gclue-mozilla.h" +/* Since this is only used for city-level accuracy, 5 minutes betweeen each + * scan is more than enough. + */ +#define WIFI_SCAN_TIMEOUT 300 + /** * SECTION:gclue-wifi * @short_description: WiFi-based geolocation @@ -49,8 +54,10 @@ struct _GClueWifiPrivate { gulong bss_added_id; gulong bss_removed_id; + gulong scan_done_id; guint refresh_timeout; + guint scan_timeout; GClueAccuracyLevel accuracy_level; }; @@ -82,6 +89,14 @@ G_DEFINE_TYPE (GClueWifi, gclue_wifi, GCLUE_TYPE_WEB_SOURCE) static void disconnect_bss_signals (GClueWifi *wifi); +static void +on_scan_call_done (GObject *source_object, + GAsyncResult *res, + gpointer user_data); +static void +on_scan_done (WPAInterface *object, + gboolean success, + gpointer user_data); static void gclue_wifi_finalize (GObject *gwifi) @@ -369,6 +384,114 @@ on_bss_removed (WPAInterface *object, } static void +cancel_wifi_scan (GClueWifi *wifi) +{ + GClueWifiPrivate *priv = wifi->priv; + + if (priv->scan_timeout != 0) { + g_source_remove (priv->scan_timeout); + priv->scan_timeout = 0; + } + + if (priv->scan_done_id != 0) { + g_signal_handler_disconnect (priv->interface, + priv->scan_done_id); + priv->scan_done_id = 0; + } +} + +static gboolean +on_scan_timeout (gpointer user_data) +{ + GClueWifi *wifi = GCLUE_WIFI (user_data); + GClueWifiPrivate *priv = wifi->priv; + GVariantBuilder builder; + GVariant *args; + + if (priv->interface == NULL) + return FALSE; + + g_debug ("WiFi scan timeout. Restarting-scan.."); + priv->scan_timeout = 0; + + if (priv->scan_done_id == 0) + priv->scan_done_id = g_signal_connect + (priv->interface, + "scan-done", + G_CALLBACK (on_scan_done), + wifi); + + g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add (&builder, + "{sv}", + "Type", g_variant_new ("s", "passive")); + args = g_variant_builder_end (&builder); + + wpa_interface_call_scan (WPA_INTERFACE (priv->interface), + args, + NULL, + on_scan_call_done, + wifi); + + return FALSE; +} + +static void +on_scan_done (WPAInterface *object, + gboolean success, + gpointer user_data) +{ + GClueWifi *wifi = GCLUE_WIFI (user_data); + GClueWifiPrivate *priv = wifi->priv; + + if (!success) { + g_warning ("WiFi scan failed"); + + return; + } + g_debug ("WiFi scan completed"); + + if (priv->interface == NULL) + return; + + gclue_web_source_refresh (GCLUE_WEB_SOURCE (wifi)); + + if (priv->accuracy_level >= GCLUE_ACCURACY_LEVEL_STREET) + /* With high-enough accuracy requests, we need to continuously + * keep scanning since user's location can change quickly. With + * low accuracy, we don't since we wouldn't want to drain power + * unnecessarily. + */ + on_scan_timeout (wifi); + else + priv->scan_timeout = g_timeout_add_seconds (WIFI_SCAN_TIMEOUT, + on_scan_timeout, + wifi); +} + +static void +on_scan_call_done (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GClueWifi *wifi = GCLUE_WIFI (user_data); + GError *error = NULL; + + if (!wpa_interface_call_scan_finish + (WPA_INTERFACE (source_object), + res, + &error)) { + g_warning ("Scanning of WiFi networks failed: %s", + error->message); + g_error_free (error); + + cancel_wifi_scan (wifi); + + return; + } +} + +static void connect_bss_signals (GClueWifi *wifi) { GClueWifiPrivate *priv = wifi->priv; @@ -383,6 +506,8 @@ connect_bss_signals (GClueWifi *wifi) return; } + on_scan_timeout (wifi); + priv->bss_added_id = g_signal_connect (priv->interface, "bss-added", G_CALLBACK (on_bss_added), @@ -411,6 +536,7 @@ disconnect_bss_signals (GClueWifi *wifi) if (priv->bss_added_id == 0 || priv->interface == NULL) return; + cancel_wifi_scan (wifi); g_signal_handler_disconnect (priv->interface, priv->bss_added_id); priv->bss_added_id = 0; g_signal_handler_disconnect (priv->interface, priv->bss_removed_id); |