/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This program 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. * * This program 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: * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2012 Lanedo GmbH * Copyright (C) 2012-2019 Aleksander Morgado */ #include #define _LIBMM_INSIDE_MM #include #include "mm-iface-modem.h" #include "mm-iface-modem-location.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #define MM_LOCATION_GPS_REFRESH_TIME_SECS 30 #define LOCATION_CONTEXT_TAG "location-context-tag" static GQuark location_context_quark; /*****************************************************************************/ void mm_iface_modem_location_bind_simple_status (MMIfaceModemLocation *self, MMSimpleStatus *status) { } /*****************************************************************************/ typedef struct { /* 3GPP location */ MMLocation3gpp *location_3gpp; /* GPS location */ time_t location_gps_nmea_last_time; MMLocationGpsNmea *location_gps_nmea; time_t location_gps_raw_last_time; MMLocationGpsRaw *location_gps_raw; /* CDMA BS location */ MMLocationCdmaBs *location_cdma_bs; } LocationContext; static void location_context_free (LocationContext *ctx) { if (ctx->location_3gpp) g_object_unref (ctx->location_3gpp); if (ctx->location_gps_nmea) g_object_unref (ctx->location_gps_nmea); if (ctx->location_gps_raw) g_object_unref (ctx->location_gps_raw); if (ctx->location_cdma_bs) g_object_unref (ctx->location_cdma_bs); g_free (ctx); } static void clear_location_context (MMIfaceModemLocation *self) { if (G_UNLIKELY (!location_context_quark)) location_context_quark = (g_quark_from_static_string ( LOCATION_CONTEXT_TAG)); /* Clear all location data */ g_object_set_qdata (G_OBJECT (self), location_context_quark, NULL); } static LocationContext * get_location_context (MMIfaceModemLocation *self) { LocationContext *ctx; if (G_UNLIKELY (!location_context_quark)) location_context_quark = (g_quark_from_static_string ( LOCATION_CONTEXT_TAG)); ctx = g_object_get_qdata (G_OBJECT (self), location_context_quark); if (!ctx) { /* Create context and keep it as object data */ ctx = g_new0 (LocationContext, 1); g_object_set_qdata_full ( G_OBJECT (self), location_context_quark, ctx, (GDestroyNotify)location_context_free); } return ctx; } /*****************************************************************************/ static GVariant * build_location_dictionary (GVariant *previous, MMLocation3gpp *location_3gpp, MMLocationGpsNmea *location_gps_nmea, MMLocationGpsRaw *location_gps_raw, MMLocationCdmaBs *location_cdma_bs) { GVariant *location_3gpp_value = NULL; GVariant *location_gps_nmea_value = NULL; GVariant *location_gps_raw_value = NULL; GVariant *location_cdma_bs_value = NULL; GVariantBuilder builder; /* If a previous dictionary given, parse its values */ if (previous) { guint source; GVariant *value; GVariantIter iter; g_variant_iter_init (&iter, previous); while (g_variant_iter_next (&iter, "{uv}", &source, &value)) { switch (source) { case MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI: g_assert (!location_3gpp_value); location_3gpp_value = value; break; case MM_MODEM_LOCATION_SOURCE_GPS_NMEA: g_assert (!location_gps_nmea_value); location_gps_nmea_value = value; break; case MM_MODEM_LOCATION_SOURCE_GPS_RAW: g_assert (!location_gps_raw_value); location_gps_raw_value = value; break; case MM_MODEM_LOCATION_SOURCE_CDMA_BS: g_assert (!location_cdma_bs_value); location_cdma_bs_value = value; break; case MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED: g_assert_not_reached (); case MM_MODEM_LOCATION_SOURCE_AGPS_MSA: g_assert_not_reached (); case MM_MODEM_LOCATION_SOURCE_AGPS_MSB: g_assert_not_reached (); default: g_warn_if_reached (); g_variant_unref (value); break; } } } /* Build the new one */ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{uv}")); /* If a new one given, use it */ if (location_3gpp) { if (location_3gpp_value) g_variant_unref (location_3gpp_value); location_3gpp_value = mm_location_3gpp_get_string_variant (location_3gpp); } if (location_3gpp_value) { g_assert (!g_variant_is_floating (location_3gpp_value)); g_variant_builder_add (&builder, "{uv}", MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI, location_3gpp_value); g_variant_unref (location_3gpp_value); } /* If a new one given, use it */ if (location_gps_nmea) { if (location_gps_nmea_value) g_variant_unref (location_gps_nmea_value); location_gps_nmea_value = mm_location_gps_nmea_get_string_variant (location_gps_nmea); } if (location_gps_nmea_value) { g_assert (!g_variant_is_floating (location_gps_nmea_value)); g_variant_builder_add (&builder, "{uv}", MM_MODEM_LOCATION_SOURCE_GPS_NMEA, location_gps_nmea_value); g_variant_unref (location_gps_nmea_value); } /* If a new one given, use it */ if (location_gps_raw) { if (location_gps_raw_value) g_variant_unref (location_gps_raw_value); location_gps_raw_value = mm_location_gps_raw_get_dictionary (location_gps_raw); } if (location_gps_raw_value) { g_assert (!g_variant_is_floating (location_gps_raw_value)); g_variant_builder_add (&builder, "{uv}", MM_MODEM_LOCATION_SOURCE_GPS_RAW, location_gps_raw_value); g_variant_unref (location_gps_raw_value); } /* If a new one given, use it */ if (location_cdma_bs) { if (location_cdma_bs_value) g_variant_unref (location_cdma_bs_value); location_cdma_bs_value = mm_location_cdma_bs_get_dictionary (location_cdma_bs); } if (location_cdma_bs_value) { g_assert (!g_variant_is_floating (location_cdma_bs_value)); g_variant_builder_add (&builder, "{uv}", MM_MODEM_LOCATION_SOURCE_CDMA_BS, location_cdma_bs_value); g_variant_unref (location_cdma_bs_value); } return g_variant_builder_end (&builder); } /*****************************************************************************/ static void notify_gps_location_update (MMIfaceModemLocation *self, MmGdbusModemLocation *skeleton, MMLocationGpsNmea *location_gps_nmea, MMLocationGpsRaw *location_gps_raw) { mm_obj_dbg (self, "GPS location updated"); /* We only update the property if we are supposed to signal * location */ if (mm_gdbus_modem_location_get_signals_location (skeleton)) mm_gdbus_modem_location_set_location ( skeleton, build_location_dictionary (mm_gdbus_modem_location_get_location (skeleton), NULL, location_gps_nmea, location_gps_raw, NULL)); } static void location_gps_update_nmea (MMIfaceModemLocation *self, const gchar *nmea_trace) { MmGdbusModemLocation *skeleton; LocationContext *ctx; gboolean update_nmea = FALSE; gboolean update_raw = FALSE; ctx = get_location_context (self); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_GPS_NMEA) { g_assert (ctx->location_gps_nmea != NULL); if (mm_location_gps_nmea_add_trace (ctx->location_gps_nmea, nmea_trace) && (ctx->location_gps_nmea_last_time == 0 || time (NULL) - ctx->location_gps_nmea_last_time >= (glong)mm_gdbus_modem_location_get_gps_refresh_rate (skeleton))) { ctx->location_gps_nmea_last_time = time (NULL); update_nmea = TRUE; } } if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_GPS_RAW) { g_assert (ctx->location_gps_raw != NULL); if (mm_location_gps_raw_add_trace (ctx->location_gps_raw, nmea_trace) && (ctx->location_gps_raw_last_time == 0 || time (NULL) - ctx->location_gps_raw_last_time >= (glong)mm_gdbus_modem_location_get_gps_refresh_rate (skeleton))) { ctx->location_gps_raw_last_time = time (NULL); update_raw = TRUE; } } if (update_nmea || update_raw) notify_gps_location_update (self, skeleton, update_nmea ? ctx->location_gps_nmea : NULL, update_raw ? ctx->location_gps_raw : NULL); g_object_unref (skeleton); } void mm_iface_modem_location_gps_update (MMIfaceModemLocation *self, const gchar *nmea_trace) { /* Helper to debug GPS location related issues. Don't depend on a real GPS * fix for debugging, just use some random values to update */ #if 0 { const gchar *prefix = NULL; const gchar *lat = NULL; /* lat N/S just to test which one is used */ if (g_str_has_prefix (nmea_trace, "$GPGGA")) { prefix = "GPGGA"; lat = "S"; } else if (g_str_has_prefix (nmea_trace, "$GNGGA")) { prefix = "GNGGA"; lat = "N"; } if (prefix && lat) { g_autoptr(GString) str = NULL; g_autoptr(GDateTime) now = NULL; mm_obj_dbg (self, "GGA trace detected: %s", nmea_trace); now = g_date_time_new_now_utc (); str = g_string_new (""); g_string_append_printf (str, "$%s,%02u%02u%02u,4807.038,%s,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47", prefix, g_date_time_get_hour (now), g_date_time_get_minute (now), g_date_time_get_second (now), lat); location_gps_update_nmea (self, str->str); return; } } #endif location_gps_update_nmea (self, nmea_trace); } /*****************************************************************************/ static void notify_3gpp_location_update (MMIfaceModemLocation *self, MmGdbusModemLocation *skeleton, MMLocation3gpp *location_3gpp) { const gchar *operator_code; operator_code = mm_location_3gpp_get_operator_code (location_3gpp); mm_obj_dbg (self, "3GPP location updated " "(MCCMNC: '%s', location area code: '%04lX', tracking area code: '%06lX', cell ID: '%08lX')", operator_code ? operator_code : "", mm_location_3gpp_get_location_area_code (location_3gpp), mm_location_3gpp_get_tracking_area_code (location_3gpp), mm_location_3gpp_get_cell_id (location_3gpp)); /* We only update the property if we are supposed to signal * location */ if (mm_gdbus_modem_location_get_signals_location (skeleton)) mm_gdbus_modem_location_set_location ( skeleton, build_location_dictionary (mm_gdbus_modem_location_get_location (skeleton), location_3gpp, NULL, NULL, NULL)); } void mm_iface_modem_location_3gpp_update_operator_code (MMIfaceModemLocation *self, const gchar *operator_code) { MmGdbusModemLocation *skeleton; LocationContext *ctx; ctx = get_location_context (self); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI) { guint changed = 0; g_assert (ctx->location_3gpp != NULL); changed += mm_location_3gpp_set_operator_code (ctx->location_3gpp, operator_code); if (changed) notify_3gpp_location_update (self, skeleton, ctx->location_3gpp); } g_object_unref (skeleton); } void mm_iface_modem_location_3gpp_update_lac_tac_ci (MMIfaceModemLocation *self, gulong location_area_code, gulong tracking_area_code, gulong cell_id) { g_autoptr(MmGdbusModemLocationSkeleton) skeleton = NULL; LocationContext *ctx; guint changed = 0; gulong old_location_area_code; gulong old_tracking_area_code; gulong old_cell_id; g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton || !(mm_gdbus_modem_location_get_enabled (MM_GDBUS_MODEM_LOCATION (skeleton)) & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI)) return; ctx = get_location_context (self); g_assert (ctx->location_3gpp != NULL); old_location_area_code = mm_location_3gpp_get_location_area_code (ctx->location_3gpp); old_tracking_area_code = mm_location_3gpp_get_tracking_area_code (ctx->location_3gpp); old_cell_id = mm_location_3gpp_get_cell_id (ctx->location_3gpp); /* Update LAC if given, and clear TAC unless a TAC is also given */ if (location_area_code) { if (old_location_area_code != location_area_code) { mm_obj_dbg (self, "3GPP location area code updated: '%04lX->%04lX'", old_location_area_code, location_area_code); mm_location_3gpp_set_location_area_code (ctx->location_3gpp, location_area_code); changed++; } if (!tracking_area_code) { if (old_tracking_area_code != 0) { mm_obj_dbg (self, "3GPP tracking area code cleared: '%06lX->%06lX'", old_tracking_area_code, tracking_area_code); mm_location_3gpp_set_tracking_area_code (ctx->location_3gpp, 0); changed++; } } } /* Update TAC if given, and clear LAC unless a LAC is also given */ if (tracking_area_code) { if (old_tracking_area_code != tracking_area_code) { mm_obj_dbg (self, "3GPP tracking area code updated: '%06lX->%06lX'", old_tracking_area_code, tracking_area_code); mm_location_3gpp_set_tracking_area_code (ctx->location_3gpp, tracking_area_code); changed++; } if (!location_area_code) { if (old_location_area_code != 0) { mm_obj_dbg (self, "3GPP location area code cleared: '%04lX->%04lX'", old_location_area_code, location_area_code); mm_location_3gpp_set_location_area_code (ctx->location_3gpp, 0); changed++; } } } /* Cell ID only updated if given. It is assumed that if LAC or TAC are given, CID is also given */ if (cell_id && (old_cell_id != cell_id)) { mm_obj_dbg (self, "3GPP cell id updated: '%08lX->%08lX'", old_cell_id, cell_id); mm_location_3gpp_set_cell_id (ctx->location_3gpp, cell_id); changed++; } if (changed) notify_3gpp_location_update (self, MM_GDBUS_MODEM_LOCATION (skeleton), ctx->location_3gpp); } void mm_iface_modem_location_3gpp_clear (MMIfaceModemLocation *self) { MmGdbusModemLocation *skeleton; LocationContext *ctx; ctx = get_location_context (self); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI) { g_assert (ctx->location_3gpp != NULL); if (mm_location_3gpp_reset (ctx->location_3gpp)) notify_3gpp_location_update (self, skeleton, ctx->location_3gpp); } g_object_unref (skeleton); } /*****************************************************************************/ static void notify_cdma_bs_location_update (MMIfaceModemLocation *self, MmGdbusModemLocation *skeleton, MMLocationCdmaBs *location_cdma_bs) { mm_obj_dbg (self, "CDMA base station location updated (longitude: '%lf', latitude: '%lf')", mm_location_cdma_bs_get_longitude (location_cdma_bs), mm_location_cdma_bs_get_latitude (location_cdma_bs)); /* We only update the property if we are supposed to signal * location */ if (mm_gdbus_modem_location_get_signals_location (skeleton)) mm_gdbus_modem_location_set_location ( skeleton, build_location_dictionary (mm_gdbus_modem_location_get_location (skeleton), NULL, NULL, NULL, location_cdma_bs)); } void mm_iface_modem_location_cdma_bs_update (MMIfaceModemLocation *self, gdouble longitude, gdouble latitude) { MmGdbusModemLocation *skeleton; LocationContext *ctx; ctx = get_location_context (self); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_CDMA_BS) { if (mm_location_cdma_bs_set (ctx->location_cdma_bs, longitude, latitude)) notify_cdma_bs_location_update (self, skeleton, ctx->location_cdma_bs); } g_object_unref (skeleton); } void mm_iface_modem_location_cdma_bs_clear (MMIfaceModemLocation *self) { mm_iface_modem_location_cdma_bs_update (self, MM_LOCATION_LONGITUDE_UNKNOWN, MM_LOCATION_LATITUDE_UNKNOWN); } /*****************************************************************************/ static void update_location_source_status (MMIfaceModemLocation *self, MMModemLocationSource source, gboolean enabled) { MMModemLocationSource mask; MmGdbusModemLocation *skeleton = NULL; LocationContext *ctx; g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) return; /* Update status in the interface */ mask = mm_gdbus_modem_location_get_enabled (skeleton); if (enabled) mask |= source; else mask &= ~source; /* Update status in the context */ ctx = get_location_context (self); switch (source) { case MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI: if (enabled) { if (!ctx->location_3gpp) ctx->location_3gpp = mm_location_3gpp_new (); } else g_clear_object (&ctx->location_3gpp); break; case MM_MODEM_LOCATION_SOURCE_GPS_NMEA: if (enabled) { if (!ctx->location_gps_nmea) ctx->location_gps_nmea = mm_location_gps_nmea_new (); } else g_clear_object (&ctx->location_gps_nmea); break; case MM_MODEM_LOCATION_SOURCE_GPS_RAW: if (enabled) { if (!ctx->location_gps_raw) ctx->location_gps_raw = mm_location_gps_raw_new (); } else g_clear_object (&ctx->location_gps_raw); break; case MM_MODEM_LOCATION_SOURCE_CDMA_BS: if (enabled) { if (!ctx->location_cdma_bs) ctx->location_cdma_bs = mm_location_cdma_bs_new (); } else g_clear_object (&ctx->location_cdma_bs); break; case MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED: case MM_MODEM_LOCATION_SOURCE_AGPS_MSA: case MM_MODEM_LOCATION_SOURCE_AGPS_MSB: case MM_MODEM_LOCATION_SOURCE_NONE: /* Nothing to setup in the context */ default: break; } mm_gdbus_modem_location_set_enabled (skeleton, mask); g_object_unref (skeleton); } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; MMModemLocationSource to_enable; MMModemLocationSource to_disable; MMModemLocationSource current; } SetupGatheringContext; static void setup_gathering_step (GTask *task); static void setup_gathering_context_free (SetupGatheringContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } static gboolean setup_gathering_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { SetupGatheringContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering_finish (self, res, &error)) { gchar *str; update_location_source_status (self, ctx->current, FALSE); str = mm_modem_location_source_build_string_from_mask (ctx->current); g_prefix_error (&error, "Couldn't enable location '%s' gathering: ", str); g_task_return_error (task, error); g_object_unref (task); g_free (str); return; } /* Keep on with next ones... */ ctx->current = ctx->current << 1; setup_gathering_step (task); } static void disable_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { SetupGatheringContext *ctx; GError *error = NULL; ctx = g_task_get_task_data (task); if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering_finish (self, res, &error)) { gchar *str; /* Back to enabled then */ update_location_source_status (self, ctx->current, TRUE); str = mm_modem_location_source_build_string_from_mask (ctx->current); g_prefix_error (&error, "Couldn't disable location '%s' gathering: ", str); g_task_return_error (task, error); g_object_unref (task); g_free (str); return; } /* Keep on with next ones... */ ctx->current = ctx->current << 1; setup_gathering_step (task); } static void setup_gathering_step (GTask *task) { MMIfaceModemLocation *self; SetupGatheringContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* Are we done? */ if (ctx->to_enable == MM_MODEM_LOCATION_SOURCE_NONE && ctx->to_disable == MM_MODEM_LOCATION_SOURCE_NONE) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } while (ctx->current <= MM_MODEM_LOCATION_SOURCE_LAST) { gchar *source_str; if (ctx->to_enable & ctx->current) { /* Remove from mask */ ctx->to_enable &= ~ctx->current; /* We update the location source status before launching the * specific actions to enable the gathering, so that we are * able to get location updates while the gathering gets * enabled. */ update_location_source_status (self, ctx->current, TRUE); /* Plugins can run custom actions to enable location gathering */ if (MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering ( MM_IFACE_MODEM_LOCATION (self), ctx->current, (GAsyncReadyCallback)enable_location_gathering_ready, task); return; } source_str = mm_modem_location_source_build_string_from_mask (ctx->current); mm_obj_dbg (self, "enabled location '%s' gathering...", source_str); g_free (source_str); } else if (ctx->to_disable & ctx->current) { /* Remove from mask */ ctx->to_disable &= ~ctx->current; update_location_source_status (self, ctx->current, FALSE); /* Plugins can run custom actions to disable location gathering */ if (MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering ( MM_IFACE_MODEM_LOCATION (self), ctx->current, (GAsyncReadyCallback)disable_location_gathering_ready, task); return; } source_str = mm_modem_location_source_build_string_from_mask (ctx->current); mm_obj_dbg (self, "disabled location '%s' gathering...", source_str); g_free (source_str); } /* go on... */ ctx->current = ctx->current << 1; } /* We just need to finish now */ g_assert (ctx->to_enable == MM_MODEM_LOCATION_SOURCE_NONE); g_assert (ctx->to_disable == MM_MODEM_LOCATION_SOURCE_NONE); setup_gathering_step (task); } static void setup_gathering (MMIfaceModemLocation *self, MMModemLocationSource mask, GAsyncReadyCallback callback, gpointer user_data) { SetupGatheringContext *ctx; GTask *task; MMModemLocationSource currently_enabled; MMModemLocationSource source; gchar *str; gboolean allow_gps_unmanaged_always = FALSE; ctx = g_new (SetupGatheringContext, 1); task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)setup_gathering_context_free); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &ctx->skeleton, MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS, &allow_gps_unmanaged_always, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } /* Get current list of enabled sources */ currently_enabled = mm_gdbus_modem_location_get_enabled (ctx->skeleton); /* Reset the list of sources to enable or disable */ ctx->to_enable = MM_MODEM_LOCATION_SOURCE_NONE; ctx->to_disable = MM_MODEM_LOCATION_SOURCE_NONE; /* Loop through all known bits in the bitmask to enable/disable specific location sources */ for (source = MM_MODEM_LOCATION_SOURCE_FIRST; source <= MM_MODEM_LOCATION_SOURCE_LAST; source = source << 1) { /* skip unsupported sources */ if (!(mm_gdbus_modem_location_get_capabilities (ctx->skeleton) & source)) continue; str = mm_modem_location_source_build_string_from_mask (source); if (mask & source) { /* Source set in mask, need to enable if disabled */ if (currently_enabled & source) mm_obj_dbg (self, "location '%s' gathering is already enabled...", str); else ctx->to_enable |= source; } else { /* Source unset in mask, need to disable if enabled */ if (currently_enabled & source) ctx->to_disable |= source; else mm_obj_dbg (self, "location '%s' gathering is already disabled...", str); } g_free (str); } /* When standard GPS retrieval (RAW/NMEA) is enabled, we cannot enable the * UNMANAGED setup, and viceversa, unless explicitly allowed to do so by the * plugin implementation (e.g. if the RAW/NMEA sources don't use the same TTY * as the GPS UNMANAGED setup. */ if (!allow_gps_unmanaged_always && ((ctx->to_enable & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED && currently_enabled & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) || (ctx->to_enable & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA) && currently_enabled & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED) || (ctx->to_enable & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA) && ctx->to_enable & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot have both unmanaged GPS and raw/nmea GPS enabled at the same time"); g_object_unref (task); return; } /* MSA A-GPS and MSB A-GPS cannot be set at the same time */ if ((ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSA && currently_enabled & MM_MODEM_LOCATION_SOURCE_AGPS_MSB) || (ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSB && currently_enabled & MM_MODEM_LOCATION_SOURCE_AGPS_MSA) || (ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSA && ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSB)) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Cannot have both MSA A-GPS and MSB A-GPS enabled at the same time"); g_object_unref (task); return; } if (ctx->to_enable != MM_MODEM_LOCATION_SOURCE_NONE) { str = mm_modem_location_source_build_string_from_mask (ctx->to_enable); mm_obj_dbg (self, "need to enable the following location sources: '%s'", str); g_free (str); } if (ctx->to_disable != MM_MODEM_LOCATION_SOURCE_NONE) { str = mm_modem_location_source_build_string_from_mask (ctx->to_disable); mm_obj_dbg (self, "need to disable the following location sources: '%s'", str); g_free (str); } /* Start enabling/disabling location sources */ ctx->current = MM_MODEM_LOCATION_SOURCE_FIRST; setup_gathering_step (task); } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemLocation *self; guint32 sources; gboolean signal_location; } HandleSetupContext; static void handle_setup_context_free (HandleSetupContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx); } static void setup_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, HandleSetupContext *ctx) { GError *error = NULL; if (!setup_gathering_finish (self, res, &error)) g_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_modem_location_complete_setup (ctx->skeleton, ctx->invocation); handle_setup_context_free (ctx); } static void handle_setup_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetupContext *ctx) { GError *error = NULL; MMModemState modem_state; MMModemLocationSource not_supported; LocationContext *location_ctx; gchar *str; if (!mm_base_modem_authorize_finish (self, res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); handle_setup_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state < MM_MODEM_STATE_ENABLED) { g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot setup location: " "device not yet enabled"); handle_setup_context_free (ctx); return; } /* If any of the location sources being enabled is NOT supported, set error */ not_supported = ((mm_gdbus_modem_location_get_capabilities (ctx->skeleton) ^ ctx->sources) & ctx->sources); if (not_supported != MM_MODEM_LOCATION_SOURCE_NONE) { str = mm_modem_location_source_build_string_from_mask (not_supported); g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot enable unsupported location sources: '%s'", str); handle_setup_context_free (ctx); g_free (str); return; } /* Enable/disable location signaling */ location_ctx = get_location_context (ctx->self); if (mm_gdbus_modem_location_get_signals_location (ctx->skeleton) != ctx->signal_location) { mm_obj_dbg (self, "%s location signaling", ctx->signal_location ? "enabling" : "disabling"); mm_gdbus_modem_location_set_signals_location (ctx->skeleton, ctx->signal_location); if (ctx->signal_location) mm_gdbus_modem_location_set_location ( ctx->skeleton, build_location_dictionary (mm_gdbus_modem_location_get_location (ctx->skeleton), location_ctx->location_3gpp, location_ctx->location_gps_nmea, location_ctx->location_gps_raw, location_ctx->location_cdma_bs)); else mm_gdbus_modem_location_set_location ( ctx->skeleton, build_location_dictionary (NULL, NULL, NULL, NULL, NULL)); } str = mm_modem_location_source_build_string_from_mask (ctx->sources); mm_obj_dbg (self, "setting up location sources: '%s'", str); g_free (str); /* Go on to enable or disable the requested sources */ setup_gathering (ctx->self, ctx->sources, (GAsyncReadyCallback)setup_gathering_ready, ctx); } static gboolean handle_setup (MmGdbusModemLocation *skeleton, GDBusMethodInvocation *invocation, guint32 sources, gboolean signal_location, MMIfaceModemLocation *self) { HandleSetupContext *ctx; ctx = g_new (HandleSetupContext, 1); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->sources = sources; ctx->signal_location = signal_location; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_setup_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemLocation *self; gchar *supl; } HandleSetSuplServerContext; static void handle_set_supl_server_context_free (HandleSetSuplServerContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx->supl); g_slice_free (HandleSetSuplServerContext, ctx); } static void set_supl_server_ready (MMIfaceModemLocation *self, GAsyncResult *res, HandleSetSuplServerContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->set_supl_server_finish (self, res, &error)) g_dbus_method_invocation_take_error (ctx->invocation, error); else { mm_gdbus_modem_location_set_supl_server (ctx->skeleton, ctx->supl); mm_gdbus_modem_location_complete_set_supl_server (ctx->skeleton, ctx->invocation); } handle_set_supl_server_context_free (ctx); } static void handle_set_supl_server_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetSuplServerContext *ctx) { GError *error = NULL; MMModemState modem_state; if (!mm_base_modem_authorize_finish (self, res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_supl_server_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state < MM_MODEM_STATE_ENABLED) { g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot set SUPL server: " "device not yet enabled"); handle_set_supl_server_context_free (ctx); return; } /* If A-GPS is NOT supported, set error */ if (!(mm_gdbus_modem_location_get_capabilities (ctx->skeleton) & (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB))) { g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set SUPL server: A-GPS not supported"); handle_set_supl_server_context_free (ctx); return; } /* Validate SUPL address string: either FQDN:PORT or IP:PORT */ if (!mm_parse_supl_address (ctx->supl, NULL, NULL, NULL, &error)) { g_dbus_method_invocation_return_gerror (ctx->invocation, error); handle_set_supl_server_context_free (ctx); return; } /* Check if plugin implements it */ if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->set_supl_server || !MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->set_supl_server_finish) { g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set SUPL server: not implemented"); handle_set_supl_server_context_free (ctx); return; } /* Request to change SUPL server */ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->set_supl_server (ctx->self, ctx->supl, (GAsyncReadyCallback)set_supl_server_ready, ctx); } static gboolean handle_set_supl_server (MmGdbusModemLocation *skeleton, GDBusMethodInvocation *invocation, const gchar *supl, MMIfaceModemLocation *self) { HandleSetSuplServerContext *ctx; ctx = g_slice_new (HandleSetSuplServerContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->supl = g_strdup (supl); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_supl_server_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemLocation *self; GVariant *datav; } HandleInjectAssistanceDataContext; static void handle_inject_assistance_data_context_free (HandleInjectAssistanceDataContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_variant_unref (ctx->datav); g_slice_free (HandleInjectAssistanceDataContext, ctx); } static void inject_assistance_data_ready (MMIfaceModemLocation *self, GAsyncResult *res, HandleInjectAssistanceDataContext *ctx) { GError *error = NULL; if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data_finish (self, res, &error)) g_dbus_method_invocation_take_error (ctx->invocation, error); else mm_gdbus_modem_location_complete_inject_assistance_data (ctx->skeleton, ctx->invocation); handle_inject_assistance_data_context_free (ctx); } static void handle_inject_assistance_data_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleInjectAssistanceDataContext *ctx) { GError *error = NULL; const guint8 *data; gsize data_size; if (!mm_base_modem_authorize_finish (self, res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); handle_inject_assistance_data_context_free (ctx); return; } /* If the type is NOT supported, set error */ if (mm_gdbus_modem_location_get_supported_assistance_data (ctx->skeleton) == MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE) { g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot inject assistance data: ununsupported"); handle_inject_assistance_data_context_free (ctx); return; } /* Check if plugin implements it */ if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data || !MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data_finish) { g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot inject assistance data: not implemented"); handle_inject_assistance_data_context_free (ctx); return; } data = (const guint8 *) g_variant_get_fixed_array (ctx->datav, &data_size, sizeof (guint8)); /* Request to inject assistance data */ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data (ctx->self, data, data_size, (GAsyncReadyCallback)inject_assistance_data_ready, ctx); } static gboolean handle_inject_assistance_data (MmGdbusModemLocation *skeleton, GDBusMethodInvocation *invocation, GVariant *datav, MMIfaceModemLocation *self) { HandleInjectAssistanceDataContext *ctx; ctx = g_slice_new (HandleInjectAssistanceDataContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->datav = g_variant_ref (datav); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_inject_assistance_data_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemLocation *self; guint rate; } HandleSetGpsRefreshRateContext; static void handle_set_gps_refresh_rate_context_free (HandleSetGpsRefreshRateContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_slice_free (HandleSetGpsRefreshRateContext, ctx); } static void handle_set_gps_refresh_rate_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleSetGpsRefreshRateContext *ctx) { GError *error = NULL; MMModemState modem_state; if (!mm_base_modem_authorize_finish (self, res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); handle_set_gps_refresh_rate_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state < MM_MODEM_STATE_ENABLED) { g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot set SUPL server: " "device not yet enabled"); handle_set_gps_refresh_rate_context_free (ctx); return; } /* If GPS is NOT supported, set error */ if (!(mm_gdbus_modem_location_get_capabilities (ctx->skeleton) & ((MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)))) { g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set GPS refresh rate: GPS not supported"); handle_set_gps_refresh_rate_context_free (ctx); return; } /* Set the new rate in the interface */ mm_gdbus_modem_location_set_gps_refresh_rate (ctx->skeleton, ctx->rate); mm_gdbus_modem_location_complete_set_gps_refresh_rate (ctx->skeleton, ctx->invocation); handle_set_gps_refresh_rate_context_free (ctx); } static gboolean handle_set_gps_refresh_rate (MmGdbusModemLocation *skeleton, GDBusMethodInvocation *invocation, guint rate, MMIfaceModemLocation *self) { HandleSetGpsRefreshRateContext *ctx; ctx = g_slice_new (HandleSetGpsRefreshRateContext); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); ctx->rate = rate; mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_DEVICE_CONTROL, (GAsyncReadyCallback)handle_set_gps_refresh_rate_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct { MmGdbusModemLocation *skeleton; GDBusMethodInvocation *invocation; MMIfaceModemLocation *self; } HandleGetLocationContext; static void handle_get_location_context_free (HandleGetLocationContext *ctx) { g_object_unref (ctx->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); g_free (ctx); } static void handle_get_location_auth_ready (MMBaseModem *self, GAsyncResult *res, HandleGetLocationContext *ctx) { MMModemState modem_state; LocationContext *location_ctx; GError *error = NULL; if (!mm_base_modem_authorize_finish (self, res, &error)) { g_dbus_method_invocation_take_error (ctx->invocation, error); handle_get_location_context_free (ctx); return; } modem_state = MM_MODEM_STATE_UNKNOWN; g_object_get (self, MM_IFACE_MODEM_STATE, &modem_state, NULL); if (modem_state < MM_MODEM_STATE_ENABLED) { g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Cannot get location: " "device not yet enabled"); handle_get_location_context_free (ctx); return; } location_ctx = get_location_context (ctx->self); mm_gdbus_modem_location_complete_get_location ( ctx->skeleton, ctx->invocation, build_location_dictionary (NULL, location_ctx->location_3gpp, location_ctx->location_gps_nmea, location_ctx->location_gps_raw, location_ctx->location_cdma_bs)); handle_get_location_context_free (ctx); } static gboolean handle_get_location (MmGdbusModemLocation *skeleton, GDBusMethodInvocation *invocation, MMIfaceModemLocation *self) { HandleGetLocationContext *ctx; ctx = g_new (HandleGetLocationContext, 1); ctx->skeleton = g_object_ref (skeleton); ctx->invocation = g_object_ref (invocation); ctx->self = g_object_ref (self); mm_base_modem_authorize (MM_BASE_MODEM (self), invocation, MM_AUTHORIZATION_LOCATION, (GAsyncReadyCallback)handle_get_location_auth_ready, ctx); return TRUE; } /*****************************************************************************/ typedef struct _DisablingContext DisablingContext; static void interface_disabling_step (GTask *task); typedef enum { DISABLING_STEP_FIRST, DISABLING_STEP_DISABLE_GATHERING, DISABLING_STEP_LAST } DisablingStep; struct _DisablingContext { DisablingStep step; MmGdbusModemLocation *skeleton; }; static void disabling_context_free (DisablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_location_disable_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void disabling_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { DisablingContext *ctx; GError *error = NULL; if (!setup_gathering_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_disabling_step (task); } static void interface_disabling_step (GTask *task) { MMIfaceModemLocation *self; DisablingContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case DISABLING_STEP_FIRST: ctx->step++; /* fall through */ case DISABLING_STEP_DISABLE_GATHERING: setup_gathering (self, MM_MODEM_LOCATION_SOURCE_NONE, (GAsyncReadyCallback)disabling_location_gathering_ready, task); return; case DISABLING_STEP_LAST: /* We are done without errors! */ clear_location_context (self); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_location_disable (MMIfaceModemLocation *self, GAsyncReadyCallback callback, gpointer user_data) { DisablingContext *ctx; GTask *task; ctx = g_new0 (DisablingContext, 1); ctx->step = DISABLING_STEP_FIRST; task = g_task_new (self, NULL, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_disabling_step (task); } /*****************************************************************************/ typedef struct _EnablingContext EnablingContext; static void interface_enabling_step (GTask *task); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_ENABLE_GATHERING, ENABLING_STEP_LAST } EnablingStep; struct _EnablingContext { EnablingStep step; MmGdbusModemLocation *skeleton; }; static void enabling_context_free (EnablingContext *ctx) { if (ctx->skeleton) g_object_unref (ctx->skeleton); g_free (ctx); } gboolean mm_iface_modem_location_enable_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void enabling_location_gathering_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { EnablingContext *ctx; GError *error = NULL; if (!setup_gathering_finish (self, res, &error)) { g_task_return_error (task, error); g_object_unref (task); return; } /* Go on to next step */ ctx = g_task_get_task_data (task); ctx->step++; interface_enabling_step (task); } static void interface_enabling_step (GTask *task) { MMIfaceModemLocation *self; EnablingContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case ENABLING_STEP_FIRST: ctx->step++; /* fall through */ case ENABLING_STEP_ENABLE_GATHERING: { MMModemLocationSource default_sources; /* By default, we'll enable all NON-GPS sources */ default_sources = mm_gdbus_modem_location_get_capabilities (ctx->skeleton); default_sources &= ~(MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED | MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB); setup_gathering (self, default_sources, (GAsyncReadyCallback)enabling_location_gathering_ready, task); return; } case ENABLING_STEP_LAST: /* We are done without errors! */ g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } void mm_iface_modem_location_enable (MMIfaceModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EnablingContext *ctx; GTask *task; ctx = g_new0 (EnablingContext, 1); ctx->step = ENABLING_STEP_FIRST; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &ctx->skeleton, NULL); if (!ctx->skeleton) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get interface skeleton"); g_object_unref (task); return; } interface_enabling_step (task); } /*****************************************************************************/ typedef struct _InitializationContext InitializationContext; static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CAPABILITIES, INITIALIZATION_STEP_VALIDATE_CAPABILITIES, INITIALIZATION_STEP_SUPL_SERVER, INITIALIZATION_STEP_SUPPORTED_ASSISTANCE_DATA, INITIALIZATION_STEP_ASSISTANCE_DATA_SERVERS, INITIALIZATION_STEP_GPS_REFRESH_RATE, INITIALIZATION_STEP_LAST } InitializationStep; struct _InitializationContext { MmGdbusModemLocation *skeleton; InitializationStep step; MMModemLocationSource capabilities; }; static void initialization_context_free (InitializationContext *ctx) { g_object_unref (ctx->skeleton); g_free (ctx); } static void load_assistance_data_servers_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; InitializationContext *ctx; gchar **servers; ctx = g_task_get_task_data (task); servers = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't load assistance data servers: %s", error->message); g_error_free (error); } mm_gdbus_modem_location_set_assistance_data_servers (ctx->skeleton, (const gchar *const *)servers); g_strfreev (servers); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_supported_assistance_data_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; MMModemLocationAssistanceDataType mask; InitializationContext *ctx; ctx = g_task_get_task_data (task); mask = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't load supported assistance data types: %s", error->message); g_error_free (error); } mm_gdbus_modem_location_set_supported_assistance_data (ctx->skeleton, (guint32) mask); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_supl_server_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; gchar *supl; InitializationContext *ctx; ctx = g_task_get_task_data (task); supl = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't load SUPL server: %s", error->message); g_error_free (error); } mm_gdbus_modem_location_set_supl_server (ctx->skeleton, supl ? supl : ""); g_free (supl); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void load_capabilities_ready (MMIfaceModemLocation *self, GAsyncResult *res, GTask *task) { GError *error = NULL; InitializationContext *ctx; ctx = g_task_get_task_data (task); ctx->capabilities = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities_finish (self, res, &error); if (error) { mm_obj_warn (self, "couldn't load location capabilities: %s", error->message); g_error_free (error); } mm_gdbus_modem_location_set_capabilities (ctx->skeleton, ctx->capabilities); /* Go on to next step */ ctx->step++; interface_initialization_step (task); } static void interface_initialization_step (GTask *task) { MMIfaceModemLocation *self; InitializationContext *ctx; /* Don't run new steps if we're cancelled */ if (g_task_return_error_if_cancelled (task)) { g_object_unref (task); return; } self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); switch (ctx->step) { case INITIALIZATION_STEP_FIRST: ctx->step++; /* fall through */ case INITIALIZATION_STEP_CAPABILITIES: /* Location capabilities value is meant to be loaded only once during * the whole lifetime of the modem. Therefore, if we already have it * loaded, don't try to load it again. */ if (!mm_gdbus_modem_location_get_capabilities (ctx->skeleton) && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities ( self, (GAsyncReadyCallback)load_capabilities_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_VALIDATE_CAPABILITIES: /* If the modem doesn't support any location capabilities, we won't export * the interface. We just report an UNSUPPORTED error. */ if (ctx->capabilities == MM_MODEM_LOCATION_SOURCE_NONE) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "The modem doesn't have location capabilities"); g_object_unref (task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_SUPL_SERVER: /* If the modem supports A-GPS, load SUPL server */ if ((ctx->capabilities & (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB)) && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server ( self, (GAsyncReadyCallback)load_supl_server_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_SUPPORTED_ASSISTANCE_DATA: /* If the modem supports any GPS-related technology, check assistance data types supported */ if ((ctx->capabilities & (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB | MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data ( self, (GAsyncReadyCallback)load_supported_assistance_data_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_ASSISTANCE_DATA_SERVERS: /* If any assistance data supported, load servers */ if ((mm_gdbus_modem_location_get_supported_assistance_data (ctx->skeleton) != MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE) && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers && MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers_finish) { MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers ( self, (GAsyncReadyCallback)load_assistance_data_servers_ready, task); return; } ctx->step++; /* fall through */ case INITIALIZATION_STEP_GPS_REFRESH_RATE: /* If we have GPS capabilities, expose the GPS refresh rate */ if (ctx->capabilities & ((MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA))) /* Set the default rate in the interface */ mm_gdbus_modem_location_set_gps_refresh_rate (ctx->skeleton, MM_LOCATION_GPS_REFRESH_TIME_SECS); ctx->step++; /* fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ /* Handle method invocations */ g_signal_connect (ctx->skeleton, "handle-setup", G_CALLBACK (handle_setup), self); g_signal_connect (ctx->skeleton, "handle-set-supl-server", G_CALLBACK (handle_set_supl_server), self); g_signal_connect (ctx->skeleton, "handle-inject-assistance-data", G_CALLBACK (handle_inject_assistance_data), self); g_signal_connect (ctx->skeleton, "handle-set-gps-refresh-rate", G_CALLBACK (handle_set_gps_refresh_rate), self); g_signal_connect (ctx->skeleton, "handle-get-location", G_CALLBACK (handle_get_location), self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_location (MM_GDBUS_OBJECT_SKELETON (self), MM_GDBUS_MODEM_LOCATION (ctx->skeleton)); g_task_return_boolean (task, TRUE); g_object_unref (task); return; default: break; } g_assert_not_reached (); } gboolean mm_iface_modem_location_initialize_finish (MMIfaceModemLocation *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } void mm_iface_modem_location_initialize (MMIfaceModemLocation *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InitializationContext *ctx; MmGdbusModemLocation *skeleton = NULL; GTask *task; /* Did we already create it? */ g_object_get (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton, NULL); if (!skeleton) { skeleton = mm_gdbus_modem_location_skeleton_new (); /* Set all initial property defaults */ mm_gdbus_modem_location_set_capabilities (skeleton, MM_MODEM_LOCATION_SOURCE_NONE); mm_gdbus_modem_location_set_supported_assistance_data (skeleton, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE); mm_gdbus_modem_location_set_enabled (skeleton, MM_MODEM_LOCATION_SOURCE_NONE); mm_gdbus_modem_location_set_signals_location (skeleton, FALSE); mm_gdbus_modem_location_set_location (skeleton, build_location_dictionary (NULL, NULL, NULL, NULL, NULL)); g_object_set (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, skeleton, NULL); } /* Perform async initialization here */ ctx = g_new0 (InitializationContext, 1); ctx->capabilities = MM_MODEM_LOCATION_SOURCE_NONE; ctx->step = INITIALIZATION_STEP_FIRST; ctx->skeleton = skeleton; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); interface_initialization_step (task); } void mm_iface_modem_location_shutdown (MMIfaceModemLocation *self) { /* Unexport DBus interface and remove the skeleton */ mm_gdbus_object_skeleton_set_modem_location (MM_GDBUS_OBJECT_SKELETON (self), NULL); g_object_set (self, MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, NULL, NULL); } /*****************************************************************************/ static void iface_modem_location_init (gpointer g_iface) { static gboolean initialized = FALSE; if (initialized) return; /* Properties */ g_object_interface_install_property (g_iface, g_param_spec_object (MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, "Location DBus skeleton", "DBus skeleton for the Location interface", MM_GDBUS_TYPE_MODEM_LOCATION_SKELETON, G_PARAM_READWRITE)); g_object_interface_install_property (g_iface, g_param_spec_boolean (MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS, "Allow unmanaged GPS always", "Whether to always allow GPS unmanaged, even when raw/nmea GPS sources are enabled", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); initialized = TRUE; } GType mm_iface_modem_location_get_type (void) { static GType iface_modem_location_type = 0; if (!G_UNLIKELY (iface_modem_location_type)) { static const GTypeInfo info = { sizeof (MMIfaceModemLocation), /* class_size */ iface_modem_location_init, /* base_init */ NULL, /* base_finalize */ }; iface_modem_location_type = g_type_register_static (G_TYPE_INTERFACE, "MMIfaceModemLocation", &info, 0); g_type_interface_add_prerequisite (iface_modem_location_type, MM_TYPE_IFACE_MODEM); } return iface_modem_location_type; }