Files
ModemManager/src/mm-iface-modem-location.c
T
Maciej S. Szmigiero c56e1f4739 iface-modem-location: fix zeroing capabilities on reinitialization
The modem location interface loads modem location capabilities only during
the very first initialization of this interface.

On each later reinitialization it skips loading these capabilities from
modem, however it still goes through the whole interface initialization
state machine.

This state machine uses its own "ctx->capabilities" field for temporary
capabilities storage, which is set to MM_MODEM_LOCATION_SOURCE_NONE at the
start of the initialization process.

However, if we aren't actually loading modem location capabilities because
we're only reinitializing the interface we still need to set this
"ctx->capabilities" field to the existing (previously loaded) modem
location capabilities.

Otherwise the next interface initialization step (validation) will
permanently disable the whole location interface.
2024-04-01 18:40:15 +02:00

2006 lines
72 KiB
C

/* -*- 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 <aleksander@aleksander.es>
*/
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-iface-modem.h"
#include "mm-iface-modem-location.h"
#include "mm-log-object.h"
#include "mm-error-helpers.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 : "<none>",
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_slice_free (HandleSetupContext, ctx);
}
static void
setup_gathering_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
HandleSetupContext *ctx)
{
GError *error = NULL;
if (!setup_gathering_finish (self, res, &error))
mm_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;
MMModemLocationSource require_enabled;
LocationContext *location_ctx;
g_autofree gchar *str = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
mm_dbus_method_invocation_take_error (ctx->invocation, error);
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);
mm_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);
return;
}
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
/* Location sources that require any kind of network access may be
* enabled only when the modem is enabled. Generic standalone GPS may be
* enabled even without a SIM card */
require_enabled = ctx->sources & (MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI |
MM_MODEM_LOCATION_SOURCE_CDMA_BS |
MM_MODEM_LOCATION_SOURCE_AGPS_MSA |
MM_MODEM_LOCATION_SOURCE_AGPS_MSB);
if (require_enabled && (modem_state < MM_MODEM_STATE_ENABLED)) {
str = mm_modem_location_source_build_string_from_mask (require_enabled);
mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
"Cannot enable location '%s': device not yet enabled", str);
handle_setup_context_free (ctx);
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_info (self, "processing user request to setup location '%s'...", 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_slice_new0 (HandleSetupContext);
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))
mm_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;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
mm_dbus_method_invocation_take_error (ctx->invocation, error);
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))) {
mm_dbus_method_invocation_return_error_literal (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)) {
mm_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) {
mm_dbus_method_invocation_return_error_literal (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_obj_info (self, "processing user request to set 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_new0 (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))
mm_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)) {
mm_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) {
mm_dbus_method_invocation_return_error_literal (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) {
mm_dbus_method_invocation_return_error_literal (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_obj_info (self, "processing user 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_new0 (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;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
mm_dbus_method_invocation_take_error (ctx->invocation, error);
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)))) {
mm_dbus_method_invocation_return_error_literal (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_obj_info (self, "processing user request to set GPS refresh rate...");
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_new0 (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_slice_free (HandleGetLocationContext, ctx);
}
static void
handle_get_location_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleGetLocationContext *ctx)
{
LocationContext *location_ctx;
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
mm_dbus_method_invocation_take_error (ctx->invocation, error);
handle_get_location_context_free (ctx);
return;
}
mm_obj_info (self, "processing user request to get location...");
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_slice_new0 (HandleGetLocationContext);
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:
/* We disable all sources here. It is true that the user may have enabled GPS
* early before getting the modem enabled, and that we may have left it active
* once the modem transitions to enabled state, but for the disabling phase
* we reset everything. */
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;
MMModemLocationSource currently_enabled;
/* 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);
/* If standalone GPS was already enabled, we keep it enabled */
currently_enabled = mm_gdbus_modem_location_get_enabled (ctx->skeleton);
setup_gathering (self,
default_sources | currently_enabled,
(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;
MMModemLocationSource existing_capabilities;
/* 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. */
existing_capabilities = mm_gdbus_modem_location_get_capabilities (ctx->skeleton);
if (existing_capabilities != MM_MODEM_LOCATION_SOURCE_NONE) {
ctx->capabilities = existing_capabilities;
} else if (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;
}