Lines 24-29
Link Here
|
24 |
#include <string.h> |
24 |
#include <string.h> |
25 |
#include <config.h> |
25 |
#include <config.h> |
26 |
#include "gclue-wifi.h" |
26 |
#include "gclue-wifi.h" |
|
|
27 |
#include "gclue-3g.h" |
27 |
#include "gclue-config.h" |
28 |
#include "gclue-config.h" |
28 |
#include "gclue-error.h" |
29 |
#include "gclue-error.h" |
29 |
#include "gclue-mozilla.h" |
30 |
#include "gclue-mozilla.h" |
Lines 49-54
Link Here
|
49 |
* full cache (excluding overheads). */ |
50 |
* full cache (excluding overheads). */ |
50 |
#define CACHE_ENTRY_MAX_AGE_SECONDS (48 * 60 * 60) |
51 |
#define CACHE_ENTRY_MAX_AGE_SECONDS (48 * 60 * 60) |
51 |
|
52 |
|
|
|
53 |
/* The signal strength can typically vary by ±5 for a stationary laptop, so |
54 |
* match cache entries with that tolerance. |
55 |
* In dBm units. |
56 |
*/ |
57 |
#define CACHE_ENTRY_MATCH_SIGNAL_WINDOW 10 |
58 |
|
52 |
/** |
59 |
/** |
53 |
* SECTION:gclue-wifi |
60 |
* SECTION:gclue-wifi |
54 |
* @short_description: WiFi-based geolocation |
61 |
* @short_description: WiFi-based geolocation |
Lines 78-84
Link Here
|
78 |
static void |
85 |
static void |
79 |
disconnect_cache_prune_timeout (GClueWifi *wifi); |
86 |
disconnect_cache_prune_timeout (GClueWifi *wifi); |
80 |
|
87 |
|
|
|
88 |
typedef struct { |
89 |
GArray *signals; |
90 |
GClueLocation *location; |
91 |
} LocationCacheElement; |
92 |
|
93 |
static LocationCacheElement * |
94 |
location_cache_element_new (GArray *signals, |
95 |
GClueLocation *location) |
96 |
{ |
97 |
LocationCacheElement *element; |
98 |
|
99 |
element = g_slice_new (LocationCacheElement); |
100 |
element->signals = signals; |
101 |
element->location = g_object_ref (location); |
102 |
return element; |
103 |
} |
104 |
|
105 |
static void location_cache_element_free (gpointer data) |
106 |
{ |
107 |
LocationCacheElement *element = data; |
108 |
|
109 |
if (element->signals) |
110 |
g_array_free (element->signals, TRUE); |
111 |
g_clear_object (&element->location); |
112 |
g_slice_free (LocationCacheElement, element); |
113 |
} |
114 |
|
115 |
typedef struct { |
116 |
GList *elements; |
117 |
} LocationCacheValue; |
118 |
|
119 |
static LocationCacheValue * |
120 |
location_cache_value_new (void) |
121 |
{ |
122 |
LocationCacheValue *value; |
123 |
|
124 |
value = g_slice_new (LocationCacheValue); |
125 |
value->elements = NULL; |
126 |
return value; |
127 |
} |
128 |
|
129 |
static void location_cache_value_free (gpointer data) |
130 |
{ |
131 |
LocationCacheValue *value = data; |
132 |
|
133 |
g_list_free_full (value->elements, location_cache_element_free); |
134 |
g_slice_free (LocationCacheValue, value); |
135 |
} |
136 |
|
81 |
struct _GClueWifiPrivate { |
137 |
struct _GClueWifiPrivate { |
|
|
138 |
GCancellable *intf_cancellable, *bss_cancellable; |
139 |
GClueMozilla *mozilla; |
82 |
WPASupplicant *supplicant; |
140 |
WPASupplicant *supplicant; |
83 |
WPAInterface *interface; |
141 |
WPAInterface *interface; |
84 |
GHashTable *bss_proxies; |
142 |
GHashTable *bss_proxies; |
Lines 92-100
Link Here
|
92 |
|
150 |
|
93 |
guint scan_timeout; |
151 |
guint scan_timeout; |
94 |
|
152 |
|
95 |
GClueAccuracyLevel accuracy_level; |
153 |
GHashTable *location_cache; /* (element-type GVariant LocationCacheValue) (owned) */ |
96 |
|
|
|
97 |
GHashTable *location_cache; /* (element-type GVariant GClueLocation) (owned) */ |
98 |
guint cache_prune_timeout_id; |
154 |
guint cache_prune_timeout_id; |
99 |
guint cache_hits, cache_misses; |
155 |
guint cache_hits, cache_misses; |
100 |
|
156 |
|
Lines 104-128
Link Here
|
104 |
#endif |
160 |
#endif |
105 |
}; |
161 |
}; |
106 |
|
162 |
|
107 |
enum |
|
|
108 |
{ |
109 |
PROP_0, |
110 |
PROP_ACCURACY_LEVEL, |
111 |
LAST_PROP |
112 |
}; |
113 |
static GParamSpec *gParamSpecs[LAST_PROP]; |
114 |
|
115 |
static SoupMessage * |
163 |
static SoupMessage * |
116 |
gclue_wifi_create_query (GClueWebSource *source, |
164 |
gclue_wifi_create_query (GClueWebSource *source, |
|
|
165 |
const char **query_data_description, |
117 |
GError **error); |
166 |
GError **error); |
118 |
static SoupMessage * |
167 |
static SoupMessage * |
119 |
gclue_wifi_create_submit_query (GClueWebSource *source, |
168 |
gclue_wifi_create_submit_query (GClueWebSource *source, |
120 |
GClueLocation *location, |
169 |
GClueLocation *location, |
121 |
GError **error); |
170 |
GError **error); |
122 |
static GClueLocation * |
|
|
123 |
gclue_wifi_parse_response (GClueWebSource *source, |
124 |
const char *json, |
125 |
GError **error); |
126 |
static GClueAccuracyLevel |
171 |
static GClueAccuracyLevel |
127 |
gclue_wifi_get_available_accuracy_level (GClueWebSource *source, |
172 |
gclue_wifi_get_available_accuracy_level (GClueWebSource *source, |
128 |
gboolean net_available); |
173 |
gboolean net_available); |
Lines 150-155
Link Here
|
150 |
|
195 |
|
151 |
G_OBJECT_CLASS (gclue_wifi_parent_class)->finalize (gwifi); |
196 |
G_OBJECT_CLASS (gclue_wifi_parent_class)->finalize (gwifi); |
152 |
|
197 |
|
|
|
198 |
g_cancellable_cancel (wifi->priv->intf_cancellable); |
199 |
|
153 |
disconnect_bss_signals (wifi); |
200 |
disconnect_bss_signals (wifi); |
154 |
disconnect_cache_prune_timeout (wifi); |
201 |
disconnect_cache_prune_timeout (wifi); |
155 |
|
202 |
|
Lines 158-205
Link Here
|
158 |
g_clear_pointer (&wifi->priv->bss_proxies, g_hash_table_unref); |
205 |
g_clear_pointer (&wifi->priv->bss_proxies, g_hash_table_unref); |
159 |
g_clear_pointer (&wifi->priv->ignored_bss_proxies, g_hash_table_unref); |
206 |
g_clear_pointer (&wifi->priv->ignored_bss_proxies, g_hash_table_unref); |
160 |
g_clear_pointer (&wifi->priv->location_cache, g_hash_table_unref); |
207 |
g_clear_pointer (&wifi->priv->location_cache, g_hash_table_unref); |
|
|
208 |
g_clear_object (&wifi->priv->mozilla); |
209 |
g_clear_object (&wifi->priv->intf_cancellable); |
161 |
} |
210 |
} |
162 |
|
211 |
|
163 |
static void |
212 |
static void |
164 |
gclue_wifi_constructed (GObject *object); |
213 |
gclue_wifi_constructed (GObject *object); |
165 |
|
214 |
|
166 |
static void |
215 |
static void |
167 |
gclue_wifi_get_property (GObject *object, |
|
|
168 |
guint prop_id, |
169 |
GValue *value, |
170 |
GParamSpec *pspec) |
171 |
{ |
172 |
GClueWifi *wifi = GCLUE_WIFI (object); |
173 |
|
174 |
switch (prop_id) { |
175 |
case PROP_ACCURACY_LEVEL: |
176 |
g_value_set_enum (value, wifi->priv->accuracy_level); |
177 |
break; |
178 |
|
179 |
default: |
180 |
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
181 |
} |
182 |
} |
183 |
|
184 |
static void |
185 |
gclue_wifi_set_property (GObject *object, |
186 |
guint prop_id, |
187 |
const GValue *value, |
188 |
GParamSpec *pspec) |
189 |
{ |
190 |
GClueWifi *wifi = GCLUE_WIFI (object); |
191 |
|
192 |
switch (prop_id) { |
193 |
case PROP_ACCURACY_LEVEL: |
194 |
wifi->priv->accuracy_level = g_value_get_enum (value); |
195 |
break; |
196 |
|
197 |
default: |
198 |
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
199 |
} |
200 |
} |
201 |
|
202 |
static void |
203 |
gclue_wifi_class_init (GClueWifiClass *klass) |
216 |
gclue_wifi_class_init (GClueWifiClass *klass) |
204 |
{ |
217 |
{ |
205 |
GClueWebSourceClass *web_class = GCLUE_WEB_SOURCE_CLASS (klass); |
218 |
GClueWebSourceClass *web_class = GCLUE_WEB_SOURCE_CLASS (klass); |
Lines 212-235
Link Here
|
212 |
web_class->refresh_finish = gclue_wifi_refresh_finish; |
225 |
web_class->refresh_finish = gclue_wifi_refresh_finish; |
213 |
web_class->create_submit_query = gclue_wifi_create_submit_query; |
226 |
web_class->create_submit_query = gclue_wifi_create_submit_query; |
214 |
web_class->create_query = gclue_wifi_create_query; |
227 |
web_class->create_query = gclue_wifi_create_query; |
215 |
web_class->parse_response = gclue_wifi_parse_response; |
|
|
216 |
web_class->get_available_accuracy_level = |
228 |
web_class->get_available_accuracy_level = |
217 |
gclue_wifi_get_available_accuracy_level; |
229 |
gclue_wifi_get_available_accuracy_level; |
218 |
gwifi_class->get_property = gclue_wifi_get_property; |
|
|
219 |
gwifi_class->set_property = gclue_wifi_set_property; |
220 |
gwifi_class->finalize = gclue_wifi_finalize; |
230 |
gwifi_class->finalize = gclue_wifi_finalize; |
221 |
gwifi_class->constructed = gclue_wifi_constructed; |
231 |
gwifi_class->constructed = gclue_wifi_constructed; |
222 |
|
|
|
223 |
gParamSpecs[PROP_ACCURACY_LEVEL] = g_param_spec_enum ("accuracy-level", |
224 |
"AccuracyLevel", |
225 |
"Max accuracy level", |
226 |
GCLUE_TYPE_ACCURACY_LEVEL, |
227 |
GCLUE_ACCURACY_LEVEL_CITY, |
228 |
G_PARAM_READWRITE | |
229 |
G_PARAM_CONSTRUCT_ONLY); |
230 |
g_object_class_install_property (gwifi_class, |
231 |
PROP_ACCURACY_LEVEL, |
232 |
gParamSpecs[PROP_ACCURACY_LEVEL]); |
233 |
} |
232 |
} |
234 |
|
233 |
|
235 |
static void |
234 |
static void |
Lines 345-363
Link Here
|
345 |
GAsyncResult *res, |
344 |
GAsyncResult *res, |
346 |
gpointer user_data) |
345 |
gpointer user_data) |
347 |
{ |
346 |
{ |
348 |
GClueWifi *wifi = GCLUE_WIFI (user_data); |
347 |
GClueWifi *wifi; |
349 |
WPABSS *bss; |
348 |
WPABSS *bss; |
350 |
GError *error = NULL; |
349 |
g_autoptr(GError) error = NULL; |
351 |
char ssid[MAX_SSID_LEN + 1] = { 0 }; |
350 |
char ssid[MAX_SSID_LEN + 1] = { 0 }; |
352 |
|
351 |
|
353 |
bss = wpa_bss_proxy_new_for_bus_finish (res, &error); |
352 |
bss = wpa_bss_proxy_new_for_bus_finish (res, &error); |
354 |
if (bss == NULL) { |
353 |
if (bss == NULL) { |
355 |
g_debug ("%s", error->message); |
354 |
if (error && !g_error_matches (error, G_IO_ERROR, |
356 |
g_error_free (error); |
355 |
G_IO_ERROR_CANCELLED)) { |
|
|
356 |
g_warning ("BSS proxy setup failed: %s", |
357 |
error->message); |
358 |
} |
357 |
|
359 |
|
358 |
return; |
360 |
return; |
359 |
} |
361 |
} |
360 |
|
362 |
|
|
|
363 |
wifi = GCLUE_WIFI (user_data); |
364 |
|
361 |
if (gclue_mozilla_should_ignore_bss (bss)) { |
365 |
if (gclue_mozilla_should_ignore_bss (bss)) { |
362 |
g_object_unref (bss); |
366 |
g_object_unref (bss); |
363 |
|
367 |
|
Lines 365-371
Link Here
|
365 |
} |
369 |
} |
366 |
|
370 |
|
367 |
get_ssid_from_bss (bss, ssid); |
371 |
get_ssid_from_bss (bss, ssid); |
368 |
g_debug ("WiFi AP '%s' added.", ssid); |
372 |
g_debug ("Got WiFi AP '%s'", ssid); |
369 |
|
373 |
|
370 |
if (wpa_bss_get_signal (bss) <= WIFI_SCAN_BSS_NOISE_LEVEL) { |
374 |
if (wpa_bss_get_signal (bss) <= WIFI_SCAN_BSS_NOISE_LEVEL) { |
371 |
const char *path; |
375 |
const char *path; |
Lines 396-408
Link Here
|
396 |
GVariant *properties, |
400 |
GVariant *properties, |
397 |
gpointer user_data) |
401 |
gpointer user_data) |
398 |
{ |
402 |
{ |
|
|
403 |
GClueWifi *wifi = GCLUE_WIFI (user_data); |
404 |
|
399 |
wpa_bss_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, |
405 |
wpa_bss_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, |
400 |
G_DBUS_PROXY_FLAGS_NONE, |
406 |
G_DBUS_PROXY_FLAGS_NONE, |
401 |
"fi.w1.wpa_supplicant1", |
407 |
"fi.w1.wpa_supplicant1", |
402 |
path, |
408 |
path, |
403 |
NULL, |
409 |
wifi->priv->bss_cancellable, |
404 |
on_bss_proxy_ready, |
410 |
on_bss_proxy_ready, |
405 |
user_data); |
411 |
wifi); |
406 |
} |
412 |
} |
407 |
|
413 |
|
408 |
static gboolean |
414 |
static gboolean |
Lines 459-465
Link Here
|
459 |
|
465 |
|
460 |
wpa_interface_call_scan (WPA_INTERFACE (priv->interface), |
466 |
wpa_interface_call_scan (WPA_INTERFACE (priv->interface), |
461 |
args, |
467 |
args, |
462 |
NULL, |
468 |
priv->bss_cancellable, |
463 |
on_scan_call_done, |
469 |
on_scan_call_done, |
464 |
wifi); |
470 |
wifi); |
465 |
} |
471 |
} |
Lines 492-498
Link Here
|
492 |
GClueWifi *wifi = GCLUE_WIFI (user_data); |
498 |
GClueWifi *wifi = GCLUE_WIFI (user_data); |
493 |
GClueWifiPrivate *priv = wifi->priv; |
499 |
GClueWifiPrivate *priv = wifi->priv; |
494 |
|
500 |
|
495 |
g_debug ("WiFi scan timeout."); |
|
|
496 |
priv->scan_timeout = 0; |
501 |
priv->scan_timeout = 0; |
497 |
|
502 |
|
498 |
if (priv->interface == NULL) |
503 |
if (priv->interface == NULL) |
Lines 503-508
Link Here
|
503 |
return G_SOURCE_REMOVE; |
508 |
return G_SOURCE_REMOVE; |
504 |
} |
509 |
} |
505 |
|
510 |
|
|
|
511 |
gboolean gclue_wifi_should_skip_bsss (GClueAccuracyLevel level) |
512 |
{ |
513 |
return level < GCLUE_ACCURACY_LEVEL_STREET; |
514 |
} |
515 |
|
506 |
static gboolean |
516 |
static gboolean |
507 |
on_scan_wait_done (gpointer wifi) |
517 |
on_scan_wait_done (gpointer wifi) |
508 |
{ |
518 |
{ |
Lines 511-519
Link Here
|
511 |
g_return_val_if_fail (GCLUE_IS_WIFI (wifi), G_SOURCE_REMOVE); |
521 |
g_return_val_if_fail (GCLUE_IS_WIFI (wifi), G_SOURCE_REMOVE); |
512 |
priv = GCLUE_WIFI(wifi)->priv; |
522 |
priv = GCLUE_WIFI(wifi)->priv; |
513 |
|
523 |
|
|
|
524 |
/* We have the latest scan result */ |
525 |
gclue_mozilla_set_wifi (priv->mozilla, wifi); |
526 |
|
514 |
if (priv->bss_list_changed) { |
527 |
if (priv->bss_list_changed) { |
515 |
priv->bss_list_changed = FALSE; |
528 |
priv->bss_list_changed = FALSE; |
516 |
g_debug ("Refreshing location…"); |
529 |
g_debug ("WiFi BSS list changed, refreshing location…"); |
|
|
530 |
gclue_mozilla_set_bss_dirty (priv->mozilla); |
517 |
gclue_web_source_refresh (GCLUE_WEB_SOURCE (wifi)); |
531 |
gclue_web_source_refresh (GCLUE_WEB_SOURCE (wifi)); |
518 |
} |
532 |
} |
519 |
priv->scan_wait_id = 0; |
533 |
priv->scan_wait_id = 0; |
Lines 521-526
Link Here
|
521 |
return G_SOURCE_REMOVE; |
535 |
return G_SOURCE_REMOVE; |
522 |
} |
536 |
} |
523 |
|
537 |
|
|
|
538 |
static GClueAccuracyLevel |
539 |
get_accuracy_level (GClueWifi *wifi) |
540 |
{ |
541 |
GClueAccuracyLevel level; |
542 |
|
543 |
g_object_get (G_OBJECT (wifi), "accuracy-level", &level, NULL); |
544 |
return level; |
545 |
} |
546 |
|
524 |
static void |
547 |
static void |
525 |
on_scan_done (WPAInterface *object, |
548 |
on_scan_done (WPAInterface *object, |
526 |
gboolean success, |
549 |
gboolean success, |
Lines 535-541
Link Here
|
535 |
|
558 |
|
536 |
return; |
559 |
return; |
537 |
} |
560 |
} |
538 |
g_debug ("WiFi scan completed"); |
|
|
539 |
|
561 |
|
540 |
if (priv->interface == NULL) |
562 |
if (priv->interface == NULL) |
541 |
return; |
563 |
return; |
Lines 558-571
Link Here
|
558 |
* user's location can change quickly. With low accuracy, we don't since |
580 |
* user's location can change quickly. With low accuracy, we don't since |
559 |
* we wouldn't want to drain power unnecessarily. |
581 |
* we wouldn't want to drain power unnecessarily. |
560 |
*/ |
582 |
*/ |
561 |
if (priv->accuracy_level >= GCLUE_ACCURACY_LEVEL_STREET) |
583 |
if (get_accuracy_level (wifi) >= GCLUE_ACCURACY_LEVEL_STREET) |
562 |
timeout = WIFI_SCAN_TIMEOUT_HIGH_ACCURACY; |
584 |
timeout = WIFI_SCAN_TIMEOUT_HIGH_ACCURACY; |
563 |
else |
585 |
else |
564 |
timeout = WIFI_SCAN_TIMEOUT_LOW_ACCURACY; |
586 |
timeout = WIFI_SCAN_TIMEOUT_LOW_ACCURACY; |
565 |
priv->scan_timeout = g_timeout_add_seconds (timeout, |
587 |
priv->scan_timeout = g_timeout_add_seconds (timeout, |
566 |
on_scan_timeout, |
588 |
on_scan_timeout, |
567 |
wifi); |
589 |
wifi); |
568 |
g_debug ("Next scan scheduled in %u seconds", timeout); |
590 |
g_debug ("WiFi scan done, next scheduled in %u seconds", timeout); |
569 |
} |
591 |
} |
570 |
|
592 |
|
571 |
static void |
593 |
static void |
Lines 573-592
Link Here
|
573 |
GAsyncResult *res, |
595 |
GAsyncResult *res, |
574 |
gpointer user_data) |
596 |
gpointer user_data) |
575 |
{ |
597 |
{ |
576 |
GClueWifi *wifi = GCLUE_WIFI (user_data); |
598 |
g_autoptr(GError) error = NULL; |
577 |
GError *error = NULL; |
|
|
578 |
|
599 |
|
579 |
if (!wpa_interface_call_scan_finish |
600 |
if (!wpa_interface_call_scan_finish (WPA_INTERFACE (source_object), |
580 |
(WPA_INTERFACE (source_object), |
601 |
res, &error)) { |
581 |
res, |
602 |
GClueWifi *wifi; |
582 |
&error)) { |
603 |
|
|
|
604 |
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { |
605 |
return; |
606 |
} |
607 |
|
608 |
wifi = GCLUE_WIFI (user_data); |
609 |
|
610 |
if (error) { |
583 |
g_warning ("Scanning of WiFi networks failed: %s", |
611 |
g_warning ("Scanning of WiFi networks failed: %s", |
584 |
error->message); |
612 |
error->message); |
585 |
g_error_free (error); |
613 |
} |
586 |
|
614 |
|
587 |
cancel_wifi_scan (wifi); |
615 |
cancel_wifi_scan (wifi); |
588 |
|
|
|
589 |
return; |
590 |
} |
616 |
} |
591 |
} |
617 |
} |
592 |
|
618 |
|
Lines 605-610
Link Here
|
605 |
return; |
631 |
return; |
606 |
} |
632 |
} |
607 |
|
633 |
|
|
|
634 |
g_assert (!priv->bss_cancellable); |
635 |
priv->bss_cancellable = g_cancellable_new (); |
636 |
|
608 |
start_wifi_scan (wifi); |
637 |
start_wifi_scan (wifi); |
609 |
|
638 |
|
610 |
priv->bss_list_changed = TRUE; |
639 |
priv->bss_list_changed = TRUE; |
Lines 633-638
Link Here
|
633 |
{ |
662 |
{ |
634 |
GClueWifiPrivate *priv = wifi->priv; |
663 |
GClueWifiPrivate *priv = wifi->priv; |
635 |
|
664 |
|
|
|
665 |
if (priv->bss_cancellable) { |
666 |
g_debug ("Cancelling WiFi requests"); |
667 |
g_cancellable_cancel (priv->bss_cancellable); |
668 |
g_clear_object (&priv->bss_cancellable); |
669 |
} |
670 |
|
636 |
cancel_wifi_scan (wifi); |
671 |
cancel_wifi_scan (wifi); |
637 |
|
672 |
|
638 |
if (priv->bss_added_id != 0) { |
673 |
if (priv->bss_added_id != 0) { |
Lines 657-678
Link Here
|
657 |
GHashTableIter iter; |
692 |
GHashTableIter iter; |
658 |
gpointer value; |
693 |
gpointer value; |
659 |
guint64 cutoff_seconds; |
694 |
guint64 cutoff_seconds; |
660 |
guint old_cache_size; |
695 |
guint old_cache_size, removed_elements = 0; |
661 |
|
696 |
|
662 |
old_cache_size = g_hash_table_size (priv->location_cache); |
697 |
old_cache_size = g_hash_table_size (priv->location_cache); |
663 |
cutoff_seconds = g_get_real_time () / G_USEC_PER_SEC - CACHE_ENTRY_MAX_AGE_SECONDS; |
698 |
cutoff_seconds = g_get_real_time () / G_USEC_PER_SEC - CACHE_ENTRY_MAX_AGE_SECONDS; |
664 |
|
699 |
|
665 |
g_hash_table_iter_init (&iter, priv->location_cache); |
700 |
g_hash_table_iter_init (&iter, priv->location_cache); |
666 |
while (g_hash_table_iter_next (&iter, NULL, &value)) { |
701 |
while (g_hash_table_iter_next (&iter, NULL, &value)) { |
667 |
GClueLocation *location = GCLUE_LOCATION (value); |
702 |
LocationCacheValue *lcvalue = (LocationCacheValue *)value; |
668 |
guint64 timestamp_seconds = gclue_location_get_timestamp (location); |
703 |
GList *l = lcvalue->elements; |
|
|
704 |
|
705 |
g_assert (l); |
706 |
while (l) { |
707 |
LocationCacheElement *element = (LocationCacheElement *)l->data; |
708 |
GList *lnext = l->next; |
709 |
|
710 |
/* Keep this location? */ |
711 |
if (gclue_location_get_timestamp (element->location) > |
712 |
cutoff_seconds) |
713 |
goto next_el; |
714 |
|
715 |
location_cache_element_free (element); |
716 |
lcvalue->elements = g_list_delete_link (lcvalue->elements, l); |
717 |
removed_elements++; |
669 |
|
718 |
|
670 |
if (timestamp_seconds <= cutoff_seconds) |
719 |
/* Deleted the last entry (element) in this hash bucket? |
|
|
720 |
* Remove this hash table entry then. |
721 |
*/ |
722 |
if (!lcvalue->elements) { |
723 |
g_assert (!lnext); |
671 |
g_hash_table_iter_remove (&iter); |
724 |
g_hash_table_iter_remove (&iter); |
672 |
} |
725 |
} |
673 |
|
726 |
|
674 |
g_debug ("Pruned cache (old size: %u, new size: %u)", |
727 |
next_el: |
675 |
old_cache_size, g_hash_table_size (priv->location_cache)); |
728 |
l = lnext; |
|
|
729 |
} |
730 |
} |
731 |
|
732 |
g_debug ("Pruned cache (old size: %u, new size: %u, removed elements: %u)", |
733 |
old_cache_size, g_hash_table_size (priv->location_cache), |
734 |
removed_elements); |
676 |
} |
735 |
} |
677 |
|
736 |
|
678 |
#if GLIB_CHECK_VERSION(2, 64, 0) |
737 |
#if GLIB_CHECK_VERSION(2, 64, 0) |
Lines 782-787
Link Here
|
782 |
static GClueLocationSourceStopResult |
841 |
static GClueLocationSourceStopResult |
783 |
gclue_wifi_stop (GClueLocationSource *source) |
842 |
gclue_wifi_stop (GClueLocationSource *source) |
784 |
{ |
843 |
{ |
|
|
844 |
GClueWifi *wifi = GCLUE_WIFI (source); |
845 |
GClueWifiPrivate *priv = wifi->priv; |
785 |
GClueLocationSourceClass *base_class; |
846 |
GClueLocationSourceClass *base_class; |
786 |
GClueLocationSourceStopResult base_result; |
847 |
GClueLocationSourceStopResult base_result; |
787 |
|
848 |
|
Lines 795-800
Link Here
|
795 |
disconnect_bss_signals (GCLUE_WIFI (source)); |
856 |
disconnect_bss_signals (GCLUE_WIFI (source)); |
796 |
disconnect_cache_prune_timeout (GCLUE_WIFI (source)); |
857 |
disconnect_cache_prune_timeout (GCLUE_WIFI (source)); |
797 |
|
858 |
|
|
|
859 |
if (gclue_mozilla_test_set_wifi (priv->mozilla, wifi, NULL)) { |
860 |
g_debug ("Removed us as the WiFi source on stop"); |
861 |
} |
862 |
|
798 |
return base_result; |
863 |
return base_result; |
799 |
} |
864 |
} |
800 |
|
865 |
|
Lines 802-816
Link Here
|
802 |
gclue_wifi_get_available_accuracy_level (GClueWebSource *source, |
867 |
gclue_wifi_get_available_accuracy_level (GClueWebSource *source, |
803 |
gboolean net_available) |
868 |
gboolean net_available) |
804 |
{ |
869 |
{ |
805 |
GClueWifiPrivate *priv = GCLUE_WIFI (source)->priv; |
870 |
GClueWifi *wifi = GCLUE_WIFI (source); |
|
|
871 |
GClueWifiPrivate *priv = wifi->priv; |
806 |
|
872 |
|
807 |
if (!net_available) |
873 |
if (!net_available) |
808 |
return GCLUE_ACCURACY_LEVEL_NONE; |
874 |
return GCLUE_ACCURACY_LEVEL_NONE; |
809 |
else if (priv->interface != NULL && |
875 |
else if (!priv->interface) |
810 |
priv->accuracy_level != GCLUE_ACCURACY_LEVEL_CITY) |
|
|
811 |
return GCLUE_ACCURACY_LEVEL_STREET; |
812 |
else |
813 |
return GCLUE_ACCURACY_LEVEL_CITY; |
876 |
return GCLUE_ACCURACY_LEVEL_CITY; |
|
|
877 |
else |
878 |
return MIN (get_accuracy_level (wifi), GCLUE_ACCURACY_LEVEL_STREET); |
814 |
} |
879 |
} |
815 |
|
880 |
|
816 |
static void |
881 |
static void |
Lines 818-835
Link Here
|
818 |
GAsyncResult *res, |
883 |
GAsyncResult *res, |
819 |
gpointer user_data) |
884 |
gpointer user_data) |
820 |
{ |
885 |
{ |
821 |
GClueWifi *wifi = GCLUE_WIFI (user_data); |
886 |
GClueWifi *wifi; |
822 |
WPAInterface *interface; |
887 |
WPAInterface *interface; |
823 |
GError *error = NULL; |
888 |
g_autoptr(GError) error = NULL; |
824 |
|
889 |
|
825 |
interface = wpa_interface_proxy_new_for_bus_finish (res, &error); |
890 |
interface = wpa_interface_proxy_new_for_bus_finish (res, &error); |
826 |
if (interface == NULL) { |
891 |
if (interface == NULL) { |
827 |
g_debug ("%s", error->message); |
892 |
if (error) { |
828 |
g_error_free (error); |
893 |
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { |
|
|
894 |
return; |
895 |
} |
896 |
|
897 |
g_warning ("Interface proxy add failed: %s", |
898 |
error->message); |
899 |
} |
829 |
|
900 |
|
830 |
return; |
901 |
return; |
831 |
} |
902 |
} |
832 |
|
903 |
|
|
|
904 |
wifi = GCLUE_WIFI (user_data); |
833 |
if (wifi->priv->interface != NULL) { |
905 |
if (wifi->priv->interface != NULL) { |
834 |
g_object_unref (interface); |
906 |
g_object_unref (interface); |
835 |
return; |
907 |
return; |
Lines 860-866
Link Here
|
860 |
G_DBUS_PROXY_FLAGS_NONE, |
932 |
G_DBUS_PROXY_FLAGS_NONE, |
861 |
"fi.w1.wpa_supplicant1", |
933 |
"fi.w1.wpa_supplicant1", |
862 |
path, |
934 |
path, |
863 |
NULL, |
935 |
wifi->priv->intf_cancellable, |
864 |
on_interface_proxy_ready, |
936 |
on_interface_proxy_ready, |
865 |
wifi); |
937 |
wifi); |
866 |
} |
938 |
} |
Lines 888-901
Link Here
|
888 |
disconnect_bss_signals (wifi); |
960 |
disconnect_bss_signals (wifi); |
889 |
g_clear_object (&wifi->priv->interface); |
961 |
g_clear_object (&wifi->priv->interface); |
890 |
|
962 |
|
|
|
963 |
if (gclue_mozilla_test_set_wifi (priv->mozilla, wifi, NULL)) { |
964 |
g_debug ("Removed interface was the WiFi source"); |
965 |
} |
966 |
|
891 |
gclue_web_source_refresh (GCLUE_WEB_SOURCE (wifi)); |
967 |
gclue_web_source_refresh (GCLUE_WEB_SOURCE (wifi)); |
892 |
} |
968 |
} |
893 |
|
969 |
|
894 |
static void |
970 |
static void |
895 |
gclue_wifi_init (GClueWifi *wifi) |
971 |
gclue_wifi_init (GClueWifi *wifi) |
896 |
{ |
972 |
{ |
|
|
973 |
GClueWebSource *web_source = GCLUE_WEB_SOURCE (wifi); |
974 |
|
897 |
wifi->priv = gclue_wifi_get_instance_private (wifi); |
975 |
wifi->priv = gclue_wifi_get_instance_private (wifi); |
898 |
|
976 |
|
|
|
977 |
wifi->priv->intf_cancellable = g_cancellable_new (); |
978 |
wifi->priv->mozilla = gclue_mozilla_get_singleton (); |
979 |
gclue_web_source_set_locate_url (web_source, |
980 |
gclue_mozilla_get_locate_url (wifi->priv->mozilla)); |
981 |
gclue_web_source_set_submit_url (web_source, |
982 |
gclue_mozilla_get_submit_url (wifi->priv->mozilla)); |
983 |
|
899 |
wifi->priv->bss_proxies = g_hash_table_new_full (g_str_hash, |
984 |
wifi->priv->bss_proxies = g_hash_table_new_full (g_str_hash, |
900 |
g_str_equal, |
985 |
g_str_equal, |
901 |
g_free, |
986 |
g_free, |
Lines 907-913
Link Here
|
907 |
wifi->priv->location_cache = g_hash_table_new_full (variant_hash, |
992 |
wifi->priv->location_cache = g_hash_table_new_full (variant_hash, |
908 |
g_variant_equal, |
993 |
g_variant_equal, |
909 |
(GDestroyNotify) g_variant_unref, |
994 |
(GDestroyNotify) g_variant_unref, |
910 |
g_object_unref); |
995 |
location_cache_value_free); |
911 |
} |
996 |
} |
912 |
|
997 |
|
913 |
static void |
998 |
static void |
Lines 916-926
Link Here
|
916 |
GClueWifi *wifi = GCLUE_WIFI (object); |
1001 |
GClueWifi *wifi = GCLUE_WIFI (object); |
917 |
GClueWifiPrivate *priv = wifi->priv; |
1002 |
GClueWifiPrivate *priv = wifi->priv; |
918 |
const gchar *const *interfaces; |
1003 |
const gchar *const *interfaces; |
919 |
GError *error = NULL; |
1004 |
g_autoptr(GError) error = NULL; |
920 |
|
1005 |
|
921 |
G_OBJECT_CLASS (gclue_wifi_parent_class)->constructed (object); |
1006 |
G_OBJECT_CLASS (gclue_wifi_parent_class)->constructed (object); |
922 |
|
1007 |
|
923 |
if (wifi->priv->accuracy_level == GCLUE_ACCURACY_LEVEL_CITY) { |
1008 |
if (get_accuracy_level (wifi) == GCLUE_ACCURACY_LEVEL_CITY) { |
924 |
GClueConfig *config = gclue_config_get_singleton (); |
1009 |
GClueConfig *config = gclue_config_get_singleton (); |
925 |
|
1010 |
|
926 |
if (!gclue_config_get_enable_wifi_source (config)) |
1011 |
if (!gclue_config_get_enable_wifi_source (config)) |
Lines 936-955
Link Here
|
936 |
NULL, |
1021 |
NULL, |
937 |
&error); |
1022 |
&error); |
938 |
if (priv->supplicant == NULL) { |
1023 |
if (priv->supplicant == NULL) { |
|
|
1024 |
if (error) |
939 |
g_warning ("Failed to connect to wpa_supplicant service: %s", |
1025 |
g_warning ("Failed to connect to wpa_supplicant service: %s", |
940 |
error->message); |
1026 |
error->message); |
941 |
g_error_free (error); |
|
|
942 |
goto refresh_n_exit; |
1027 |
goto refresh_n_exit; |
943 |
} |
1028 |
} |
944 |
|
1029 |
|
945 |
g_signal_connect (priv->supplicant, |
1030 |
g_signal_connect_object (priv->supplicant, |
946 |
"interface-added", |
1031 |
"interface-added", |
947 |
G_CALLBACK (on_interface_added), |
1032 |
G_CALLBACK (on_interface_added), |
948 |
wifi); |
1033 |
wifi, 0); |
949 |
g_signal_connect (priv->supplicant, |
1034 |
g_signal_connect_object (priv->supplicant, |
950 |
"interface-removed", |
1035 |
"interface-removed", |
951 |
G_CALLBACK (on_interface_removed), |
1036 |
G_CALLBACK (on_interface_removed), |
952 |
wifi); |
1037 |
wifi, 0); |
953 |
|
1038 |
|
954 |
interfaces = wpa_supplicant_get_interfaces (priv->supplicant); |
1039 |
interfaces = wpa_supplicant_get_interfaces (priv->supplicant); |
955 |
if (interfaces != NULL && interfaces[0] != NULL) |
1040 |
if (interfaces != NULL && interfaces[0] != NULL) |
Lines 982-1004
Link Here
|
982 |
GClueWifi * |
1067 |
GClueWifi * |
983 |
gclue_wifi_get_singleton (GClueAccuracyLevel level) |
1068 |
gclue_wifi_get_singleton (GClueAccuracyLevel level) |
984 |
{ |
1069 |
{ |
985 |
static GClueWifi *wifi[] = { NULL, NULL }; |
1070 |
static GClueWifi *wifi[] = { NULL, NULL, NULL }; |
986 |
guint i; |
1071 |
guint i; |
|
|
1072 |
GClueConfig *config = gclue_config_get_singleton (); |
1073 |
gboolean wifi_enabled; |
987 |
gboolean scramble_location = FALSE; |
1074 |
gboolean scramble_location = FALSE; |
988 |
gboolean compute_movement = FALSE; |
1075 |
gboolean compute_movement = FALSE; |
989 |
|
1076 |
|
990 |
g_return_val_if_fail (level >= GCLUE_ACCURACY_LEVEL_CITY, NULL); |
1077 |
g_return_val_if_fail (level >= GCLUE_ACCURACY_LEVEL_CITY, NULL); |
991 |
if (level == GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD) |
|
|
992 |
level = GCLUE_ACCURACY_LEVEL_CITY; |
993 |
|
1078 |
|
|
|
1079 |
wifi_enabled = gclue_config_get_enable_wifi_source (config); |
994 |
if (level == GCLUE_ACCURACY_LEVEL_CITY) { |
1080 |
if (level == GCLUE_ACCURACY_LEVEL_CITY) { |
995 |
GClueConfig *config = gclue_config_get_singleton (); |
|
|
996 |
|
997 |
i = 0; |
1081 |
i = 0; |
998 |
if (gclue_config_get_enable_wifi_source (config)) |
1082 |
if (wifi_enabled) |
999 |
scramble_location = TRUE; |
1083 |
scramble_location = TRUE; |
1000 |
} else { |
1084 |
} else if (level == GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD) { |
|
|
1085 |
g_return_val_if_fail (wifi_enabled, NULL); |
1086 |
|
1001 |
i = 1; |
1087 |
i = 1; |
|
|
1088 |
scramble_location = TRUE; |
1089 |
} else { |
1090 |
g_return_val_if_fail (wifi_enabled, NULL); |
1091 |
|
1092 |
i = 2; |
1002 |
compute_movement = TRUE; |
1093 |
compute_movement = TRUE; |
1003 |
} |
1094 |
} |
1004 |
|
1095 |
|
Lines 1017-1054
Link Here
|
1017 |
return wifi[i]; |
1108 |
return wifi[i]; |
1018 |
} |
1109 |
} |
1019 |
|
1110 |
|
1020 |
GClueAccuracyLevel |
1111 |
static gboolean |
1021 |
gclue_wifi_get_accuracy_level (GClueWifi *wifi) |
1112 |
wifi_should_skip_tower (GClueWifi *wifi) |
1022 |
{ |
1113 |
{ |
1023 |
g_return_val_if_fail (GCLUE_IS_WIFI (wifi), |
1114 |
return gclue_3g_should_skip_tower (get_accuracy_level (wifi)); |
1024 |
GCLUE_ACCURACY_LEVEL_NONE); |
|
|
1025 |
|
1026 |
return wifi->priv->accuracy_level; |
1027 |
} |
1115 |
} |
1028 |
|
1116 |
|
1029 |
/* Can return NULL, signifying an empty BSS list. */ |
1117 |
/* Can return NULL, signifying an empty BSS list. */ |
1030 |
static GList * |
1118 |
GList * |
1031 |
get_bss_list (GClueWifi *wifi) |
1119 |
gclue_wifi_get_bss_list (GClueWifi *wifi) |
1032 |
{ |
1120 |
{ |
1033 |
return g_hash_table_get_values (wifi->priv->bss_proxies); |
1121 |
return g_hash_table_get_values (wifi->priv->bss_proxies); |
1034 |
} |
1122 |
} |
1035 |
|
1123 |
|
1036 |
static SoupMessage * |
1124 |
static SoupMessage * |
1037 |
gclue_wifi_create_query (GClueWebSource *source, |
1125 |
gclue_wifi_create_query (GClueWebSource *source, |
|
|
1126 |
const char **query_data_description, |
1038 |
GError **error) |
1127 |
GError **error) |
1039 |
{ |
1128 |
{ |
1040 |
GClueWifi *wifi = GCLUE_WIFI (source); |
1129 |
GClueWifi *wifi = GCLUE_WIFI (source); |
1041 |
GList *bss_list = NULL; /* As in Access Points */ |
1130 |
gboolean skip_tower; |
1042 |
SoupMessage *msg; |
|
|
1043 |
|
1131 |
|
1044 |
if (wifi->priv->interface == NULL) { |
1132 |
if (wifi->priv->interface == NULL) { |
1045 |
goto create_query; |
1133 |
goto create_query; |
1046 |
} |
1134 |
} |
1047 |
|
1135 |
|
1048 |
bss_list = get_bss_list (wifi); |
|
|
1049 |
|
1050 |
/* Empty list? */ |
1136 |
/* Empty list? */ |
1051 |
if (bss_list == NULL) { |
1137 |
if (!g_hash_table_size (wifi->priv->bss_proxies)) { |
1052 |
g_set_error_literal (error, |
1138 |
g_set_error_literal (error, |
1053 |
G_IO_ERROR, |
1139 |
G_IO_ERROR, |
1054 |
G_IO_ERROR_FAILED, |
1140 |
G_IO_ERROR_FAILED, |
Lines 1057-1073
Link Here
|
1057 |
} |
1143 |
} |
1058 |
|
1144 |
|
1059 |
create_query: |
1145 |
create_query: |
1060 |
msg = gclue_mozilla_create_query (bss_list, NULL, error); |
1146 |
skip_tower = wifi_should_skip_tower (wifi); |
1061 |
g_list_free (bss_list); |
1147 |
if (skip_tower) { |
1062 |
return msg; |
1148 |
g_debug ("Will skip 3GPP tower in query due to our accuracy level"); |
1063 |
} |
1149 |
} |
1064 |
|
1150 |
|
1065 |
static GClueLocation * |
1151 |
return gclue_mozilla_create_query (wifi->priv->mozilla, skip_tower, FALSE, |
1066 |
gclue_wifi_parse_response (GClueWebSource *source, |
1152 |
query_data_description, error); |
1067 |
const char *json, |
|
|
1068 |
GError **error) |
1069 |
{ |
1070 |
return gclue_mozilla_parse_response (json, error); |
1071 |
} |
1153 |
} |
1072 |
|
1154 |
|
1073 |
static SoupMessage * |
1155 |
static SoupMessage * |
Lines 1076-1082
Link Here
|
1076 |
GError **error) |
1158 |
GError **error) |
1077 |
{ |
1159 |
{ |
1078 |
GClueWifi *wifi = GCLUE_WIFI (source); |
1160 |
GClueWifi *wifi = GCLUE_WIFI (source); |
1079 |
GList *bss_list; /* As in Access Points */ |
|
|
1080 |
SoupMessage * msg; |
1161 |
SoupMessage * msg; |
1081 |
|
1162 |
|
1082 |
if (wifi->priv->interface == NULL) { |
1163 |
if (wifi->priv->interface == NULL) { |
Lines 1087-1096
Link Here
|
1087 |
return NULL; |
1168 |
return NULL; |
1088 |
} |
1169 |
} |
1089 |
|
1170 |
|
1090 |
bss_list = get_bss_list (wifi); |
|
|
1091 |
|
1092 |
/* Empty list? */ |
1171 |
/* Empty list? */ |
1093 |
if (bss_list == NULL) { |
1172 |
if (!g_hash_table_size (wifi->priv->bss_proxies)) { |
1094 |
g_set_error_literal (error, |
1173 |
g_set_error_literal (error, |
1095 |
G_IO_ERROR, |
1174 |
G_IO_ERROR, |
1096 |
G_IO_ERROR_FAILED, |
1175 |
G_IO_ERROR_FAILED, |
Lines 1098-1108
Link Here
|
1098 |
return NULL; |
1177 |
return NULL; |
1099 |
} |
1178 |
} |
1100 |
|
1179 |
|
1101 |
msg = gclue_mozilla_create_submit_query (location, |
1180 |
msg = gclue_mozilla_create_submit_query (wifi->priv->mozilla, |
1102 |
bss_list, |
1181 |
location, |
1103 |
NULL, |
|
|
1104 |
error); |
1182 |
error); |
1105 |
g_list_free (bss_list); |
|
|
1106 |
return msg; |
1183 |
return msg; |
1107 |
} |
1184 |
} |
1108 |
|
1185 |
|
Lines 1145-1164
Link Here
|
1145 |
return g_bytes_hash (bytes); |
1222 |
return g_bytes_hash (bytes); |
1146 |
} |
1223 |
} |
1147 |
|
1224 |
|
1148 |
static GVariant * |
1225 |
static void location_cache_key_fill_tower (GClueWifi *wifi, GClue3GTower *tower) |
1149 |
get_location_cache_key (GClueWifi *wifi) |
1226 |
{ |
|
|
1227 |
GClueWifiPrivate *priv = wifi->priv; |
1228 |
GClue3GTower *moztower; |
1229 |
|
1230 |
memset (tower, 0, sizeof (*tower)); |
1231 |
tower->tec = GCLUE_TOWER_TEC_NO_FIX; |
1232 |
|
1233 |
moztower = gclue_mozilla_get_tower (priv->mozilla); |
1234 |
if (!moztower || wifi_should_skip_tower (wifi)) { |
1235 |
return; |
1236 |
} |
1237 |
|
1238 |
g_assert (moztower->tec != GCLUE_TOWER_TEC_NO_FIX); |
1239 |
*tower = *moztower; |
1240 |
} |
1241 |
|
1242 |
static void location_cache_key_add_tower (GClueWifi *wifi, GVariantBuilder *builder) |
1243 |
{ |
1244 |
GClue3GTower tower; |
1245 |
|
1246 |
location_cache_key_fill_tower (wifi, &tower); |
1247 |
g_variant_builder_add (builder, "u", (guint32)tower.tec); |
1248 |
g_variant_builder_add (builder, "s", tower.opc); |
1249 |
g_variant_builder_add (builder, "t", (guint64)tower.lac); |
1250 |
g_variant_builder_add (builder, "t", (guint64)tower.cell_id); |
1251 |
} |
1252 |
|
1253 |
static GPtrArray * |
1254 |
get_location_cache_bss_array (GClueWifi *wifi) |
1150 |
{ |
1255 |
{ |
1151 |
GHashTableIter iter; |
1256 |
GHashTableIter iter; |
1152 |
gpointer value; |
1257 |
gpointer value; |
1153 |
g_autoptr(GPtrArray) bss_array = g_ptr_array_new_with_free_func (NULL); /* (element-type WPABSS) */ |
1258 |
g_autoptr(GPtrArray) bss_array = g_ptr_array_new_with_free_func (NULL); /* (element-type WPABSS) */ |
1154 |
guint i; |
|
|
1155 |
GVariantBuilder builder; |
1156 |
|
1259 |
|
1157 |
/* The Mozilla service puts BSSID and signal strength for each BSS into |
1260 |
/* The Mozilla service puts BSSID and signal strength for each BSS into |
1158 |
* its query. The signal strength can typically vary by ±5 for a |
1261 |
* its query. Pack the whole lot into a #GVariant for simplicity, sorted |
1159 |
* stationary laptop, so quantise by that. Pack the whole lot into a |
1262 |
* by MAC address. The sorting has to happen in an array beforehand, |
1160 |
* #GVariant for simplicity, sorted by MAC address. The sorting has to |
1263 |
* as variants are immutable. |
1161 |
* happen in an array beforehand, as variants are immutable. */ |
1264 |
*/ |
1162 |
g_hash_table_iter_init (&iter, wifi->priv->bss_proxies); |
1265 |
g_hash_table_iter_init (&iter, wifi->priv->bss_proxies); |
1163 |
|
1266 |
|
1164 |
while (g_hash_table_iter_next (&iter, NULL, &value)) { |
1267 |
while (g_hash_table_iter_next (&iter, NULL, &value)) { |
Lines 1169-1207
Link Here
|
1169 |
|
1272 |
|
1170 |
g_ptr_array_sort (bss_array, bss_compare); |
1273 |
g_ptr_array_sort (bss_array, bss_compare); |
1171 |
|
1274 |
|
|
|
1275 |
return g_steal_pointer (&bss_array); |
1276 |
} |
1277 |
|
1278 |
static GVariant * |
1279 |
get_location_cache_hashtable_key (GClueWifi *wifi, GPtrArray *bss_array) |
1280 |
{ |
1281 |
guint i; |
1282 |
GVariantBuilder builder; |
1283 |
|
1172 |
/* Serialise to a variant. */ |
1284 |
/* Serialise to a variant. */ |
1173 |
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayn)")); |
1285 |
g_variant_builder_init (&builder, G_VARIANT_TYPE ("(usttaay)")); |
|
|
1286 |
location_cache_key_add_tower (wifi, &builder); |
1287 |
|
1288 |
g_variant_builder_open (&builder, G_VARIANT_TYPE ("aay")); |
1174 |
for (i = 0; i < bss_array->len; i++) { |
1289 |
for (i = 0; i < bss_array->len; i++) { |
1175 |
WPABSS *bss = WPA_BSS (bss_array->pdata[i]); |
1290 |
WPABSS *bss = WPA_BSS (bss_array->pdata[i]); |
1176 |
GVariant *bssid; |
1291 |
GVariant *bssid; |
1177 |
|
1292 |
|
1178 |
g_variant_builder_open (&builder, G_VARIANT_TYPE ("(ayn)")); |
|
|
1179 |
|
1180 |
bssid = wpa_bss_get_bssid (bss); |
1293 |
bssid = wpa_bss_get_bssid (bss); |
1181 |
if (bssid == NULL) |
1294 |
if (bssid == NULL) |
1182 |
continue; |
1295 |
continue; |
1183 |
|
1296 |
|
1184 |
g_variant_builder_add_value (&builder, bssid); |
1297 |
g_variant_builder_add_value (&builder, bssid); |
1185 |
g_variant_builder_add (&builder, "n", wpa_bss_get_signal (bss) / 10); |
|
|
1186 |
|
1187 |
g_variant_builder_close (&builder); |
1188 |
} |
1298 |
} |
|
|
1299 |
g_variant_builder_close (&builder); |
1189 |
|
1300 |
|
1190 |
return g_variant_builder_end (&builder); |
1301 |
return g_variant_builder_end (&builder); |
1191 |
} |
1302 |
} |
1192 |
|
1303 |
|
|
|
1304 |
static GArray * |
1305 |
get_location_cache_signal_array (GClueWifi *wifi, GPtrArray *bss_array) |
1306 |
{ |
1307 |
g_autoptr(GArray) signal_array = NULL; |
1308 |
guint i; |
1309 |
|
1310 |
signal_array = g_array_sized_new (FALSE, FALSE, sizeof (gint16), bss_array->len); |
1311 |
for (i = 0; i < bss_array->len; i++) { |
1312 |
WPABSS *bss = WPA_BSS (bss_array->pdata[i]); |
1313 |
gint16 signal = wpa_bss_get_signal (bss); |
1314 |
|
1315 |
g_array_append_val (signal_array, signal); |
1316 |
} |
1317 |
|
1318 |
return g_steal_pointer (&signal_array); |
1319 |
} |
1320 |
|
1321 |
static gboolean cached_signals_match (GArray *signals1, GArray *signals2) |
1322 |
{ |
1323 |
guint i; |
1324 |
|
1325 |
if (signals1->len != signals2->len) { |
1326 |
g_warning ("Different signal count in one hash table entry: %u vs %u", |
1327 |
signals1->len, signals2->len); |
1328 |
return FALSE; |
1329 |
} |
1330 |
|
1331 |
for (i = 0; i < signals1->len; i++) { |
1332 |
gint s1 = g_array_index (signals1, gint16, i); |
1333 |
gint s2 = g_array_index (signals2, gint16, i); |
1334 |
|
1335 |
if (ABS (s1 - s2) > CACHE_ENTRY_MATCH_SIGNAL_WINDOW / 2) |
1336 |
return FALSE; |
1337 |
} |
1338 |
|
1339 |
return TRUE; |
1340 |
} |
1341 |
|
1193 |
static GClueLocation * |
1342 |
static GClueLocation * |
1194 |
duplicate_location_new_timestamp (GClueLocation *location) |
1343 |
find_cached_location (GHashTable *cache, GVariant *key, GArray *signals) |
1195 |
{ |
1344 |
{ |
1196 |
return g_object_new (GCLUE_TYPE_LOCATION, |
1345 |
g_autofree gchar *key_str = g_variant_print (key, FALSE); |
1197 |
"latitude", gclue_location_get_latitude (location), |
1346 |
GClueLocation *location = NULL; |
1198 |
"longitude", gclue_location_get_longitude (location), |
1347 |
LocationCacheValue *value; |
1199 |
"accuracy", gclue_location_get_accuracy (location), |
1348 |
GList *l; |
1200 |
"altitude", gclue_location_get_altitude (location), |
1349 |
|
1201 |
"timestamp", 0, |
1350 |
value = g_hash_table_lookup (cache, key); |
1202 |
"speed", gclue_location_get_speed (location), |
1351 |
if (!value) { |
1203 |
"heading", gclue_location_get_heading (location), |
1352 |
g_debug ("Cache miss for key %s", key_str); |
1204 |
NULL); |
1353 |
return NULL; |
|
|
1354 |
} |
1355 |
|
1356 |
g_assert (value->elements); |
1357 |
for (l = value->elements; l; l = l->next) { |
1358 |
LocationCacheElement *element = l->data; |
1359 |
|
1360 |
if (location && |
1361 |
gclue_location_get_accuracy (element->location) >= |
1362 |
gclue_location_get_accuracy (location)) { |
1363 |
/* Have at least as accurate location already, |
1364 |
* don't bother with comparing signals. |
1365 |
*/ |
1366 |
continue; |
1367 |
} |
1368 |
|
1369 |
if (!cached_signals_match (element->signals, signals)) |
1370 |
continue; |
1371 |
|
1372 |
location = element->location; |
1373 |
} |
1374 |
|
1375 |
if (location) { |
1376 |
g_debug ("Cache hit for key %s: got location %p (%s)", |
1377 |
key_str, location, |
1378 |
gclue_location_get_description (location)); |
1379 |
} else { |
1380 |
g_debug ("Cache had key %s, but with different signals", key_str); |
1381 |
} |
1382 |
|
1383 |
return location; |
1384 |
} |
1385 |
|
1386 |
typedef struct { |
1387 |
GVariant *cache_key; |
1388 |
GArray *signals; |
1389 |
} RefreshTaskData; |
1390 |
|
1391 |
static RefreshTaskData * |
1392 |
refresh_task_data_new (GVariant *cache_key, |
1393 |
GArray *signals) |
1394 |
{ |
1395 |
RefreshTaskData *tdata; |
1396 |
|
1397 |
tdata = g_slice_new (RefreshTaskData); |
1398 |
tdata->cache_key = g_variant_ref (cache_key); |
1399 |
tdata->signals = signals; |
1400 |
return tdata; |
1401 |
} |
1402 |
|
1403 |
static void refresh_task_data_free (gpointer data) |
1404 |
{ |
1405 |
RefreshTaskData *rdata = data; |
1406 |
|
1407 |
g_clear_pointer (&rdata->cache_key, g_variant_unref); |
1408 |
if (rdata->signals) |
1409 |
g_array_free (rdata->signals, TRUE); |
1410 |
g_slice_free (RefreshTaskData, rdata); |
1205 |
} |
1411 |
} |
1206 |
|
1412 |
|
1207 |
static void |
1413 |
static void |
Lines 1212-1251
Link Here
|
1212 |
{ |
1418 |
{ |
1213 |
GClueWifi *wifi = GCLUE_WIFI (source); |
1419 |
GClueWifi *wifi = GCLUE_WIFI (source); |
1214 |
g_autoptr(GTask) task = g_task_new (source, cancellable, callback, user_data); |
1420 |
g_autoptr(GTask) task = g_task_new (source, cancellable, callback, user_data); |
1215 |
g_autoptr(GVariant) cache_key = get_location_cache_key (wifi); |
1421 |
g_autoptr(GPtrArray) bss_array = get_location_cache_bss_array (wifi); |
1216 |
g_autofree gchar *cache_key_str = g_variant_print (cache_key, FALSE); |
1422 |
g_autoptr(GVariant) cache_key = get_location_cache_hashtable_key (wifi, bss_array); |
1217 |
GClueLocation *cached_location = g_hash_table_lookup (wifi->priv->location_cache, cache_key); |
1423 |
g_autoptr(GArray) signal_array = get_location_cache_signal_array (wifi, bss_array); |
|
|
1424 |
GClueLocation *cached_location = find_cached_location (wifi->priv->location_cache, |
1425 |
cache_key, signal_array); |
1426 |
RefreshTaskData *tdata; |
1218 |
|
1427 |
|
1219 |
g_task_set_source_tag (task, gclue_wifi_refresh_async); |
1428 |
g_task_set_source_tag (task, gclue_wifi_refresh_async); |
1220 |
g_task_set_task_data (task, g_steal_pointer (&cache_key), (GDestroyNotify) g_variant_unref); |
|
|
1221 |
|
1429 |
|
1222 |
if (gclue_location_source_get_active (GCLUE_LOCATION_SOURCE (source))) { |
1430 |
if (gclue_location_source_get_active (GCLUE_LOCATION_SOURCE (source))) { |
1223 |
/* Try the cache. */ |
1431 |
/* Try the cache. */ |
1224 |
if (cached_location != NULL) { |
1432 |
if (cached_location != NULL) { |
1225 |
g_autoptr(GClueLocation) new_location = NULL; |
1433 |
g_autoptr(GClueLocation) new_location = NULL; |
1226 |
|
1434 |
|
1227 |
g_debug ("Cache hit for key %s: got location %p (%s)", |
|
|
1228 |
cache_key_str, cached_location, |
1229 |
gclue_location_get_description (cached_location)); |
1230 |
wifi->priv->cache_hits++; |
1435 |
wifi->priv->cache_hits++; |
1231 |
|
1436 |
|
1232 |
/* Duplicate the location so its timestamp is updated. */ |
1437 |
/* Duplicate the location so its timestamp is updated. */ |
1233 |
new_location = duplicate_location_new_timestamp (cached_location); |
1438 |
new_location = gclue_location_duplicate_fresh (cached_location); |
1234 |
gclue_location_source_set_location (GCLUE_LOCATION_SOURCE (source), new_location); |
1439 |
gclue_location_source_set_location (GCLUE_LOCATION_SOURCE (source), new_location); |
1235 |
|
1440 |
|
1236 |
g_task_return_pointer (task, g_steal_pointer (&new_location), g_object_unref); |
1441 |
g_task_return_pointer (task, g_steal_pointer (&new_location), g_object_unref); |
1237 |
return; |
1442 |
return; |
1238 |
} |
1443 |
} |
1239 |
|
1444 |
|
1240 |
g_debug ("Cache miss for key %s; querying web service", cache_key_str); |
|
|
1241 |
wifi->priv->cache_misses++; |
1445 |
wifi->priv->cache_misses++; |
1242 |
} |
1446 |
} |
1243 |
|
1447 |
|
|
|
1448 |
tdata = refresh_task_data_new (cache_key, g_steal_pointer (&signal_array)); |
1449 |
g_task_set_task_data (task, tdata, refresh_task_data_free); |
1450 |
|
1244 |
/* Fall back to querying the web service. */ |
1451 |
/* Fall back to querying the web service. */ |
1245 |
GCLUE_WEB_SOURCE_CLASS (gclue_wifi_parent_class)->refresh_async (source, cancellable, refresh_cb, g_steal_pointer (&task)); |
1452 |
GCLUE_WEB_SOURCE_CLASS (gclue_wifi_parent_class)->refresh_async (source, cancellable, refresh_cb, g_steal_pointer (&task)); |
1246 |
} |
1453 |
} |
1247 |
|
1454 |
|
1248 |
static void |
1455 |
static void |
|
|
1456 |
add_cached_location (GHashTable *cache, |
1457 |
GVariant *key, GArray **signals, |
1458 |
GClueLocation *location) |
1459 |
{ |
1460 |
LocationCacheValue *value; |
1461 |
LocationCacheElement *element; |
1462 |
|
1463 |
value = g_hash_table_lookup (cache, key); |
1464 |
if (!value) { |
1465 |
value = location_cache_value_new (); |
1466 |
g_hash_table_insert (cache, g_variant_ref (key), value); |
1467 |
} |
1468 |
|
1469 |
element = location_cache_element_new (g_steal_pointer (signals), location); |
1470 |
value->elements = g_list_prepend (value->elements, element); |
1471 |
} |
1472 |
|
1473 |
static void |
1249 |
refresh_cb (GObject *source_object, |
1474 |
refresh_cb (GObject *source_object, |
1250 |
GAsyncResult *result, |
1475 |
GAsyncResult *result, |
1251 |
gpointer user_data) |
1476 |
gpointer user_data) |
Lines 1255-1261
Link Here
|
1255 |
g_autoptr(GTask) task = g_steal_pointer (&user_data); |
1480 |
g_autoptr(GTask) task = g_steal_pointer (&user_data); |
1256 |
g_autoptr(GClueLocation) location = NULL; |
1481 |
g_autoptr(GClueLocation) location = NULL; |
1257 |
g_autoptr(GError) local_error = NULL; |
1482 |
g_autoptr(GError) local_error = NULL; |
1258 |
GVariant *cache_key; |
1483 |
RefreshTaskData *tdata; |
1259 |
g_autofree gchar *cache_key_str = NULL; |
1484 |
g_autofree gchar *cache_key_str = NULL; |
1260 |
double cache_hit_ratio; |
1485 |
double cache_hit_ratio; |
1261 |
|
1486 |
|
Lines 1268-1276
Link Here
|
1268 |
} |
1493 |
} |
1269 |
|
1494 |
|
1270 |
/* Cache the result. */ |
1495 |
/* Cache the result. */ |
1271 |
cache_key = g_task_get_task_data (task); |
1496 |
tdata = g_task_get_task_data (task); |
1272 |
cache_key_str = g_variant_print (cache_key, FALSE); |
1497 |
cache_key_str = g_variant_print (tdata->cache_key, FALSE); |
1273 |
g_hash_table_replace (wifi->priv->location_cache, g_variant_ref (cache_key), g_object_ref (location)); |
1498 |
add_cached_location (wifi->priv->location_cache, |
|
|
1499 |
tdata->cache_key, &tdata->signals, |
1500 |
location); |
1274 |
|
1501 |
|
1275 |
if (wifi->priv->cache_hits || wifi->priv->cache_misses) { |
1502 |
if (wifi->priv->cache_hits || wifi->priv->cache_misses) { |
1276 |
double cache_attempts; |
1503 |
double cache_attempts; |