diff options
author | Dan Williams <dcbw@redhat.com> | 2010-07-19 16:43:53 -0700 |
---|---|---|
committer | Bastien Nocera <hadess@hadess.net> | 2010-07-20 17:59:08 +0100 |
commit | eb1f0383794ef7301fa9c8b91b465181161cfda4 (patch) | |
tree | 48f891213a835d73e65e774fe032e4f6d2ed1336 | |
parent | 11e262dc093844b1c079e7c2a6b53c8cd700b31c (diff) |
gsmloc: add ModemManager GSM LAC/CID backend
Backend requires ModemManager git master as of 2010-07-19. geoclue
will enable location services on any modem that ModemManager knows
about.
https://bugs.freedesktop.org/show_bug.cgi?id=29154
-rw-r--r-- | providers/gsmloc/Makefile.am | 14 | ||||
-rw-r--r-- | providers/gsmloc/geoclue-gsmloc-mm.c | 779 | ||||
-rw-r--r-- | providers/gsmloc/geoclue-gsmloc-mm.h | 56 | ||||
-rw-r--r-- | providers/gsmloc/geoclue-gsmloc.c | 26 | ||||
-rw-r--r-- | providers/gsmloc/mm-marshal.list | 2 |
5 files changed, 872 insertions, 5 deletions
diff --git a/providers/gsmloc/Makefile.am b/providers/gsmloc/Makefile.am index 4f3bfd6..1820f8c 100644 --- a/providers/gsmloc/Makefile.am +++ b/providers/gsmloc/Makefile.am @@ -7,7 +7,9 @@ nodist_geoclue_gsmloc_SOURCES = \ ofono-manager-bindings.h \ ofono-modem-bindings.h \ ofono-network-registration-bindings.h \ - ofono-network-operator-bindings.h + ofono-network-operator-bindings.h \ + mm-marshal.c \ + mm-marshal.h BUILT_SOURCES = \ $(nodist_geoclue_gsmloc_SOURCES) @@ -16,7 +18,9 @@ geoclue_gsmloc_SOURCES = \ mcc.h \ geoclue-gsmloc.c \ geoclue-gsmloc-ofono.c \ - geoclue-gsmloc-ofono.h + geoclue-gsmloc-ofono.h \ + geoclue-gsmloc-mm.c \ + geoclue-gsmloc-mm.h geoclue_gsmloc_CFLAGS = \ @@ -44,6 +48,7 @@ EXTRA_DIST = \ ofono-modem.xml \ ofono-network-operator.xml \ ofono-network-registration.xml \ + mm-marshal.list \ $(service_in_files) \ $(providers_DATA) @@ -65,3 +70,8 @@ ofono-marshal.h: ofono-marshal.list $(GLIB_GENMARSHAL) $(GLIB_GENMARSHAL) $< --header --prefix=ofono_marshal > $@ ofono-marshal.c: ofono-marshal.list ofono-marshal.h $(GLIB_GENMARSHAL) $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=ofono_marshal $(srcdir)/ofono-marshal.list --header --body >> $@ + +mm-marshal.h: mm-marshal.list $(GLIB_GENMARSHAL) + $(AM_V_GEN) $(GLIB_GENMARSHAL) $< --header --prefix=mm_marshal > $@ +mm-marshal.c: mm-marshal.list mm-marshal.h $(GLIB_GENMARSHAL) + $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=mm_marshal $(srcdir)/mm-marshal.list --header --body >> $@ diff --git a/providers/gsmloc/geoclue-gsmloc-mm.c b/providers/gsmloc/geoclue-gsmloc-mm.c new file mode 100644 index 0000000..bd882d1 --- /dev/null +++ b/providers/gsmloc/geoclue-gsmloc-mm.c @@ -0,0 +1,779 @@ +/* + * Geoclue + * geoclue-gsmloc-mm.c - An Address/Position provider for ModemManager + * + * Author: Dan Williams <dcbw@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <config.h> + +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +#include <glib-object.h> +#include <dbus/dbus.h> +#include <dbus/dbus-glib-bindings.h> + +#include "geoclue-gsmloc-mm.h" + +#include "mm-marshal.h" + +#define MM_DBUS_SERVICE "org.freedesktop.ModemManager" +#define MM_DBUS_PATH "/org/freedesktop/ModemManager" +#define MM_DBUS_INTERFACE "org.freedesktop.ModemManager" +#define MM_DBUS_LOC_INTERFACE "org.freedesktop.ModemManager.Modem.Location" +#define DBUS_PROPS_INTERFACE "org.freedesktop.DBus.Properties" +#define MM_DBUS_MODEM_INTERFACE "org.freedesktop.ModemManager.Modem" + +G_DEFINE_TYPE (GeoclueGsmlocMm, geoclue_gsmloc_mm, G_TYPE_OBJECT) + +#define GEOCLUE_GSMLOC_MM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GEOCLUE_TYPE_GSMLOC_MM, GeoclueGsmlocMmPrivate)) + +typedef struct { + char *path; + + DBusGProxy *loc_proxy; + DBusGProxy *props_proxy; + DBusGProxy *modem_proxy; + + gboolean got_enabled; + gboolean enabled; + gboolean enabling; + + gboolean got_loc_enabled; + gboolean loc_enabled; + gboolean loc_enabling; + gboolean got_initial_loc; + + /* Whether the modem signals its location or whether we + * have to poll for it. + */ + gboolean signals; + guint loc_idle; + + gboolean has_location; + + gpointer owner; +} Modem; + +typedef struct { + DBusGConnection *bus; + DBusGProxy *dbus_proxy; + + /* Listens for device add/remove events */ + DBusGProxy *mm_proxy; + DBusGProxy *props_proxy; + + /* List of Modem objects */ + GSList *modems; +} GeoclueGsmlocMmPrivate; + +enum { + PROP_0, + PROP_AVAILABLE, +}; + +enum { + NETWORK_DATA_CHANGED, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = {0}; + +#define LOC_CAP_GSM_LACCI 0x02 + +gboolean mm_debug = FALSE; + +#define debugmsg(fmt, args...) \ + { if (mm_debug) { g_debug (fmt, ##args); } } + + +static gboolean +is_available (GeoclueGsmlocMm *self) +{ + GeoclueGsmlocMmPrivate *priv = GEOCLUE_GSMLOC_MM_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->modems; iter; iter = g_slist_next (iter)) { + Modem *modem = iter->data; + + if (modem->enabled && modem->loc_enabled && modem->has_location) + return TRUE; + } + + return FALSE; +} + +static Modem * +find_modem (GeoclueGsmlocMm *self, const char *path) +{ + GeoclueGsmlocMmPrivate *priv = GEOCLUE_GSMLOC_MM_GET_PRIVATE (self); + GSList *iter; + + g_return_val_if_fail (path != NULL, NULL); + + for (iter = priv->modems; iter; iter = g_slist_next (iter)) { + Modem *modem = iter->data; + + if (strcmp (path, modem->path) == 0) + return modem; + } + + return NULL; +} + +static void +recheck_available (GeoclueGsmlocMm *self) +{ + g_object_notify (G_OBJECT (self), "available"); +} + +static void +location_update (GeoclueGsmlocMm *self, const char *loc) +{ + char **components = NULL; + char *dec_lac = NULL, *dec_cid = NULL; + unsigned long int num; + + components = g_strsplit (loc, ",", 0); + if (!components || g_strv_length (components) < 4) { + g_warning ("%s: invalid GSM LAC/CI location: '%s'", __func__, loc); + goto out; + } + + /* convert lac to decimal */ + errno = 0; + num = strtoul (components[2], NULL, 16); + if (errno != 0) { + g_warning ("%s: cannot convert LAC '%s' to decimal!", + __func__, components[2]); + goto out; + } + dec_lac = g_strdup_printf ("%u", num); + + /* convert cell id to decimal */ + errno = 0; + num = strtoul (components[3], NULL, 16); + if (errno != 0) { + g_warning ("%s: cannot convert Cell ID '%s' to decimal!", + __func__, components[3]); + goto out; + } + dec_cid = g_strdup_printf ("%u", num); + + debugmsg ("%s: emitting location: %s/%s/%s/%s", + __func__, components[0], components[1], dec_lac, dec_cid); + g_signal_emit (G_OBJECT (self), signals[NETWORK_DATA_CHANGED], 0, + components[0], /* MCC */ + components[1], /* MNC */ + dec_lac, /* LAC */ + dec_cid); /* CID */ + +out: + if (components) + g_strfreev (components); + g_free (dec_lac); + g_free (dec_cid); +} + +static void +modem_location_update (Modem *modem, GHashTable *locations) +{ + GValue *lacci; + + /* GSMLOC only handles GSM LAC/CI location info */ + lacci = g_hash_table_lookup (locations, GUINT_TO_POINTER (LOC_CAP_GSM_LACCI)); + if (!lacci) + return; + if (!G_VALUE_HOLDS_STRING (lacci)) { + g_warning ("%s: GSM LAC/CI location member not a string!", __func__); + return; + } + + debugmsg ("%s: GSM LAC/CI: %s", __func__, g_value_get_string (lacci)); + location_update (modem->owner, g_value_get_string (lacci)); +} + +#define DBUS_TYPE_LOCATIONS (dbus_g_type_get_map ("GHashTable", G_TYPE_UINT, G_TYPE_VALUE)) +#define DBUS_TYPE_G_MAP_OF_VARIANT (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) + +static void +loc_poll_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + GError *error = NULL; + GHashTable *locations = NULL; + + if (!dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_LOCATIONS, &locations, + G_TYPE_INVALID)) { + g_warning ("%s: failed to get location: (%d) %s", + __func__, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } + + modem_location_update ((Modem *) user_data, locations); + g_hash_table_destroy (locations); +} + +static gboolean +modem_loc_poll (gpointer user_data) +{ + Modem *modem = user_data; + + dbus_g_proxy_begin_call (modem->loc_proxy, "GetLocation", + loc_poll_cb, modem, NULL, + G_TYPE_INVALID); + + return TRUE; +} + +static void +modem_loc_enable_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + Modem *modem = user_data; + GError *error = NULL; + + modem->loc_enabling = FALSE; + if (!dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) { + g_warning ("%s: failed to enable modem location services: (%d) %s", + __func__, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } +} + +static void +modem_try_loc_enable (Modem *modem) +{ + /* Don't enable location services if we don't have all the modem's + * status yet or if location services are already enabled. + */ + + if (!modem->got_loc_enabled || + !modem->enabled || + !modem->has_location || + !modem->got_loc_enabled || + modem->loc_enabled || + modem->loc_enabling) + return; + + modem->loc_enabling = TRUE; + debugmsg ("%s: (%s) enabling location services...", __func__, modem->path); + dbus_g_proxy_begin_call (modem->loc_proxy, "Enable", + modem_loc_enable_cb, modem, NULL, + G_TYPE_BOOLEAN, TRUE, /* enable */ + G_TYPE_BOOLEAN, TRUE, /* signal location changes */ + G_TYPE_INVALID); +} + +static void +modem_enable_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + Modem *modem = user_data; + GError *error = NULL; + + modem->enabling = FALSE; + if (!dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) { + g_warning ("%s: failed to enable modem: (%d) %s", + __func__, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } + + /* enable location services */ + modem_try_loc_enable (modem); +} + +static void +modem_properties_changed (DBusGProxy *proxy, + const char *interface, + GHashTable *props, + gpointer user_data) +{ + Modem *modem = user_data; + GValue *value; + gboolean old_avail = modem->enabled && modem->loc_enabled && modem->has_location; + gboolean new_avail; + + if (strcmp (interface, MM_DBUS_MODEM_INTERFACE) == 0) { + value = g_hash_table_lookup (props, "Enabled"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) { + modem->enabled = g_value_get_boolean (value); + modem->got_enabled = TRUE; + debugmsg ("%s: (%s) modem %s", __func__, modem->path, + modem->enabled ? "enabled" : "disabled"); + } + } else if (strcmp (interface, MM_DBUS_LOC_INTERFACE) == 0) { + value = g_hash_table_lookup (props, "Enabled"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) { + modem->loc_enabled = g_value_get_boolean (value); + modem->got_loc_enabled = TRUE; + debugmsg ("%s: (%s) modem location services %s", + __func__, modem->path, + modem->loc_enabled ? "enabled" : "disabled"); + } + + value = g_hash_table_lookup (props, "SignalsLocation"); + if (value && G_VALUE_HOLDS_BOOLEAN (value)) { + modem->signals = g_value_get_boolean (value); + debugmsg ("%s: (%s) modem %s signal location updates", + __func__, modem->path, + modem->signals ? "will" : "does not"); + } + + value = g_hash_table_lookup (props, "Capabilities"); + if (value && G_VALUE_HOLDS_UINT (value)) { + debugmsg ("%s: (%s) modem location capabilities: 0x%X", + __func__, modem->path, + g_value_get_uint (value)); + + if (g_value_get_uint (value) & LOC_CAP_GSM_LACCI) + modem->has_location = TRUE; + } + + value = g_hash_table_lookup (props, "Location"); + if (value && G_VALUE_HOLDS_BOXED (value)) + modem_location_update (modem, (GHashTable *) g_value_get_boxed (value)); + } + + new_avail = modem->enabled && modem->loc_enabled && modem->has_location; + + /* If the modem doesn't signal its location, start polling for the + * location now. + */ + if (new_avail && !modem->signals && !modem->loc_idle) { + modem->loc_idle = g_timeout_add_seconds (20, modem_loc_poll, modem); + /* Kick off a quick location request */ + modem_loc_poll (modem); + } + + /* If the modem is no longer enabled, or it now signals its location + * then we no longer need to poll. + */ + if ((!new_avail || modem->signals) && modem->loc_idle) + g_source_remove (modem->loc_idle); + + /* Tell the manager to recheck availability of location info */ + if (old_avail != new_avail) + recheck_available (modem->owner); + + /* If we've successfully retrieved modem properties and the modem + * isn't enabled, do that now. + */ + if (modem->got_enabled && !modem->enabled && !modem->enabling) { + debugmsg ("%s: (%s) enabling...", __func__, modem->path); + modem->enabling = TRUE; + dbus_g_proxy_begin_call (modem->modem_proxy, "Enable", + modem_enable_cb, modem, NULL, + G_TYPE_BOOLEAN, TRUE, G_TYPE_INVALID); + } + + /* If the modem was already enabled but location services weren't, + * enable them now. + */ + modem_try_loc_enable (modem); + + /* After location is enabled, try to get the location ASAP */ + if (modem->has_location && modem->loc_enabled && !modem->got_initial_loc) { + modem->got_initial_loc = TRUE; + modem_loc_poll (modem); + } +} + +static void +modem_props_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + GError *error = NULL; + GHashTable *props = NULL; + Modem *modem = user_data; + + if (!dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_VARIANT, &props, + G_TYPE_INVALID)) { + g_warning ("%s: failed to get modem interface properties: (%d) %s", + __func__, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } + + modem_properties_changed (modem->loc_proxy, MM_DBUS_MODEM_INTERFACE, props, modem); + g_hash_table_destroy (props); +} + +static void +loc_props_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + GError *error = NULL; + GHashTable *props = NULL; + Modem *modem = user_data; + + if (!dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_VARIANT, &props, + G_TYPE_INVALID)) { + g_warning ("%s: failed to get location interface properties: (%d) %s", + __func__, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } + + modem_properties_changed (modem->loc_proxy, MM_DBUS_LOC_INTERFACE, props, modem); + g_hash_table_destroy (props); + + /* Now that we know the device supports location services, get basic + * modem properties and start grabbing location info. + */ + dbus_g_proxy_begin_call (modem->props_proxy, "GetAll", + modem_props_cb, modem, NULL, + G_TYPE_STRING, MM_DBUS_MODEM_INTERFACE, G_TYPE_INVALID); +} + +static Modem * +modem_new (DBusGConnection *bus, const char *path, gpointer owner) +{ + Modem *modem; + + modem = g_slice_new0 (Modem); + modem->owner = owner; + modem->path = g_strdup (path); + + modem->loc_proxy = dbus_g_proxy_new_for_name (bus, + MM_DBUS_SERVICE, + path, + MM_DBUS_LOC_INTERFACE); + + modem->modem_proxy = dbus_g_proxy_new_for_name (bus, + MM_DBUS_SERVICE, + path, + MM_DBUS_MODEM_INTERFACE); + + /* Listen for property changes */ + modem->props_proxy = dbus_g_proxy_new_for_name (bus, + MM_DBUS_SERVICE, + path, + "org.freedesktop.DBus.Properties"); + dbus_g_object_register_marshaller (mm_marshal_VOID__STRING_BOXED, + G_TYPE_NONE, + G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (modem->props_proxy, "MmPropertiesChanged", + G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (modem->props_proxy, "MmPropertiesChanged", + G_CALLBACK (modem_properties_changed), + modem, + NULL); + + debugmsg ("%s: (%s) modem created", __func__, path); + + /* Check if the Location interface is actually supported before doing + * anything with the modem, because if it's not, we don't care about + * the modem at all. + */ + dbus_g_proxy_begin_call (modem->props_proxy, "GetAll", + loc_props_cb, modem, NULL, + G_TYPE_STRING, MM_DBUS_LOC_INTERFACE, G_TYPE_INVALID); + + return modem; +} + +static void +modem_free (Modem *modem) +{ + + debugmsg ("%s: (%s) modem removed", __func__, modem->path); + + g_free (modem->path); + g_object_unref (modem->loc_proxy); + g_object_unref (modem->modem_proxy); + g_object_unref (modem->props_proxy); + + if (modem->loc_idle) + g_source_remove (modem->loc_idle); + + memset (modem, 0, sizeof (Modem)); + g_slice_free (Modem, modem); +} + +static void +modem_added (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + GeoclueGsmlocMm *self = GEOCLUE_GSMLOC_MM (user_data); + GeoclueGsmlocMmPrivate *priv = GEOCLUE_GSMLOC_MM_GET_PRIVATE (self); + Modem *modem; + + if (!find_modem (self, path)) { + modem = modem_new (priv->bus, path, self); + priv->modems = g_slist_prepend (priv->modems, modem); + } +} + +static void +enumerate_modems_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + GPtrArray *modems; + GError *error = NULL; + int i; + + if (!dbus_g_proxy_end_call (proxy, call, &error, + dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &modems, + G_TYPE_INVALID)) { + g_warning ("%s: failed to enumerate modems: (%d) %s", + __func__, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } + + for (i = 0; i < modems->len; i++) { + char *path = g_ptr_array_index (modems, i); + + modem_added (NULL, path, GEOCLUE_GSMLOC_MM (user_data)); + g_free (path); + } + g_ptr_array_free (modems, TRUE); +} + +static void +enumerate_modems (GeoclueGsmlocMm *self) +{ + GeoclueGsmlocMmPrivate *priv = GEOCLUE_GSMLOC_MM_GET_PRIVATE (self); + + dbus_g_proxy_begin_call (priv->mm_proxy, "EnumerateDevices", + enumerate_modems_cb, self, NULL, + G_TYPE_INVALID); +} + +static void +modem_removed (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + GeoclueGsmlocMm *self = GEOCLUE_GSMLOC_MM (user_data); + GeoclueGsmlocMmPrivate *priv = GEOCLUE_GSMLOC_MM_GET_PRIVATE (self); + Modem *modem; + + modem = find_modem (self, path); + if (modem) { + gboolean old_available = is_available (self); + + priv->modems = g_slist_remove (priv->modems, modem); + modem_free (modem); + if (is_available (self) != old_available) + g_object_notify (G_OBJECT (self), "available"); + } +} + +static void +kill_modems (GeoclueGsmlocMm *self) +{ + GeoclueGsmlocMmPrivate *priv = GEOCLUE_GSMLOC_MM_GET_PRIVATE (self); + gboolean old_available = is_available (self); + GSList *iter; + + /* Kill all modems */ + for (iter = priv->modems; iter; iter = g_slist_next (iter)) + modem_free ((Modem *) iter->data); + g_slist_free (priv->modems); + priv->modems = NULL; + + /* No more modems; clearly location is no longer available */ + if (old_available) + g_object_notify (G_OBJECT (self), "available"); +} + +static void +name_owner_changed (DBusGProxy *proxy, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + gboolean old_owner_good; + gboolean new_owner_good; + + if (strcmp (MM_DBUS_SERVICE, name) != 0) + return; + + old_owner_good = (old_owner && strlen (old_owner)); + new_owner_good = (new_owner && strlen (new_owner)); + + if (!old_owner_good && new_owner_good) { + debugmsg ("ModemManager appeared"); + enumerate_modems (GEOCLUE_GSMLOC_MM (user_data)); + } else if (old_owner_good && !new_owner_good) { + debugmsg ("ModemManager disappeared"); + kill_modems (GEOCLUE_GSMLOC_MM (user_data)); + } +} + +GeoclueGsmlocMm * +geoclue_gsmloc_mm_new (void) +{ + return (GeoclueGsmlocMm *) g_object_new (GEOCLUE_TYPE_GSMLOC_MM, NULL); +} + +static gboolean +mm_alive (DBusGProxy *proxy) +{ + char *owner = NULL; + gboolean owned = FALSE; + GError *error = NULL; + + if (dbus_g_proxy_call_with_timeout (proxy, + "GetNameOwner", 2000, &error, + G_TYPE_STRING, MM_DBUS_SERVICE, + G_TYPE_INVALID, + G_TYPE_STRING, &owner, + G_TYPE_INVALID)) { + owned = !!owner; + g_free (owner); + } + return owned; +} + +static void +geoclue_gsmloc_mm_init (GeoclueGsmlocMm *self) +{ + GeoclueGsmlocMmPrivate *priv = GEOCLUE_GSMLOC_MM_GET_PRIVATE (self); + + if (getenv ("GEOCLUE_GSMLOC_MM_DEBUG")) + mm_debug = TRUE; + + priv->bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL); + if (!priv->bus) { + g_warning ("Failed to acquire a connection to the D-Bus system bus."); + return; + } + + priv->dbus_proxy = dbus_g_proxy_new_for_name (priv->bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + /* Handle ModemManager restarts */ + dbus_g_proxy_add_signal (priv->dbus_proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->dbus_proxy, "NameOwnerChanged", + G_CALLBACK (name_owner_changed), + self, NULL); + + priv->mm_proxy = dbus_g_proxy_new_for_name (priv->bus, + MM_DBUS_SERVICE, + MM_DBUS_PATH, + MM_DBUS_INTERFACE); + g_assert (priv->mm_proxy); + + dbus_g_proxy_add_signal (priv->mm_proxy, "DeviceAdded", + DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->mm_proxy, "DeviceAdded", + G_CALLBACK (modem_added), self, NULL); + dbus_g_proxy_add_signal (priv->mm_proxy, "DeviceRemoved", + DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->mm_proxy, "DeviceRemoved", + G_CALLBACK (modem_removed), self, NULL); + + if (mm_alive (priv->dbus_proxy)) { + debugmsg ("ModemManager is alive"); + enumerate_modems (self); + } +} + +static void +dispose (GObject *object) +{ + GeoclueGsmlocMmPrivate *priv = GEOCLUE_GSMLOC_MM_GET_PRIVATE (object); + + kill_modems (GEOCLUE_GSMLOC_MM (object)); + + /* Stop listening to ModemManager */ + if (priv->mm_proxy) { + g_object_unref (priv->mm_proxy); + priv->mm_proxy = NULL; + } + + if (priv->props_proxy) { + g_object_unref (priv->props_proxy); + priv->props_proxy = NULL; + } + + if (priv->dbus_proxy) { + g_object_unref (priv->dbus_proxy); + priv->dbus_proxy = NULL; + } + + G_OBJECT_CLASS (geoclue_gsmloc_mm_parent_class)->dispose (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_AVAILABLE: + g_value_set_boolean (value, is_available (GEOCLUE_GSMLOC_MM (object))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +geoclue_gsmloc_mm_class_init (GeoclueGsmlocMmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GeoclueGsmlocMmPrivate)); + + /* virtual methods */ + object_class->get_property = get_property; + object_class->dispose = dispose; + + /* properties */ + g_object_class_install_property + (object_class, PROP_AVAILABLE, + g_param_spec_boolean ("available", + "Available", + "Whether any mobile broadband device is " + "providing location information at this " + "time.", + FALSE, + G_PARAM_READABLE)); + + /* signals */ + signals[NETWORK_DATA_CHANGED] = + g_signal_new ("network-data-changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, 0, + NULL, NULL, + mm_marshal_VOID__STRING_STRING_STRING_STRING, + G_TYPE_NONE, 4, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); +} + diff --git a/providers/gsmloc/geoclue-gsmloc-mm.h b/providers/gsmloc/geoclue-gsmloc-mm.h new file mode 100644 index 0000000..8daf1eb --- /dev/null +++ b/providers/gsmloc/geoclue-gsmloc-mm.h @@ -0,0 +1,56 @@ +/* + * Geoclue + * geoclue-gsmloc-mm.h - An Address/Position provider for ModemManager + * + * Author: Dan Williams <dcbw@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _GEOCLUE_GSMLOC_MM +#define _GEOCLUE_GSMLOC_MM + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GEOCLUE_TYPE_GSMLOC_MM (geoclue_gsmloc_mm_get_type ()) + +#define GEOCLUE_GSMLOC_MM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEOCLUE_TYPE_GSMLOC_MM, GeoclueGsmlocMm)) +#define GEOCLUE_GSMLOC_MM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEOCLUE_TYPE_GSMLOC_MM, GeoclueGsmlocMmClass)) +#define GEOCLUE_IS_GSMLOC_MM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEOCLUE_TYPE_GSMLOC_MM)) +#define GEOCLUE_IS_GSMLOC_MM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEOCLUE_TYPE_GSMLOC_MM)) + +typedef struct _GeoclueGsmlocMm { + GObject parent; +} GeoclueGsmlocMm; + +typedef struct _GeoclueGsmlocMmClass { + GObjectClass parent_class; + + void (*network_data_changed) (GeoclueGsmlocMm *mm, + char *mcc, char *mnc, + char *lac, char *cid); +} GeoclueGsmlocMmClass; + +GType geoclue_gsmloc_mm_get_type (void); + +GeoclueGsmlocMm *geoclue_gsmloc_mm_new (void); + +G_END_DECLS + +#endif /* _GEOCLUE_GSMLOC_MM */ + diff --git a/providers/gsmloc/geoclue-gsmloc.c b/providers/gsmloc/geoclue-gsmloc.c index 222d42c..6d8b5ba 100644 --- a/providers/gsmloc/geoclue-gsmloc.c +++ b/providers/gsmloc/geoclue-gsmloc.c @@ -5,6 +5,7 @@ * Author: Jussi Kukkonen <jku@linux.intel.com> * Copyright 2008 by Garmin Ltd. or its subsidiaries * 2010 Intel Corporation + * 2010 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -31,8 +32,8 @@ * "mobile country code -> ISO country code" lookup table: as a result address * will only ever have country code and country fields. * - * Gsmloc requires the telephony stack oFono to work -- more IMSI data - * sources could be added fairly easily. + * Gsmloc requires the oFono or ModemManager telephony stacks to work -- more + * IMSI data sources could be added fairly easily. **/ #include <config.h> @@ -54,6 +55,9 @@ /* ofono implementation */ #include "geoclue-gsmloc-ofono.h" +/* ModemManager implementation */ +#include "geoclue-gsmloc-mm.h" + /* country code list */ #include "mcc.h" @@ -76,6 +80,7 @@ struct _GeoclueGsmloc { GcWebService *web_service; GeoclueGsmlocOfono *ofono; + GeoclueGsmlocMm *mm; /* current data */ char *mcc; @@ -114,10 +119,12 @@ geoclue_gsmloc_get_status (GcIfaceGeoclue *iface, { GeoclueGsmloc *gsmloc = GEOCLUE_GSMLOC (iface); gboolean ofono_available; + gboolean mm_available; g_object_get (gsmloc->ofono, "available", &ofono_available, NULL); + g_object_get (gsmloc->mm, "available", &mm_available, NULL); - if (!ofono_available) { + if (!ofono_available && !mm_available) { *status = GEOCLUE_STATUS_ERROR; } else if (!gsmloc->mcc || !gsmloc->mnc || !gsmloc->lac || !gsmloc->cid) { @@ -364,6 +371,14 @@ geoclue_gsmloc_dispose (GObject *obj) gsmloc->ofono = NULL; } + if (gsmloc->mm) { + g_signal_handlers_disconnect_by_func (gsmloc->mm, + network_data_changed_cb, + gsmloc); + g_object_unref (gsmloc->mm); + gsmloc->mm = NULL; + } + if (gsmloc->address) { g_hash_table_destroy (gsmloc->address); gsmloc->address = NULL; @@ -408,6 +423,11 @@ geoclue_gsmloc_init (GeoclueGsmloc *gsmloc) gsmloc->ofono = geoclue_gsmloc_ofono_new (); g_signal_connect (gsmloc->ofono, "network-data-changed", G_CALLBACK (network_data_changed_cb), gsmloc); + + /* init mm */ + gsmloc->mm = geoclue_gsmloc_mm_new (); + g_signal_connect (gsmloc->mm, "network-data-changed", + G_CALLBACK (network_data_changed_cb), gsmloc); } static void diff --git a/providers/gsmloc/mm-marshal.list b/providers/gsmloc/mm-marshal.list new file mode 100644 index 0000000..d2ce65a --- /dev/null +++ b/providers/gsmloc/mm-marshal.list @@ -0,0 +1,2 @@ +VOID:STRING,STRING,STRING,STRING +VOID:STRING,BOXED |