diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gclue-3g.c | 172 | ||||
-rw-r--r-- | src/gclue-3g.h | 10 | ||||
-rw-r--r-- | src/gclue-locator.c | 2 | ||||
-rw-r--r-- | src/gclue-mozilla.c | 199 | ||||
-rw-r--r-- | src/gclue-mozilla.h | 56 | ||||
-rw-r--r-- | src/gclue-wifi.c | 118 | ||||
-rw-r--r-- | src/gclue-wifi.h | 2 |
7 files changed, 471 insertions, 88 deletions
diff --git a/src/gclue-3g.c b/src/gclue-3g.c index 0f33dcd..29d2746 100644 --- a/src/gclue-3g.c +++ b/src/gclue-3g.c @@ -24,9 +24,11 @@ #include <libsoup/soup.h> #include <string.h> #include "gclue-3g.h" +#include "gclue-3g-tower.h" #include "gclue-modem-manager.h" #include "gclue-location.h" #include "gclue-mozilla.h" +#include "gclue-wifi.h" /** * SECTION:gclue-3g @@ -35,14 +37,24 @@ * Contains functions to get the geolocation based on 3GPP cell towers. **/ +/* Should be slightly less than MAX_LOCATION_AGE in gclue-locator.c, so we don't + * get replaced by a less accurate WiFi location while still connected to a tower. + * Technically, this can only happen on the NEIGHBORHOOD accuracy level (since at + * this level WiFi does scrambling), but it won't hurt on higher ones, too. + * In seconds. + */ +#define LOCATION_3GPP_TIMEOUT (25 * 60) + +static unsigned int gclue_3g_running; + struct _GClue3GPrivate { + GClueMozilla *mozilla; GClueModem *modem; GCancellable *cancellable; gulong threeg_notify_id; - - GClue3GTower *tower; + guint location_3gpp_timeout_id; }; G_DEFINE_TYPE_WITH_CODE (GClue3G, @@ -115,6 +127,17 @@ gclue_3g_parse_response (GClueWebSource *web, return gclue_mozilla_parse_response (content, error); } +static void cancel_location_3gpp_timeout (GClue3G *g3g) +{ + GClue3GPrivate *priv = g3g->priv; + + if (!priv->location_3gpp_timeout_id) + return; + + g_source_remove (priv->location_3gpp_timeout_id); + priv->location_3gpp_timeout_id = 0; +} + static void gclue_3g_finalize (GObject *g3g) { @@ -129,7 +152,10 @@ gclue_3g_finalize (GObject *g3g) priv->threeg_notify_id); priv->threeg_notify_id = 0; + cancel_location_3gpp_timeout (source); + g_clear_object (&priv->modem); + g_clear_object (&priv->mozilla); g_clear_object (&priv->cancellable); } @@ -161,12 +187,15 @@ gclue_3g_init (GClue3G *source) priv->cancellable = g_cancellable_new (); + priv->mozilla = gclue_mozilla_get_singleton (); + priv->modem = gclue_modem_manager_get_singleton (); priv->threeg_notify_id = g_signal_connect (priv->modem, "notify::is-3g-available", G_CALLBACK (on_is_3g_available_notify), source); + priv->location_3gpp_timeout_id = 0; } static void @@ -181,27 +210,32 @@ on_3g_destroyed (gpointer data, /** * gclue_3g_new: * - * Get the #GClue3G singleton. + * Get the #GClue3G singleton, for the specified max accuracy level @level. * * Returns: (transfer full): a new ref to #GClue3G. Use g_object_unref() * when done. **/ GClue3G * -gclue_3g_get_singleton (void) +gclue_3g_get_singleton (GClueAccuracyLevel level) { - static GClue3G *source = NULL; - - if (source == NULL) { - source = g_object_new (GCLUE_TYPE_3G, - "compute-movement", FALSE, - NULL); - g_object_weak_ref (G_OBJECT (source), + static GClue3G *source[] = { NULL, NULL }; + int i; + + g_return_val_if_fail (level >= GCLUE_ACCURACY_LEVEL_CITY, NULL); + + i = gclue_wifi_should_skip_bsss (level) ? 0 : 1; + if (source[i] == NULL) { + source[i] = g_object_new (GCLUE_TYPE_3G, + "accuracy-level", level, + "compute-movement", FALSE, + NULL); + g_object_weak_ref (G_OBJECT (source[i]), on_3g_destroyed, - &source); + &source[i]); } else - g_object_ref (source); + g_object_ref (source[i]); - return source; + return source[i]; } static SoupMessage * @@ -209,8 +243,10 @@ gclue_3g_create_query (GClueWebSource *web, GError **error) { GClue3GPrivate *priv = GCLUE_3G (web)->priv; + GClueAccuracyLevel level; + gboolean skip_bss; - if (priv->tower == NULL) { + if (!gclue_mozilla_has_tower (priv->mozilla)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, @@ -218,7 +254,14 @@ gclue_3g_create_query (GClueWebSource *web, return NULL; /* Not initialized yet */ } - return gclue_mozilla_create_query (NULL, priv->tower, error); + g_object_get (G_OBJECT(web), "accuracy-level", &level, NULL); + skip_bss = gclue_wifi_should_skip_bsss (level); + if (skip_bss) { + g_debug ("Will skip BSSs in query as our accuracy level is %d", + (int)level); + } + + return gclue_mozilla_create_query (priv->mozilla, FALSE, skip_bss, error); } static SoupMessage * @@ -228,7 +271,7 @@ gclue_3g_create_submit_query (GClueWebSource *web, { GClue3GPrivate *priv = GCLUE_3G (web)->priv; - if (priv->tower == NULL) { + if (!gclue_mozilla_has_tower (priv->mozilla)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, @@ -236,9 +279,8 @@ gclue_3g_create_submit_query (GClueWebSource *web, return NULL; /* Not initialized yet */ } - return gclue_mozilla_create_submit_query (location, - NULL, - priv->tower, + return gclue_mozilla_create_submit_query (priv->mozilla, + location, error); } @@ -253,6 +295,41 @@ gclue_3g_get_available_accuracy_level (GClueWebSource *web, return GCLUE_ACCURACY_LEVEL_NONE; } +gboolean gclue_3g_should_skip_tower (GClueAccuracyLevel level) +{ + return level < GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD; +} + +static gboolean +on_location_3gpp_timeout (gpointer user_data) +{ + GClue3G *g3g = GCLUE_3G (user_data); + GClue3GPrivate *priv = g3g->priv; + + if (!gclue_mozilla_has_tower (priv->mozilla)) { + g_debug ("3GPP location timeout, but no tower"); + priv->location_3gpp_timeout_id = 0; + return G_SOURCE_REMOVE; + } + + g_debug ("3GPP location timeout, re-sending existing location"); + gclue_web_source_refresh (GCLUE_WEB_SOURCE (g3g)); + + return G_SOURCE_CONTINUE; +} + +static void set_location_3gpp_timeout (GClue3G *g3g) +{ + GClue3GPrivate *priv = g3g->priv; + + g_debug ("Scheduling new 3GPP location timeout"); + + cancel_location_3gpp_timeout (g3g); + priv->location_3gpp_timeout_id = g_timeout_add_seconds (LOCATION_3GPP_TIMEOUT, + on_location_3gpp_timeout, + g3g); +} + static void on_fix_3g (GClueModem *modem, const gchar *opc, @@ -261,18 +338,26 @@ on_fix_3g (GClueModem *modem, GClueTowerTec tec, gpointer user_data) { - GClue3GPrivate *priv = GCLUE_3G (user_data)->priv; - - if (tec == GCLUE_TOWER_TEC_NO_FIX) - return; - - if (priv->tower == NULL) - priv->tower = g_slice_new0 (GClue3GTower); - g_strlcpy (priv->tower->opc, opc, - GCLUE_3G_TOWER_OPERATOR_CODE_STR_LEN + 1); - priv->tower->lac = lac; - priv->tower->cell_id = cell_id; - priv->tower->tec = tec; + GClue3G *g3g = GCLUE_3G (user_data); + GClue3GPrivate *priv = g3g->priv; + + g_debug ("3GPP %s fix available", + tec == GCLUE_TOWER_TEC_NO_FIX ? "no" : "new"); + + if (tec != GCLUE_TOWER_TEC_NO_FIX) { + GClue3GTower tower; + + g_strlcpy (tower.opc, opc, + GCLUE_3G_TOWER_OPERATOR_CODE_STR_LEN + 1); + tower.lac = lac; + tower.cell_id = cell_id; + tower.tec = tec; + set_location_3gpp_timeout (g3g); + gclue_mozilla_set_tower (priv->mozilla, &tower); + } else { + cancel_location_3gpp_timeout (g3g); + gclue_mozilla_set_tower (priv->mozilla, NULL); + } gclue_web_source_refresh (GCLUE_WEB_SOURCE (user_data)); } @@ -293,10 +378,10 @@ gclue_3g_start (GClueLocationSource *source) if (base_result != GCLUE_LOCATION_SOURCE_START_RESULT_OK) return base_result; - if (priv->tower != NULL) { - g_slice_free (GClue3GTower, priv->tower); - priv->tower = NULL; + if (gclue_3g_running == 0) { + g_debug ("First 3GPP source starting up"); } + gclue_3g_running++; g_signal_connect (priv->modem, "fix-3g", @@ -315,7 +400,8 @@ gclue_3g_start (GClueLocationSource *source) static GClueLocationSourceStopResult gclue_3g_stop (GClueLocationSource *source) { - GClue3GPrivate *priv = GCLUE_3G (source)->priv; + GClue3G *g3g = GCLUE_3G (source); + GClue3GPrivate *priv = g3g->priv; GClueLocationSourceClass *base_class; GError *error = NULL; GClueLocationSourceStopResult base_result; @@ -324,13 +410,23 @@ gclue_3g_stop (GClueLocationSource *source) base_class = GCLUE_LOCATION_SOURCE_CLASS (gclue_3g_parent_class); base_result = base_class->stop (source); - if (base_result == GCLUE_LOCATION_SOURCE_STOP_RESULT_STILL_USED) + if (base_result != GCLUE_LOCATION_SOURCE_STOP_RESULT_OK) return base_result; g_signal_handlers_disconnect_by_func (G_OBJECT (priv->modem), G_CALLBACK (on_fix_3g), source); + cancel_location_3gpp_timeout (g3g); + + g_assert (gclue_3g_running > 0); + gclue_3g_running--; + if (gclue_3g_running > 0) { + return base_result; + } + + g_debug ("Last 3GPP source stopping, disabling location gathering and invalidating existing tower"); + if (gclue_modem_get_is_3g_available (priv->modem)) if (!gclue_modem_disable_3g (priv->modem, priv->cancellable, @@ -340,5 +436,7 @@ gclue_3g_stop (GClueLocationSource *source) g_error_free (error); } + gclue_mozilla_set_tower (priv->mozilla, NULL); + return base_result; } diff --git a/src/gclue-3g.h b/src/gclue-3g.h index bec215b..0eacf88 100644 --- a/src/gclue-3g.h +++ b/src/gclue-3g.h @@ -22,6 +22,8 @@ #ifndef GCLUE_3G_H #define GCLUE_3G_H +#include "config.h" + #include <glib.h> #include <gio/gio.h> #include "gclue-web-source.h" @@ -62,7 +64,13 @@ struct _GClue3GClass { GClueWebSourceClass parent_class; }; -GClue3G * gclue_3g_get_singleton (void); +GClue3G * gclue_3g_get_singleton (GClueAccuracyLevel level); + +#if GCLUE_USE_3G_SOURCE +gboolean gclue_3g_should_skip_tower (GClueAccuracyLevel level); +#else +static inline gboolean gclue_3g_should_skip_tower (GClueAccuracyLevel level) { return TRUE; } +#endif G_END_DECLS diff --git a/src/gclue-locator.c b/src/gclue-locator.c index 5820f99..a7dd890 100644 --- a/src/gclue-locator.c +++ b/src/gclue-locator.c @@ -364,7 +364,7 @@ gclue_locator_constructed (GObject *object) #if GCLUE_USE_3G_SOURCE if (gclue_config_get_enable_3g_source (gconfig)) { - GClue3G *source = gclue_3g_get_singleton (); + GClue3G *source = gclue_3g_get_singleton (locator->priv->accuracy_level); locator->priv->sources = g_list_append (locator->priv->sources, source); } diff --git a/src/gclue-mozilla.c b/src/gclue-mozilla.c index b0e5b29..fee637e 100644 --- a/src/gclue-mozilla.c +++ b/src/gclue-mozilla.c @@ -25,8 +25,10 @@ #include <string.h> #include <config.h> #include "gclue-mozilla.h" +#include "gclue-3g-tower.h" #include "gclue-config.h" #include "gclue-error.h" +#include "gclue-wifi.h" /** * SECTION:gclue-mozilla @@ -40,6 +42,22 @@ * its easy to switch to Google's API. **/ +struct _GClueMozillaPrivate +{ + GClueWifi *wifi; + + GClue3GTower tower; + gboolean tower_valid; + gboolean tower_submitted; + + gboolean bss_submitted; +}; + +G_DEFINE_TYPE_WITH_CODE (GClueMozilla, + gclue_mozilla, + G_TYPE_OBJECT, + G_ADD_PRIVATE (GClueMozilla)) + #define BSSID_LEN 6 #define BSSID_STR_LEN 17 #define MAX_SSID_LEN 32 @@ -137,12 +155,14 @@ error: } SoupMessage * -gclue_mozilla_create_query (GList *bss_list, /* As in Access Points */ - GClue3GTower *tower, +gclue_mozilla_create_query (GClueMozilla *mozilla, + gboolean skip_tower, + gboolean skip_bss, GError **error) { SoupMessage *ret = NULL; JsonBuilder *builder; + g_autoptr(GList) bss_list = NULL; JsonGenerator *generator; JsonNode *root_node; char *data; @@ -155,6 +175,9 @@ gclue_mozilla_create_query (GList *bss_list, /* As in Access Points */ builder = json_builder_new (); json_builder_begin_object (builder); + if (mozilla->priv->wifi && !skip_bss) { + bss_list = gclue_wifi_get_bss_list (mozilla->priv->wifi); + } /* We send pure geoip query using empty object if both bss_list and * tower are NULL. * @@ -172,9 +195,8 @@ gclue_mozilla_create_query (GList *bss_list, /* As in Access Points */ n_non_ignored_bsss++; } - if (tower != NULL && - operator_code_to_mcc_mnc (tower->opc, &mcc, &mnc)) { - + if (mozilla->priv->tower_valid && !skip_tower && + operator_code_to_mcc_mnc (mozilla->priv->tower.opc, &mcc, &mnc)) { json_builder_set_member_name (builder, "radioType"); json_builder_add_string_value (builder, "gsm"); @@ -184,14 +206,14 @@ gclue_mozilla_create_query (GList *bss_list, /* As in Access Points */ json_builder_begin_object (builder); json_builder_set_member_name (builder, "cellId"); - json_builder_add_int_value (builder, tower->cell_id); + json_builder_add_int_value (builder, mozilla->priv->tower.cell_id); json_builder_set_member_name (builder, "mobileCountryCode"); json_builder_add_int_value (builder, mcc); json_builder_set_member_name (builder, "mobileNetworkCode"); json_builder_add_int_value (builder, mnc); json_builder_set_member_name (builder, "locationAreaCode"); - json_builder_add_int_value (builder, tower->lac); - if (tower->tec == GCLUE_TOWER_TEC_4G) { + json_builder_add_int_value (builder, mozilla->priv->tower.lac); + if (mozilla->priv->tower.tec == GCLUE_TOWER_TEC_4G) { json_builder_set_member_name (builder, "radioType"); json_builder_add_string_value (builder, "lte"); } @@ -316,9 +338,8 @@ get_submit_config (const char **nick) } SoupMessage * -gclue_mozilla_create_submit_query (GClueLocation *location, - GList *bss_list, /* As in Access Points */ - GClue3GTower *tower, +gclue_mozilla_create_submit_query (GClueMozilla *mozilla, + GClueLocation *location, GError **error) { SoupMessage *ret = NULL; @@ -326,6 +347,7 @@ gclue_mozilla_create_submit_query (GClueLocation *location, JsonGenerator *generator; JsonNode *root_node; char *data, *timestr; + g_autoptr(GList) bss_list = NULL; const char *url, *nick; gsize data_len; GList *iter; @@ -333,6 +355,17 @@ gclue_mozilla_create_submit_query (GClueLocation *location, GDateTime *datetime; gint64 mcc, mnc; + if (mozilla->priv->bss_submitted && + (!mozilla->priv->tower_valid || + mozilla->priv->tower_submitted)) + { + g_debug ("Already created submit req for this data (bss submitted %d; tower: valid %d submitted %d)", + (int)mozilla->priv->bss_submitted, + (int)mozilla->priv->tower_valid, + (int)mozilla->priv->tower_submitted); + goto out; + } + url = get_submit_config (&nick); if (url == NULL) goto out; @@ -379,6 +412,9 @@ gclue_mozilla_create_submit_query (GClueLocation *location, json_builder_set_member_name (builder, "radioType"); json_builder_add_string_value (builder, "gsm"); + if (mozilla->priv->wifi) { + bss_list = gclue_wifi_get_bss_list (mozilla->priv->wifi); + } if (bss_list != NULL) { json_builder_set_member_name (builder, "wifi"); json_builder_begin_array (builder); @@ -410,9 +446,8 @@ gclue_mozilla_create_submit_query (GClueLocation *location, json_builder_end_array (builder); /* wifi */ } - if (tower != NULL && - operator_code_to_mcc_mnc (tower->opc, &mcc, &mnc)) { - + if (mozilla->priv->tower_valid && + operator_code_to_mcc_mnc (mozilla->priv->tower.opc, &mcc, &mnc)) { json_builder_set_member_name (builder, "cell"); json_builder_begin_array (builder); @@ -421,13 +456,13 @@ gclue_mozilla_create_submit_query (GClueLocation *location, json_builder_set_member_name (builder, "radio"); json_builder_add_string_value (builder, "gsm"); json_builder_set_member_name (builder, "cid"); - json_builder_add_int_value (builder, tower->cell_id); + json_builder_add_int_value (builder, mozilla->priv->tower.cell_id); json_builder_set_member_name (builder, "mcc"); json_builder_add_int_value (builder, mcc); json_builder_set_member_name (builder, "mnc"); json_builder_add_int_value (builder, mnc); json_builder_set_member_name (builder, "lac"); - json_builder_add_int_value (builder, tower->lac); + json_builder_add_int_value (builder, mozilla->priv->tower.lac); json_builder_end_object (builder); @@ -459,6 +494,9 @@ gclue_mozilla_create_submit_query (GClueLocation *location, data_len); g_debug ("Sending following request to '%s':\n%s", url, data); + mozilla->priv->bss_submitted = TRUE; + mozilla->priv->tower_submitted = TRUE; + out: return ret; } @@ -485,3 +523,132 @@ gclue_mozilla_should_ignore_bss (WPABSS *bss) return FALSE; } + +static void +gclue_mozilla_finalize (GObject *object) +{ + GClueMozilla *mozilla = GCLUE_MOZILLA (object); + + g_clear_weak_pointer (&mozilla->priv->wifi); + + G_OBJECT_CLASS (gclue_mozilla_parent_class)->finalize (object); +} + +static void +gclue_mozilla_init (GClueMozilla *mozilla) +{ + mozilla->priv = gclue_mozilla_get_instance_private (mozilla); + mozilla->priv->wifi = NULL; + mozilla->priv->tower_valid = FALSE; + mozilla->priv->bss_submitted = FALSE; +} + +static void +gclue_mozilla_class_init (GClueMozillaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gclue_mozilla_finalize; +} + +GClueMozilla * +gclue_mozilla_get_singleton (void) +{ + static GClueMozilla *mozilla = NULL; + + if (!mozilla) { + mozilla = g_object_new (GCLUE_TYPE_MOZILLA, NULL); + g_object_add_weak_pointer (G_OBJECT (mozilla), (gpointer) &mozilla); + } else + g_object_ref (mozilla); + + return mozilla; +} + +void +gclue_mozilla_set_wifi (GClueMozilla *mozilla, + GClueWifi *wifi) +{ + g_return_if_fail (GCLUE_IS_MOZILLA (mozilla)); + + if (mozilla->priv->wifi == wifi) + return; + + g_clear_weak_pointer (&mozilla->priv->wifi); + + if (!wifi) { + return; + } + + mozilla->priv->wifi = wifi; + g_object_add_weak_pointer (G_OBJECT (mozilla->priv->wifi), + (gpointer) &mozilla->priv->wifi); +} + +gboolean +gclue_mozilla_test_set_wifi (GClueMozilla *mozilla, + GClueWifi *old, GClueWifi *new) +{ + if (mozilla->priv->wifi != old) + return FALSE; + + gclue_mozilla_set_wifi (mozilla, new); + return TRUE; +} + +void +gclue_mozilla_set_bss_dirty (GClueMozilla *mozilla) +{ + g_return_if_fail (GCLUE_IS_MOZILLA (mozilla)); + + mozilla->priv->bss_submitted = FALSE; +} + +static gboolean gclue_mozilla_tower_identical (const GClue3GTower *t1, + const GClue3GTower *t2) +{ + return g_strcmp0 (t1->opc, t2->opc) == 0 && t1->lac == t2->lac && + t1->cell_id == t2->cell_id && t1->tec == t2->tec; +} + +void +gclue_mozilla_set_tower (GClueMozilla *mozilla, + const GClue3GTower *tower) +{ + g_return_if_fail (GCLUE_IS_MOZILLA (mozilla)); + + if (!tower) { + mozilla->priv->tower_valid = FALSE; + return; + } + + if (mozilla->priv->tower_valid && + mozilla->priv->tower_submitted) { + mozilla->priv->tower_submitted = + gclue_mozilla_tower_identical (&mozilla->priv->tower, + tower); + } else + mozilla->priv->tower_submitted = FALSE; + + mozilla->priv->tower = *tower; + mozilla->priv->tower_valid = TRUE; +} + +gboolean +gclue_mozilla_has_tower (GClueMozilla *mozilla) +{ + g_return_val_if_fail (GCLUE_IS_MOZILLA (mozilla), FALSE); + + return mozilla->priv->tower_valid; +} + +GClue3GTower * +gclue_mozilla_get_tower (GClueMozilla *mozilla) +{ + g_return_val_if_fail (GCLUE_IS_MOZILLA (mozilla), NULL); + + if (!mozilla->priv->tower_valid) + return NULL; + + return &mozilla->priv->tower; +} diff --git a/src/gclue-mozilla.h b/src/gclue-mozilla.h index db859e5..c284006 100644 --- a/src/gclue-mozilla.h +++ b/src/gclue-mozilla.h @@ -30,17 +30,63 @@ G_BEGIN_DECLS +#define GCLUE_TYPE_MOZILLA (gclue_mozilla_get_type()) +#define GCLUE_MOZILLA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCLUE_TYPE_MOZILLA, GClueMozilla)) +#define GCLUE_MOZILLA_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCLUE_TYPE_MOZILLA, GClueMozilla const)) +#define GCLUE_MOZILLA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCLUE_TYPE_MOZILLA, GClueMozillaClass)) +#define GCLUE_IS_MOZILLA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCLUE_TYPE_MOZILLA)) +#define GCLUE_IS_MOZILLA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCLUE_TYPE_MOZILLA)) +#define GCLUE_MOZILLA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCLUE_TYPE_MOZILLA, GClueMozillaClass)) + +typedef struct _GClueMozilla GClueMozilla; +typedef struct _GClueMozillaClass GClueMozillaClass; +typedef struct _GClueMozillaPrivate GClueMozillaPrivate; + +struct _GClueMozilla +{ + GObject parent; + + /*< private >*/ + GClueMozillaPrivate *priv; +}; + +struct _GClueMozillaClass +{ + GObjectClass parent_class; +}; + +struct GClueWifi; +typedef struct _GClueWifi GClueWifi; + +GType gclue_mozilla_get_type (void) G_GNUC_CONST; + +GClueMozilla *gclue_mozilla_get_singleton (void); + +void gclue_mozilla_set_wifi (GClueMozilla *mozilla, + GClueWifi *wifi); +gboolean +gclue_mozilla_test_set_wifi (GClueMozilla *mozilla, + GClueWifi *old, GClueWifi *new); +void gclue_mozilla_set_bss_dirty (GClueMozilla *mozilla); + +void gclue_mozilla_set_tower (GClueMozilla *mozilla, + const GClue3GTower *tower); +gboolean +gclue_mozilla_has_tower (GClueMozilla *mozilla); +GClue3GTower * +gclue_mozilla_get_tower (GClueMozilla *mozilla); + SoupMessage * -gclue_mozilla_create_query (GList *bss_list, /* As in Access Points */ - GClue3GTower *tower, +gclue_mozilla_create_query (GClueMozilla *mozilla, + gboolean skip_tower, + gboolean skip_bss, GError **error); GClueLocation * gclue_mozilla_parse_response (const char *json, GError **error); SoupMessage * -gclue_mozilla_create_submit_query (GClueLocation *location, - GList *bss_list, /* As in Access Points */ - GClue3GTower *tower, +gclue_mozilla_create_submit_query (GClueMozilla *mozilla, + GClueLocation *location, GError **error); gboolean gclue_mozilla_should_ignore_bss (WPABSS *bss); diff --git a/src/gclue-wifi.c b/src/gclue-wifi.c index 1c309eb..5d3685b 100644 --- a/src/gclue-wifi.c +++ b/src/gclue-wifi.c @@ -24,6 +24,7 @@ #include <string.h> #include <config.h> #include "gclue-wifi.h" +#include "gclue-3g.h" #include "gclue-config.h" #include "gclue-error.h" #include "gclue-mozilla.h" @@ -134,6 +135,7 @@ static void location_cache_value_free (gpointer data) } struct _GClueWifiPrivate { + GClueMozilla *mozilla; WPASupplicant *supplicant; WPAInterface *interface; GHashTable *bss_proxies; @@ -203,6 +205,7 @@ gclue_wifi_finalize (GObject *gwifi) g_clear_pointer (&wifi->priv->bss_proxies, g_hash_table_unref); g_clear_pointer (&wifi->priv->ignored_bss_proxies, g_hash_table_unref); g_clear_pointer (&wifi->priv->location_cache, g_hash_table_unref); + g_clear_object (&wifi->priv->mozilla); } static void @@ -499,6 +502,11 @@ on_scan_timeout (gpointer user_data) return G_SOURCE_REMOVE; } +gboolean gclue_wifi_should_skip_bsss (GClueAccuracyLevel level) +{ + return level < GCLUE_ACCURACY_LEVEL_STREET; +} + static gboolean on_scan_wait_done (gpointer wifi) { @@ -507,9 +515,13 @@ on_scan_wait_done (gpointer wifi) g_return_val_if_fail (GCLUE_IS_WIFI (wifi), G_SOURCE_REMOVE); priv = GCLUE_WIFI(wifi)->priv; + /* We have the latest scan result */ + gclue_mozilla_set_wifi (priv->mozilla, wifi); + if (priv->bss_list_changed) { priv->bss_list_changed = FALSE; g_debug ("Refreshing location…"); + gclue_mozilla_set_bss_dirty (priv->mozilla); gclue_web_source_refresh (GCLUE_WEB_SOURCE (wifi)); } priv->scan_wait_id = 0; @@ -811,6 +823,8 @@ gclue_wifi_start (GClueLocationSource *source) static GClueLocationSourceStopResult gclue_wifi_stop (GClueLocationSource *source) { + GClueWifi *wifi = GCLUE_WIFI (source); + GClueWifiPrivate *priv = wifi->priv; GClueLocationSourceClass *base_class; GClueLocationSourceStopResult base_result; @@ -824,6 +838,10 @@ gclue_wifi_stop (GClueLocationSource *source) disconnect_bss_signals (GCLUE_WIFI (source)); disconnect_cache_prune_timeout (GCLUE_WIFI (source)); + if (gclue_mozilla_test_set_wifi (priv->mozilla, wifi, NULL)) { + g_debug ("Removed us as the WiFi source on stop"); + } + return base_result; } @@ -836,11 +854,10 @@ gclue_wifi_get_available_accuracy_level (GClueWebSource *source, if (!net_available) return GCLUE_ACCURACY_LEVEL_NONE; - else if (priv->interface != NULL && - get_accuracy_level (wifi) != GCLUE_ACCURACY_LEVEL_CITY) - return GCLUE_ACCURACY_LEVEL_STREET; - else + else if (!priv->interface) return GCLUE_ACCURACY_LEVEL_CITY; + else + return MIN (get_accuracy_level (wifi), GCLUE_ACCURACY_LEVEL_STREET); } static void @@ -918,6 +935,10 @@ on_interface_removed (WPASupplicant *supplicant, disconnect_bss_signals (wifi); g_clear_object (&wifi->priv->interface); + if (gclue_mozilla_test_set_wifi (priv->mozilla, wifi, NULL)) { + g_debug ("Removed interface was the WiFi source"); + } + gclue_web_source_refresh (GCLUE_WEB_SOURCE (wifi)); } @@ -926,6 +947,7 @@ gclue_wifi_init (GClueWifi *wifi) { wifi->priv = gclue_wifi_get_instance_private (wifi); + wifi->priv->mozilla = gclue_mozilla_get_singleton (); wifi->priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, @@ -1012,23 +1034,29 @@ on_wifi_destroyed (gpointer data, GClueWifi * gclue_wifi_get_singleton (GClueAccuracyLevel level) { - static GClueWifi *wifi[] = { NULL, NULL }; + static GClueWifi *wifi[] = { NULL, NULL, NULL }; guint i; + GClueConfig *config = gclue_config_get_singleton (); + gboolean wifi_enabled; gboolean scramble_location = FALSE; gboolean compute_movement = FALSE; g_return_val_if_fail (level >= GCLUE_ACCURACY_LEVEL_CITY, NULL); - if (level == GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD) - level = GCLUE_ACCURACY_LEVEL_CITY; + wifi_enabled = gclue_config_get_enable_wifi_source (config); if (level == GCLUE_ACCURACY_LEVEL_CITY) { - GClueConfig *config = gclue_config_get_singleton (); - i = 0; - if (gclue_config_get_enable_wifi_source (config)) + if (wifi_enabled) scramble_location = TRUE; - } else { + } else if (level == GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD) { + g_return_val_if_fail (wifi_enabled, NULL); + i = 1; + scramble_location = TRUE; + } else { + g_return_val_if_fail (wifi_enabled, NULL); + + i = 2; compute_movement = TRUE; } @@ -1048,8 +1076,8 @@ gclue_wifi_get_singleton (GClueAccuracyLevel level) } /* Can return NULL, signifying an empty BSS list. */ -static GList * -get_bss_list (GClueWifi *wifi) +GList * +gclue_wifi_get_bss_list (GClueWifi *wifi) { return g_hash_table_get_values (wifi->priv->bss_proxies); } @@ -1059,17 +1087,16 @@ gclue_wifi_create_query (GClueWebSource *source, GError **error) { GClueWifi *wifi = GCLUE_WIFI (source); - GList *bss_list = NULL; /* As in Access Points */ + GClueAccuracyLevel level; + gboolean skip_tower; SoupMessage *msg; if (wifi->priv->interface == NULL) { goto create_query; } - bss_list = get_bss_list (wifi); - /* Empty list? */ - if (bss_list == NULL) { + if (!g_hash_table_size (wifi->priv->bss_proxies)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -1078,8 +1105,14 @@ gclue_wifi_create_query (GClueWebSource *source, } create_query: - msg = gclue_mozilla_create_query (bss_list, NULL, error); - g_list_free (bss_list); + level = get_accuracy_level (wifi); + skip_tower = gclue_3g_should_skip_tower (level); + if (skip_tower) { + g_debug ("Will skip 3GPP tower in query as our accuracy level is %d", + (int)level); + } + + msg = gclue_mozilla_create_query (wifi->priv->mozilla, skip_tower, FALSE, error); return msg; } @@ -1097,7 +1130,6 @@ gclue_wifi_create_submit_query (GClueWebSource *source, GError **error) { GClueWifi *wifi = GCLUE_WIFI (source); - GList *bss_list; /* As in Access Points */ SoupMessage * msg; if (wifi->priv->interface == NULL) { @@ -1108,10 +1140,8 @@ gclue_wifi_create_submit_query (GClueWebSource *source, return NULL; } - bss_list = get_bss_list (wifi); - /* Empty list? */ - if (bss_list == NULL) { + if (!g_hash_table_size (wifi->priv->bss_proxies)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -1119,11 +1149,9 @@ gclue_wifi_create_submit_query (GClueWebSource *source, return NULL; } - msg = gclue_mozilla_create_submit_query (location, - bss_list, - NULL, + msg = gclue_mozilla_create_submit_query (wifi->priv->mozilla, + location, error); - g_list_free (bss_list); return msg; } @@ -1166,6 +1194,36 @@ variant_hash (gconstpointer key) return g_bytes_hash (bytes); } +static void location_cache_key_fill_tower (GClueWifi *wifi, GClue3GTower *tower) +{ + GClueWifiPrivate *priv = wifi->priv; + GClue3GTower *moztower; + GClueAccuracyLevel level; + + memset (tower, 0, sizeof (*tower)); + tower->tec = GCLUE_TOWER_TEC_NO_FIX; + + moztower = gclue_mozilla_get_tower (priv->mozilla); + level = get_accuracy_level (wifi); + if (!moztower || gclue_3g_should_skip_tower (level)) { + return; + } + + g_assert (moztower->tec != GCLUE_TOWER_TEC_NO_FIX); + *tower = *moztower; +} + +static void location_cache_key_add_tower (GClueWifi *wifi, GVariantBuilder *builder) +{ + GClue3GTower tower; + + location_cache_key_fill_tower (wifi, &tower); + g_variant_builder_add (builder, "u", (guint32)tower.tec); + g_variant_builder_add (builder, "s", tower.opc); + g_variant_builder_add (builder, "t", (guint64)tower.lac); + g_variant_builder_add (builder, "t", (guint64)tower.cell_id); +} + static GPtrArray * get_location_cache_bss_array (GClueWifi *wifi) { @@ -1198,7 +1256,10 @@ get_location_cache_hashtable_key (GClueWifi *wifi, GPtrArray *bss_array) GVariantBuilder builder; /* Serialise to a variant. */ - g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay")); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("(usttaay)")); + location_cache_key_add_tower (wifi, &builder); + + g_variant_builder_open (&builder, G_VARIANT_TYPE ("aay")); for (i = 0; i < bss_array->len; i++) { WPABSS *bss = WPA_BSS (bss_array->pdata[i]); GVariant *bssid; @@ -1209,6 +1270,7 @@ get_location_cache_hashtable_key (GClueWifi *wifi, GPtrArray *bss_array) g_variant_builder_add_value (&builder, bssid); } + g_variant_builder_close (&builder); return g_variant_builder_end (&builder); } diff --git a/src/gclue-wifi.h b/src/gclue-wifi.h index d64de5c..29ee9ce 100644 --- a/src/gclue-wifi.h +++ b/src/gclue-wifi.h @@ -63,6 +63,8 @@ struct _GClueWifiClass { }; GClueWifi * gclue_wifi_get_singleton (GClueAccuracyLevel level); +gboolean gclue_wifi_should_skip_bsss (GClueAccuracyLevel level); +GList *gclue_wifi_get_bss_list (GClueWifi *wifi); G_END_DECLS |