/* vim: set et ts=8 sw=8: */ /* * Copyright 2014 Red Hat, Inc. * * 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) */ #include #include #include #include "gclue-cdma.h" #include "gclue-modem-manager.h" #include "gclue-location.h" /** * SECTION:gclue-cdma * @short_description: WiFi-based geolocation * @include: gclue-glib/gclue-cdma.h * * Contains functions to get the geolocation from a CDMA cell tower. **/ struct _GClueCDMAPrivate { GClueModem *modem; GCancellable *cancellable; gulong cdma_notify_id; }; G_DEFINE_TYPE_WITH_CODE (GClueCDMA, gclue_cdma, GCLUE_TYPE_LOCATION_SOURCE, G_ADD_PRIVATE (GClueCDMA)) static GClueLocationSourceStartResult gclue_cdma_start (GClueLocationSource *source); static GClueLocationSourceStopResult gclue_cdma_stop (GClueLocationSource *source); static void refresh_accuracy_level (GClueCDMA *source) { GClueAccuracyLevel new, existing; existing = gclue_location_source_get_available_accuracy_level (GCLUE_LOCATION_SOURCE (source)); if (gclue_modem_get_is_cdma_available (source->priv->modem)) new = GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD; else new = GCLUE_ACCURACY_LEVEL_NONE; if (new != existing) { g_debug ("Available accuracy level from %s: %u", G_OBJECT_TYPE_NAME (source), new); g_object_set (G_OBJECT (source), "available-accuracy-level", new, NULL); } } static void on_cdma_enabled (GObject *source_object, GAsyncResult *result, gpointer user_data) { g_autoptr(GError) error = NULL; if (!gclue_modem_enable_cdma_finish (GCLUE_MODEM (source_object), result, &error)) { if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_warning ("Failed to enable CDMA: %s", error->message); } } } static void on_is_cdma_available_notify (GObject *gobject, GParamSpec *pspec, gpointer user_data) { GClueCDMA *source = GCLUE_CDMA (user_data); GClueCDMAPrivate *priv = source->priv; refresh_accuracy_level (source); if (gclue_location_source_get_active (GCLUE_LOCATION_SOURCE (source)) && gclue_modem_get_is_cdma_available (priv->modem)) gclue_modem_enable_cdma (priv->modem, priv->cancellable, on_cdma_enabled, source); } static void gclue_cdma_finalize (GObject *gcdma) { GClueCDMAPrivate *priv = GCLUE_CDMA (gcdma)->priv; G_OBJECT_CLASS (gclue_cdma_parent_class)->finalize (gcdma); g_signal_handler_disconnect (priv->modem, priv->cdma_notify_id); priv->cdma_notify_id = 0; g_cancellable_cancel (priv->cancellable); g_clear_object (&priv->cancellable); g_clear_object (&priv->modem); } static void gclue_cdma_class_init (GClueCDMAClass *klass) { GClueLocationSourceClass *source_class = GCLUE_LOCATION_SOURCE_CLASS (klass); GObjectClass *gcdma_class = G_OBJECT_CLASS (klass); gcdma_class->finalize = gclue_cdma_finalize; source_class->start = gclue_cdma_start; source_class->stop = gclue_cdma_stop; } static void gclue_cdma_init (GClueCDMA *source) { GClueCDMAPrivate *priv; source->priv = gclue_cdma_get_instance_private (source); priv = source->priv; priv->cancellable = g_cancellable_new (); priv->modem = gclue_modem_manager_get_singleton (); priv->cdma_notify_id = g_signal_connect (priv->modem, "notify::is-cdma-available", G_CALLBACK (on_is_cdma_available_notify), source); } static void on_cdma_destroyed (gpointer data, GObject *where_the_object_was) { GClueCDMA **source = (GClueCDMA **) data; *source = NULL; } /** * gclue_cdma_get_singleton: * * Get the #GClueCDMA singleton. * * Returns: (transfer full): a new ref to #GClueCDMA. Use g_object_unref() * when done. **/ GClueCDMA * gclue_cdma_get_singleton (void) { static GClueCDMA *source = NULL; if (source == NULL) { source = g_object_new (GCLUE_TYPE_CDMA, "compute-movement", FALSE, NULL); g_object_weak_ref (G_OBJECT (source), on_cdma_destroyed, &source); } else g_object_ref (source); return source; } static void on_fix_cdma (GClueModem *modem, gdouble latitude, gdouble longitude, gpointer user_data) { GClueLocation *location; location = gclue_location_new (latitude, longitude, 1000, /* Assume 1 km accuracy */ "CDMA"); gclue_location_source_set_location (GCLUE_LOCATION_SOURCE (user_data), location); g_object_unref (location); } static GClueLocationSourceStartResult gclue_cdma_start (GClueLocationSource *source) { GClueLocationSourceClass *base_class; GClueCDMAPrivate *priv; GClueLocationSourceStartResult base_result; g_return_val_if_fail (GCLUE_IS_LOCATION_SOURCE (source), GCLUE_LOCATION_SOURCE_START_RESULT_FAILED); priv = GCLUE_CDMA (source)->priv; base_class = GCLUE_LOCATION_SOURCE_CLASS (gclue_cdma_parent_class); base_result = base_class->start (source); if (base_result != GCLUE_LOCATION_SOURCE_START_RESULT_OK) return base_result; g_signal_connect (priv->modem, "fix-cdma", G_CALLBACK (on_fix_cdma), source); if (gclue_modem_get_is_cdma_available (priv->modem)) gclue_modem_enable_cdma (priv->modem, priv->cancellable, on_cdma_enabled, source); return base_result; } static GClueLocationSourceStopResult gclue_cdma_stop (GClueLocationSource *source) { GClueCDMAPrivate *priv = GCLUE_CDMA (source)->priv; GClueLocationSourceClass *base_class; g_autoptr(GError) error = NULL; GClueLocationSourceStopResult base_result; g_return_val_if_fail (GCLUE_IS_LOCATION_SOURCE (source), FALSE); base_class = GCLUE_LOCATION_SOURCE_CLASS (gclue_cdma_parent_class); base_result = base_class->stop (source); if (base_result == GCLUE_LOCATION_SOURCE_STOP_RESULT_STILL_USED) return base_result; g_signal_handlers_disconnect_by_func (G_OBJECT (priv->modem), G_CALLBACK (on_fix_cdma), source); if (gclue_modem_get_is_cdma_available (priv->modem)) if (!gclue_modem_disable_cdma (priv->modem, priv->cancellable, &error)) { g_warning ("Failed to disable CDMA: %s", error->message); } return base_result; }