From 1efab4238caaec4589424da82faf699da5ecc5e6 Mon Sep 17 00:00:00 2001 From: Ankit Date: Thu, 18 Jun 2015 20:02:00 +0530 Subject: [PATCH] Add GClueNMEASource GClueNMEASource receives NMEA GGA sentences from a NMEA source on the network. This will enable us to receive location info from other devices on the local network with location capabilities, such as smartphones. https://bugs.freedesktop.org/show_bug.cgi?id=90974 --- data/geoclue.conf.in | 3 + src/Makefile.am | 2 + src/gclue-config.c | 19 +++ src/gclue-config.h | 1 + src/gclue-locator.c | 9 ++ src/gclue-nmea-source.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++ src/gclue-nmea-source.h | 71 ++++++++++ 7 files changed, 444 insertions(+) create mode 100644 src/gclue-nmea-source.c create mode 100644 src/gclue-nmea-source.h diff --git a/data/geoclue.conf.in b/data/geoclue.conf.in index 4b8f81b..d6c4bcc 100644 --- a/data/geoclue.conf.in +++ b/data/geoclue.conf.in @@ -26,6 +26,9 @@ url=https://location.services.mozilla.com/v1/geolocate?key=geoclue # submit-data=false +# Do we want to use Network NMEA source +use-nmea-source=true + # URL to submission API of Mozilla Location Service submission-url=https://location.services.mozilla.com/v1/submit?key=geoclue diff --git a/src/Makefile.am b/src/Makefile.am index b81c4f5..f0da53d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -94,6 +94,8 @@ libgeoclue_la_SOURCES = \ gclue-mozilla.c \ gclue-location.h \ gclue-location.c \ + gclue-nmea-source.h \ + gclue-nmea-source.c \ $(NULL) if BUILD_MODEM_SOURCE diff --git a/src/gclue-config.c b/src/gclue-config.c index 2ef5f83..a84bb26 100644 --- a/src/gclue-config.c +++ b/src/gclue-config.c @@ -56,6 +56,7 @@ struct _GClueConfigPrivate char *wifi_url; gboolean wifi_submit; + gboolean use_nmea_source; char *wifi_submit_url; char *wifi_submit_nick; @@ -211,6 +212,18 @@ load_wifi_config (GClueConfig *config) return; } + priv->use_nmea_source = g_key_file_get_boolean (priv->key_file, + "wifi", + "use-nmea-source", + &error); + if (error != NULL) { + g_debug ("Failed to get config wifi/use-nmea-source: %s", + error->message); + g_error_free (error); + + return; + } + priv->wifi_submit_url = g_key_file_get_string (priv->key_file, "wifi", "submission-url", @@ -383,6 +396,12 @@ gclue_config_get_wifi_submit_data (GClueConfig *config) return config->priv->wifi_submit; } +gboolean +gclue_config_get_use_nmea_source (GClueConfig *config) +{ + return config->priv->use_nmea_source; +} + void gclue_config_set_wifi_submit_data (GClueConfig *config, gboolean submit) diff --git a/src/gclue-config.h b/src/gclue-config.h index 93e12c5..f0ce5de 100644 --- a/src/gclue-config.h +++ b/src/gclue-config.h @@ -78,6 +78,7 @@ const char * gclue_config_get_wifi_submit_nick (GClueConfig *config); void gclue_config_set_wifi_submit_nick (GClueConfig *config, const char *nick); gboolean gclue_config_get_wifi_submit_data (GClueConfig *config); +gboolean gclue_config_get_use_nmea_source (GClueConfig *config); void gclue_config_set_wifi_submit_data (GClueConfig *config, gboolean submit); diff --git a/src/gclue-locator.c b/src/gclue-locator.c index a062eff..1e2edcd 100644 --- a/src/gclue-locator.c +++ b/src/gclue-locator.c @@ -28,6 +28,7 @@ #include "public-api/gclue-enum-types.h" #include "gclue-wifi.h" +#include "gclue-config.h" #if GCLUE_USE_3G_SOURCE #include "gclue-3g.h" @@ -41,6 +42,8 @@ #include "gclue-modem-gps.h" #endif +#include "gclue-nmea-source.h" + /* This class is like a master location source that hides all individual * location sources from rest of the code */ @@ -279,6 +282,7 @@ gclue_locator_constructed (GObject *object) GClueLocator *locator = GCLUE_LOCATOR (object); GClueLocationSource *submit_source = NULL; GList *node; + GClueConfig * gconfig = gclue_config_get_singleton (); G_OBJECT_CLASS (gclue_locator_parent_class)->constructed (object); @@ -298,6 +302,11 @@ gclue_locator_constructed (GObject *object) locator->priv->sources = g_list_append (locator->priv->sources, gps); submit_source = GCLUE_LOCATION_SOURCE (gps); #endif + if (gclue_config_get_use_nmea_source (gconfig)) { + GClueNMEASource *nmea = gclue_nmea_source_get_singleton (); + locator->priv->sources = g_list_append (locator->priv->sources, + nmea); + } for (node = locator->priv->sources; node != NULL; node = node->next) { g_signal_connect (G_OBJECT (node->data), diff --git a/src/gclue-nmea-source.c b/src/gclue-nmea-source.c new file mode 100644 index 0000000..a5ecebd --- /dev/null +++ b/src/gclue-nmea-source.c @@ -0,0 +1,339 @@ +/* vim: set et ts=8 sw=8: */ +/* + * Copyright (C) 2014 Red Hat, Inc. + * Copyright (C) 2015 Ankit (Verma) + * + * Geoclue is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Geoclue is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with Geoclue; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Zeeshan Ali (Khattak) + * Ankit (Verma) + */ + +#include +#include +#include "gclue-nmea-source.h" +#include "gclue-location.h" +#include "config.h" + +#define GCLUE_NMEA_SERVER_PORT 60793 + + +struct _GClueNMEASourcePrivate { + GSocketConnection * connection; + + GSocketClient * client; + + GCancellable * cancellable; + + gchar * buffer; +}; + +G_DEFINE_TYPE (GClueNMEASource, gclue_nmea_source, GCLUE_TYPE_LOCATION_SOURCE) + +static gboolean +gclue_nmea_source_start (GClueLocationSource *source); +static gboolean +gclue_nmea_source_stop (GClueLocationSource *source); + +static void +refresh_accuracy_level (GClueNMEASource *source) +{ + + /* FIXME: Accuracy level is currently hardcoded. Accuracy level will be + * provided out of band through Avahi to avoid having to connect to + * server for only fetching this peace of information. */ + + g_debug ("Available accuracy level from %s: %u", + G_OBJECT_TYPE_NAME (source), GCLUE_ACCURACY_LEVEL_EXACT); + g_object_set (G_OBJECT (source), + "available-accuracy-level", GCLUE_ACCURACY_LEVEL_EXACT, + NULL); +} + +static void +on_read_gga_sentence (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GInputStream *istream = G_INPUT_STREAM (object); + GError *error = NULL; + GClueNMEASource * source = GCLUE_NMEA_SOURCE (user_data); + GClueLocation *location; + GDataInputStream * distream; + guint32 data_size; + int count; + + count = g_input_stream_read_finish (istream, + result, + &error); + + if (count == -1) { + g_warning ("Error when receiving message"); + if (error != NULL) { + g_warning ("%s", error->message); + g_clear_error (&error); + } + return; + } + g_message ("Network source sent: \"%s\"", source->priv->buffer); + + location = gclue_location_new_from_gga (source->priv->buffer); + + gclue_location_source_set_location (GCLUE_LOCATION_SOURCE (source), + location); + + g_free (source->priv->buffer); + + distream = g_data_input_stream_new (istream); + g_data_input_stream_set_byte_order + (distream, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); + data_size = g_data_input_stream_read_uint32 (distream, + source->priv->cancellable, + &error); + if (error != NULL) { + g_warning ("%s", error->message); + return; + } + + source->priv->buffer = g_malloc_n (data_size, sizeof(gchar)); + + g_input_stream_read_async (istream, + source->priv->buffer, + data_size, + G_PRIORITY_DEFAULT, + source->priv->cancellable, + on_read_gga_sentence, + source); +} + +static void +on_connection_to_location_server (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GClueNMEASource * source = GCLUE_NMEA_SOURCE (user_data); + GSocketClient * client = G_SOCKET_CLIENT (object); + GError * error = NULL; + GDataInputStream * distream; + GInputStream * istream; + guint32 data_size; + + source->priv->connection = g_socket_client_connect_to_host_finish + (client, + result, + &error); + + if (error != NULL) { + g_warning ("%s", error->message); + return; + } + + istream = g_io_stream_get_input_stream + (G_IO_STREAM (source->priv->connection)); + distream = g_data_input_stream_new (istream); + + g_data_input_stream_set_byte_order + (distream, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); + + data_size = g_data_input_stream_read_uint32 (distream, + source->priv->cancellable, + &error); + + if (error != NULL) { + g_warning ("%s", error->message); + return; + } + + source->priv->buffer = g_malloc_n (data_size, sizeof(gchar)); + + g_input_stream_read_async (istream, + source->priv->buffer, + data_size, + G_PRIORITY_DEFAULT, + source->priv->cancellable, + on_read_gga_sentence, + source); +} + +static void +on_connection_close (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GIOStream * iostream = G_IO_STREAM (object); + GClueNMEASourcePrivate * priv = GCLUE_NMEA_SOURCE (user_data)->priv; + GError * error = NULL; + + g_io_stream_close_finish (iostream, + result, + &error); + if (error != NULL) { + g_warning ("%s", error->message); + return; + } + g_clear_object (&priv->connection); +} + +static void +on_input_stream_close (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GInputStream * istream = G_INPUT_STREAM (object); + GClueNMEASource * source = GCLUE_NMEA_SOURCE (user_data); + GError * error = NULL; + + g_input_stream_close_finish (istream, + result, + &error); + if (error != NULL) { + g_warning ("%s", error->message); + return; + } + g_io_stream_close_async (G_IO_STREAM (source->priv->connection), + G_PRIORITY_DEFAULT, + NULL, + on_connection_close, + source); +} + +static void +gclue_nmea_source_finalize (GObject *gnmea) +{ + GClueNMEASourcePrivate *priv = GCLUE_NMEA_SOURCE (gnmea)->priv; + + G_OBJECT_CLASS (gclue_nmea_source_parent_class)->finalize (gnmea); + + g_clear_object (&priv->connection); + g_clear_object (&priv->client); + g_clear_object (&priv->cancellable); + g_free (priv->buffer); + +} + +static void +gclue_nmea_source_class_init (GClueNMEASourceClass *klass) +{ + GClueLocationSourceClass *source_class = GCLUE_LOCATION_SOURCE_CLASS (klass); + GObjectClass *gnmea_class = G_OBJECT_CLASS (klass); + + gnmea_class->finalize = gclue_nmea_source_finalize; + + source_class->start = gclue_nmea_source_start; + source_class->stop = gclue_nmea_source_stop; + + g_type_class_add_private (klass, sizeof (GClueNMEASourcePrivate)); +} + +static void +gclue_nmea_source_init (GClueNMEASource *source) +{ + GClueNMEASourcePrivate *priv; + + source->priv = G_TYPE_INSTANCE_GET_PRIVATE ((source), + GCLUE_TYPE_NMEA_SOURCE, + GClueNMEASourcePrivate); + priv = source->priv; + + priv->cancellable = g_cancellable_new (); + + refresh_accuracy_level (source); +} + +static void +on_nmea_source_destroyed (gpointer data, + GObject *where_the_object_was) +{ + GClueNMEASource **source = (GClueNMEASource **) data; + + *source = NULL; +} + +/** + * gclue_nmea_source_get_singleton: + * + * Get the #GClueNMEASource singleton. + * + * Returns: (transfer full): a new ref to #GClueNMEASource. Use g_object_unref() + * when done. + **/ +GClueNMEASource * +gclue_nmea_source_get_singleton (void) +{ + static GClueNMEASource *source = NULL; + + if (source == NULL) { + source = g_object_new (GCLUE_TYPE_NMEA_SOURCE, NULL); + g_object_weak_ref (G_OBJECT (source), + on_nmea_source_destroyed, + &source); + } else + g_object_ref (source); + + return source; +} + +static gboolean +gclue_nmea_source_start (GClueLocationSource *source) +{ + GClueLocationSourceClass *base_class; + GClueNMEASourcePrivate *priv; + + g_return_val_if_fail (GCLUE_IS_LOCATION_SOURCE (source), FALSE); + priv = GCLUE_NMEA_SOURCE (source)->priv; + + base_class = GCLUE_LOCATION_SOURCE_CLASS (gclue_nmea_source_parent_class); + if (!base_class->start (source)) + return FALSE; + + priv->client = g_socket_client_new (); + + /* FIXME: Don't hardcode hostname; Use Avahi for auto-discovery. */ + g_socket_client_connect_to_host_async + (priv->client, + "localhost", + GCLUE_NMEA_SERVER_PORT, + NULL, + on_connection_to_location_server, + source); + + return TRUE; +} + +static gboolean +gclue_nmea_source_stop (GClueLocationSource *source) +{ + GClueNMEASourcePrivate *priv = GCLUE_NMEA_SOURCE (source)->priv; + GClueLocationSourceClass *base_class; + GInputStream * istream; + + g_return_val_if_fail (GCLUE_IS_LOCATION_SOURCE (source), FALSE); + + base_class = GCLUE_LOCATION_SOURCE_CLASS (gclue_nmea_source_parent_class); + if (!base_class->stop (source)) + return FALSE; + + istream = g_io_stream_get_input_stream + (G_IO_STREAM (priv->connection)); + + g_cancellable_cancel (priv->cancellable); + + g_input_stream_close_async (istream, + G_PRIORITY_DEFAULT, + NULL, + on_input_stream_close, + source); + return TRUE; +} \ No newline at end of file diff --git a/src/gclue-nmea-source.h b/src/gclue-nmea-source.h new file mode 100644 index 0000000..6bcedc6 --- /dev/null +++ b/src/gclue-nmea-source.h @@ -0,0 +1,71 @@ +/* vim: set et ts=8 sw=8: */ +/* + * Copyright (C) 2014 Red Hat, Inc. + * Copyright (C) 2015 Ankit (Verma) + * + * Geoclue is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Geoclue is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with Geoclue; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Zeeshan Ali (Khattak) + * Ankit (Verma) + */ + +#ifndef GCLUE_NMEA_SOURCE_H +#define GCLUE_NMEA_SOURCE_H + +#include +#include +#include "gclue-location-source.h" + +G_BEGIN_DECLS + +GType gclue_nmea_source_get_type (void) G_GNUC_CONST; + +#define GCLUE_TYPE_NMEA_SOURCE (gclue_nmea_source_get_type ()) +#define GCLUE_NMEA_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCLUE_TYPE_NMEA_SOURCE, GClueNMEASource)) +#define GCLUE_IS_NMEA_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCLUE_TYPE_NMEA_SOURCE)) +#define GCLUE_NMEA_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCLUE_TYPE_NMEA_SOURCE, GClueNMEASourceClass)) +#define GCLUE_IS_NMEA_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCLUE_TYPE_NMEA_SOURCE)) +#define GCLUE_NMEA_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCLUE_TYPE_NMEA_SOURCE, GClueNMEASourceClass)) + +/** + * GClueNMEASource: + * + * All the fields in the #GClueNMEASource structure are private and should never be accessed directly. +**/ +typedef struct _GClueNMEASource GClueNMEASource; +typedef struct _GClueNMEASourceClass GClueNMEASourceClass; +typedef struct _GClueNMEASourcePrivate GClueNMEASourcePrivate; + +struct _GClueNMEASource { + /* */ + GClueLocationSource parent_instance; + GClueNMEASourcePrivate *priv; +}; + +/** + * GClueNMEASourceClass: + * + * All the fields in the #GClueNMEASourceClass structure are private and should never be accessed directly. +**/ +struct _GClueNMEASourceClass { + /* */ + GClueLocationSourceClass parent_class; +}; + +GClueNMEASource * gclue_nmea_source_get_singleton (void); + +G_END_DECLS + +#endif /* GCLUE_NMEA_SOURCE_H */ -- 2.1.0