aboutsummaryrefslogtreecommitdiff
path: root/libmm-glib/mm-modem-location.c
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2021-06-24 10:14:17 +0200
committerAleksander Morgado <aleksander@aleksander.es>2021-07-01 01:10:39 +0200
commitf226b6330115c39c3b209f84452729a0e412da24 (patch)
tree6a6e96884301ac3fc399c068a10226c56aea9c79 /libmm-glib/mm-modem-location.c
parent4e4cb6d66acaec56960948ad3adfe7df359d2807 (diff)
libmm-glib,location: new signaled location API support
We allow clients to receive asynchronous updates of location information, e.g. if "location signaling" is explicitly enabled (with the setup() method). But if so, we should also allow clients to easily process those asynchronous updates in the libmm-glib library, instead of requiring them to run explicit DBus queries to refresh the location information. These new signaled location APIs allow clients to do so; they can enable location signaling, and then just wait for the updates to arrive.
Diffstat (limited to 'libmm-glib/mm-modem-location.c')
-rw-r--r--libmm-glib/mm-modem-location.c387
1 files changed, 387 insertions, 0 deletions
diff --git a/libmm-glib/mm-modem-location.c b/libmm-glib/mm-modem-location.c
index dc5e2f72..06aafdde 100644
--- a/libmm-glib/mm-modem-location.c
+++ b/libmm-glib/mm-modem-location.c
@@ -40,6 +40,15 @@
G_DEFINE_TYPE (MMModemLocation, mm_modem_location, MM_GDBUS_TYPE_MODEM_LOCATION_PROXY)
+struct _MMModemLocationPrivate {
+ GMutex signaled_location_mutex;
+ guint signaled_location_id;
+ MMLocation3gpp *signaled_location_3gpp;
+ MMLocationGpsNmea *signaled_location_gps_nmea;
+ MMLocationGpsRaw *signaled_location_gps_raw;
+ MMLocationCdmaBs *signaled_location_cdma_bs;
+};
+
/*****************************************************************************/
/**
@@ -1207,11 +1216,389 @@ mm_modem_location_get_gps_refresh_rate (MMModemLocation *self)
/*****************************************************************************/
static void
+signaled_location_updated (MMModemLocation *self,
+ GParamSpec *pspec)
+{
+ g_mutex_lock (&self->priv->signaled_location_mutex);
+ {
+ GVariant *dictionary;
+
+ g_clear_object (&self->priv->signaled_location_3gpp);
+ g_clear_object (&self->priv->signaled_location_gps_nmea);
+ g_clear_object (&self->priv->signaled_location_gps_raw);
+ g_clear_object (&self->priv->signaled_location_cdma_bs);
+
+ dictionary = mm_gdbus_modem_location_get_location (MM_GDBUS_MODEM_LOCATION (self));
+ if (dictionary) {
+ g_autoptr(GError) error = NULL;
+
+ if (!build_locations (dictionary,
+ &self->priv->signaled_location_3gpp,
+ &self->priv->signaled_location_gps_nmea,
+ &self->priv->signaled_location_gps_raw,
+ &self->priv->signaled_location_cdma_bs,
+ &error))
+ g_warning ("Invalid signaled location received: %s", error->message);
+ }
+ }
+ g_mutex_unlock (&self->priv->signaled_location_mutex);
+}
+
+static void
+ensure_internal_signaled_location (MMModemLocation *self,
+ MMLocation3gpp **dupl_location_3gpp,
+ MMLocationGpsNmea **dupl_location_gps_nmea,
+ MMLocationGpsRaw **dupl_location_gps_raw,
+ MMLocationCdmaBs **dupl_location_cdma_bs)
+{
+ g_mutex_lock (&self->priv->signaled_location_mutex);
+ {
+ /* If this is the first time ever asking for the object, setup the
+ * update listener and the initial object, if any. */
+ if (!self->priv->signaled_location_id) {
+ g_autoptr(GVariant) dictionary = NULL;
+
+ dictionary = mm_gdbus_modem_location_dup_location (MM_GDBUS_MODEM_LOCATION (self));
+ if (dictionary) {
+ g_autoptr(GError) error = NULL;
+
+ if (!build_locations (dictionary,
+ &self->priv->signaled_location_3gpp,
+ &self->priv->signaled_location_gps_nmea,
+ &self->priv->signaled_location_gps_raw,
+ &self->priv->signaled_location_cdma_bs,
+ &error))
+ g_warning ("Invalid initial signaled location: %s", error->message);
+ }
+
+ /* No need to clear this signal connection when freeing self */
+ self->priv->signaled_location_id =
+ g_signal_connect (self,
+ "notify::location",
+ G_CALLBACK (signaled_location_updated),
+ NULL);
+ }
+
+ if (dupl_location_3gpp && self->priv->signaled_location_3gpp)
+ *dupl_location_3gpp = g_object_ref (self->priv->signaled_location_3gpp);
+ if (dupl_location_gps_nmea && self->priv->signaled_location_gps_nmea)
+ *dupl_location_gps_nmea = g_object_ref (self->priv->signaled_location_gps_nmea);
+ if (dupl_location_gps_raw && self->priv->signaled_location_gps_raw)
+ *dupl_location_gps_raw = g_object_ref (self->priv->signaled_location_gps_raw);
+ if (dupl_location_cdma_bs && self->priv->signaled_location_cdma_bs)
+ *dupl_location_cdma_bs = g_object_ref (self->priv->signaled_location_cdma_bs);
+ }
+ g_mutex_unlock (&self->priv->signaled_location_mutex);
+}
+
+/**
+ * mm_modem_location_peek_signaled_3gpp:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocation3gpp object with the current 3GPP location information.
+ *
+ * Unlike mm_modem_location_get_3gpp() or mm_modem_location_get_3gpp_sync(),
+ * this method does not perform an explicit query. Instead, this method will
+ * return the location information that may have been signaled by the modem.
+ * Therefore, this method will only succeed if location signaling is enabled
+ * (e.g. with mm_modem_location_setup() in the #MMModemLocation).
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_location_get_signaled_3gpp() if on
+ * another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMLocation3gpp, or %NULL if none available. Do
+ * not free the returned value, it belongs to @self.
+ *
+ * Since: 1.18
+ */
+MMLocation3gpp *
+mm_modem_location_peek_signaled_3gpp (MMModemLocation *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL);
+
+ ensure_internal_signaled_location (self, NULL, NULL, NULL, NULL);
+ return self->priv->signaled_location_3gpp;
+}
+
+/**
+ * mm_modem_location_get_signaled_3gpp:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocation3gpp object with the current 3GPP location information.
+ *
+ * Unlike mm_modem_location_get_3gpp() or mm_modem_location_get_3gpp_sync(),
+ * this method does not perform an explicit query. Instead, this method will
+ * return the location information that may have been signaled by the modem.
+ * Therefore, this method will only succeed if location signaling is enabled
+ * (e.g. with mm_modem_location_setup() in the #MMModemLocation).
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_modem_location_get_signaled_3gpp() again to get a new #MMLocation3gpp
+ * with the new values.</warning>
+ *
+ * Returns: (transfer full): A #MMLocation3gpp that must be freed with
+ * g_object_unref() or %NULL if none available.
+ *
+ * Since: 1.18
+ */
+MMLocation3gpp *
+mm_modem_location_get_signaled_3gpp (MMModemLocation *self)
+{
+ MMLocation3gpp *location_3gpp = NULL;
+
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL);
+
+ ensure_internal_signaled_location (self, &location_3gpp, NULL, NULL, NULL);
+ return location_3gpp;
+}
+
+/**
+ * mm_modem_location_peek_signaled_gps_nmea:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationGpsNmea object with the current GPS NMEA location
+ * information.
+ *
+ * Unlike mm_modem_location_get_gps_nmea() or
+ * mm_modem_location_get_gps_nmea_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_location_get_signaled_gps_nmea() if on
+ * another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMLocationGpsNmea, or %NULL if none available. Do
+ * not free the returned value, it belongs to @self.
+ *
+ * Since: 1.18
+ */
+MMLocationGpsNmea *
+mm_modem_location_peek_signaled_gps_nmea (MMModemLocation *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL);
+
+ ensure_internal_signaled_location (self, NULL, NULL, NULL, NULL);
+ return self->priv->signaled_location_gps_nmea;
+}
+
+/**
+ * mm_modem_location_get_signaled_gps_nmea:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationGpsNmea object with the current GPS NMEA location
+ * information.
+ *
+ * Unlike mm_modem_location_get_gps_nmea() or
+ * mm_modem_location_get_gps_nmea_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_modem_location_get_signaled_gps_nmea() again to get a new #MMLocationGpsNmea
+ * with the new values.</warning>
+ *
+ * Returns: (transfer full): A #MMLocationGpsNmea that must be freed with
+ * g_object_unref() or %NULL if none available.
+ *
+ * Since: 1.18
+ */
+MMLocationGpsNmea *
+mm_modem_location_get_signaled_gps_nmea (MMModemLocation *self)
+{
+ MMLocationGpsNmea *location_gps_nmea = NULL;
+
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL);
+
+ ensure_internal_signaled_location (self, NULL, &location_gps_nmea, NULL, NULL);
+ return location_gps_nmea;
+}
+
+/**
+ * mm_modem_location_peek_signaled_gps_raw:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationGpsRaw object with the current GPS raw location
+ * information.
+ *
+ * Unlike mm_modem_location_get_gps_raw() or
+ * mm_modem_location_get_gps_raw_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_location_get_signaled_gps_raw() if on
+ * another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMLocationGpsRaw, or %NULL if none available. Do
+ * not free the returned value, it belongs to @self.
+ *
+ * Since: 1.18
+ */
+MMLocationGpsRaw *
+mm_modem_location_peek_signaled_gps_raw (MMModemLocation *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL);
+
+ ensure_internal_signaled_location (self, NULL, NULL, NULL, NULL);
+ return self->priv->signaled_location_gps_raw;
+}
+
+/**
+ * mm_modem_location_get_signaled_gps_raw:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationGpsRaw object with the current GPS raw location
+ * information.
+ *
+ * Unlike mm_modem_location_get_gps_raw() or
+ * mm_modem_location_get_gps_raw_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_modem_location_get_signaled_gps_raw() again to get a new #MMLocationGpsRaw
+ * with the new values.</warning>
+ *
+ * Returns: (transfer full): A #MMLocationGpsRaw that must be freed with
+ * g_object_unref() or %NULL if none available.
+ *
+ * Since: 1.18
+ */
+MMLocationGpsRaw *
+mm_modem_location_get_signaled_gps_raw (MMModemLocation *self)
+{
+ MMLocationGpsRaw *location_gps_raw = NULL;
+
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL);
+
+ ensure_internal_signaled_location (self, NULL, NULL, &location_gps_raw, NULL);
+ return location_gps_raw;
+}
+
+/**
+ * mm_modem_location_peek_signaled_cdma_bs:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationCdmaBs object with the current CDMA base station location
+ * information.
+ *
+ * Unlike mm_modem_location_get_cdma_bs() or
+ * mm_modem_location_get_cdma_bs_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_location_get_signaled_cdma_bs() if on
+ * another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMLocationCdmaBs, or %NULL if none available. Do
+ * not free the returned value, it belongs to @self.
+ *
+ * Since: 1.18
+ */
+MMLocationCdmaBs *
+mm_modem_location_peek_signaled_cdma_bs (MMModemLocation *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL);
+
+ ensure_internal_signaled_location (self, NULL, NULL, NULL, NULL);
+ return self->priv->signaled_location_cdma_bs;
+}
+
+/**
+ * mm_modem_location_get_signaled_cdma_bs:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationCdmaBs object with the current CDMA base station location
+ * information.
+ *
+ * Unlike mm_modem_location_get_cdma_bs() or
+ * mm_modem_location_get_cdma_bs_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_modem_location_get_signaled_cdma_bs() again to get a new #MMLocationCdmaBs
+ * with the new values.</warning>
+ *
+ * Returns: (transfer full): A #MMLocationCdmaBs that must be freed with
+ * g_object_unref() or %NULL if none available.
+ *
+ * Since: 1.18
+ */
+MMLocationCdmaBs *
+mm_modem_location_get_signaled_cdma_bs (MMModemLocation *self)
+{
+ MMLocationCdmaBs *location_cdma_bs = NULL;
+
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL);
+
+ ensure_internal_signaled_location (self, NULL, NULL, NULL, &location_cdma_bs);
+ return location_cdma_bs;
+}
+
+/*****************************************************************************/
+
+static void
mm_modem_location_init (MMModemLocation *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_LOCATION, MMModemLocationPrivate);
+ g_mutex_init (&self->priv->signaled_location_mutex);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMModemLocation *self = MM_MODEM_LOCATION (object);
+
+ g_mutex_clear (&self->priv->signaled_location_mutex);
+
+ G_OBJECT_CLASS (mm_modem_location_parent_class)->finalize (object);
+}
+
+static void
+dispose (GObject *object)
+{
+ MMModemLocation *self = MM_MODEM_LOCATION (object);
+
+ g_clear_object (&self->priv->signaled_location_3gpp);
+ g_clear_object (&self->priv->signaled_location_gps_nmea);
+ g_clear_object (&self->priv->signaled_location_gps_raw);
+ g_clear_object (&self->priv->signaled_location_cdma_bs);
+
+ G_OBJECT_CLASS (mm_modem_location_parent_class)->dispose (object);
}
static void
mm_modem_location_class_init (MMModemLocationClass *modem_class)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (modem_class);
+
+ g_type_class_add_private (object_class, sizeof (MMModemLocationPrivate));
+
+ /* Virtual methods */
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
}