summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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