summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaciej S. Szmigiero <mail@maciej.szmigiero.name>2021-11-19 14:36:02 +0100
committerMaciej S. Szmigiero <mail@maciej.szmigiero.name>2022-08-07 17:12:20 +0200
commit0de88a82727ab2a3a156df63206453556d80d707 (patch)
treec62a6e1b163b3735debed810e62d62bf731a7f46
parent8b07a08ed5c611a0f7db9f41ca87a3711489797c (diff)
downloadgeoclue-0de88a82727ab2a3a156df63206453556d80d707.tar.gz
Submit MLS requests with combined WiFi and 3GPP tower data
Currently, there is no combining of WiFi Access Points data and 3GPP tower data to make a single, combined MLS geolocation request. Instead, the code does two separate MLS requests if both of these sources are available, each containing only either WiFi AP data or 3GPP tower data. This results in two different location determinations. If these two locations differ (which happens often) the Geoclue location jumps between these two locations as WiFi and 3GPP location sources are refreshed. There is also a problem of reduced accuracy since with two separate requests the MLS backend can't cross-correlate data between both. Instead, one is supposed to send a MLS query containing all the collected data at the same time, like the example at MLS API page shows. The same goes for MLS location submissions. Sending WiFi and 3GPP tower data combined makes Geoclue location determination significantly more stable indeed when both of these sources are active.
-rw-r--r--src/gclue-3g.c172
-rw-r--r--src/gclue-3g.h10
-rw-r--r--src/gclue-locator.c2
-rw-r--r--src/gclue-mozilla.c199
-rw-r--r--src/gclue-mozilla.h56
-rw-r--r--src/gclue-wifi.c118
-rw-r--r--src/gclue-wifi.h2
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