Merge request !1418 from 'feat/inject-time-position'
shared-qmi: handle inject time and inject position requests from the modem https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1418
This commit is contained in:
+13
-11
@@ -10,7 +10,7 @@ project(
|
|||||||
'c_std=gnu89',
|
'c_std=gnu89',
|
||||||
'warning_level=2',
|
'warning_level=2',
|
||||||
],
|
],
|
||||||
meson_version: '>= 0.53.0',
|
meson_version: '>= 0.60.0',
|
||||||
)
|
)
|
||||||
|
|
||||||
meson_version = meson.version()
|
meson_version = meson.version()
|
||||||
@@ -31,11 +31,13 @@ mm_includedir = get_option('includedir')
|
|||||||
mm_libdir = get_option('libdir')
|
mm_libdir = get_option('libdir')
|
||||||
mm_sbindir = get_option('sbindir')
|
mm_sbindir = get_option('sbindir')
|
||||||
mm_sysconfdir = get_option('sysconfdir')
|
mm_sysconfdir = get_option('sysconfdir')
|
||||||
|
mm_sharedstatedir = get_option('sharedstatedir')
|
||||||
|
|
||||||
mm_pkgdatadir = mm_datadir / mm_name
|
mm_pkgdatadir = mm_datadir / mm_name
|
||||||
mm_pkgincludedir = mm_includedir / mm_name
|
mm_pkgincludedir = mm_includedir / mm_name
|
||||||
mm_pkglibdir = mm_libdir / mm_name
|
mm_pkglibdir = mm_libdir / mm_name
|
||||||
mm_pkgsysconfdir = mm_sysconfdir / mm_name
|
mm_pkgsysconfdir = mm_sysconfdir / mm_name
|
||||||
|
mm_pkgsharedstatedir = mm_sharedstatedir / mm_name
|
||||||
|
|
||||||
mm_glib_name = 'libmm-glib'
|
mm_glib_name = 'libmm-glib'
|
||||||
mm_glib_pkgincludedir = mm_includedir / mm_glib_name
|
mm_glib_pkgincludedir = mm_includedir / mm_glib_name
|
||||||
@@ -168,21 +170,21 @@ glib_deps = declare_dependency(
|
|||||||
|
|
||||||
# DBus system directory
|
# DBus system directory
|
||||||
dbus_dep = dependency('dbus-1')
|
dbus_dep = dependency('dbus-1')
|
||||||
dbus_interfaces_dir = dbus_dep.get_pkgconfig_variable('interfaces_dir', define_variable: ['datadir', mm_datadir])
|
dbus_interfaces_dir = dbus_dep.get_variable(pkgconfig: 'interfaces_dir', pkgconfig_define: ['datadir', mm_datadir])
|
||||||
dbus_system_bus_services_dir = dbus_dep.get_pkgconfig_variable('system_bus_services_dir', define_variable: ['datadir', mm_datadir])
|
dbus_system_bus_services_dir = dbus_dep.get_variable(pkgconfig: 'system_bus_services_dir', pkgconfig_define: ['datadir', mm_datadir])
|
||||||
|
|
||||||
dbus_policy_dir = get_option('dbus_policy_dir')
|
dbus_policy_dir = get_option('dbus_policy_dir')
|
||||||
if dbus_policy_dir == ''
|
if dbus_policy_dir == ''
|
||||||
dbus_policy_dir = dbus_dep.get_pkgconfig_variable('sysconfdir', define_variable: ['sysconfdir', mm_sysconfdir]) / 'dbus-1/system.d'
|
dbus_policy_dir = dbus_dep.get_variable(pkgconfig: 'sysconfdir', pkgconfig_define: ['sysconfdir', mm_sysconfdir]) / 'dbus-1/system.d'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
enable_bash_completion = get_option('bash_completion')
|
enable_bash_completion = get_option('bash_completion')
|
||||||
if enable_bash_completion
|
if enable_bash_completion
|
||||||
bash_completion_dep = dependency('bash-completion')
|
bash_completion_dep = dependency('bash-completion')
|
||||||
bash_completion_completionsdir = bash_completion_dep.get_pkgconfig_variable(
|
bash_completion_completionsdir = bash_completion_dep.get_variable(
|
||||||
'completionsdir',
|
pkgconfig: 'completionsdir',
|
||||||
# bash-completion 2.10 changed the substitutions
|
# bash-completion 2.10 changed the substitutions
|
||||||
define_variable: bash_completion_dep.version().version_compare('>= 2.10') ? ['datadir', mm_datadir] : ['prefix', mm_prefix],
|
pkgconfig_define: bash_completion_dep.version().version_compare('>= 2.10') ? ['datadir', mm_datadir] : ['prefix', mm_prefix],
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -197,7 +199,7 @@ config_h.set('WITH_UDEV', enable_udev)
|
|||||||
udev_udevdir = get_option('udevdir')
|
udev_udevdir = get_option('udevdir')
|
||||||
if udev_udevdir == ''
|
if udev_udevdir == ''
|
||||||
assert(enable_udev, 'udevdir must be explicitly given if udev support is disabled')
|
assert(enable_udev, 'udevdir must be explicitly given if udev support is disabled')
|
||||||
udev_udevdir = dependency('udev').get_pkgconfig_variable('udevdir')
|
udev_udevdir = dependency('udev').get_variable(pkgconfig: 'udevdir')
|
||||||
endif
|
endif
|
||||||
udev_rulesdir = udev_udevdir / 'rules.d'
|
udev_rulesdir = udev_udevdir / 'rules.d'
|
||||||
|
|
||||||
@@ -207,7 +209,7 @@ install_systemdunitdir = (systemd_systemdsystemunitdir != 'no')
|
|||||||
|
|
||||||
if install_systemdunitdir and systemd_systemdsystemunitdir == ''
|
if install_systemdunitdir and systemd_systemdsystemunitdir == ''
|
||||||
systemd_dep = dependency('systemd', not_found_message: 'systemd required but not found, please provide a valid systemd user unit dir or disable it')
|
systemd_dep = dependency('systemd', not_found_message: 'systemd required but not found, please provide a valid systemd user unit dir or disable it')
|
||||||
systemd_systemdsystemunitdir = systemd_dep.get_pkgconfig_variable('systemdsystemunitdir', define_variable: ['root_prefix', mm_prefix])
|
systemd_systemdsystemunitdir = systemd_dep.get_variable(pkgconfig: 'systemdsystemunitdir', pkgconfig_define: ['root_prefix', mm_prefix])
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Suspend/resume support
|
# Suspend/resume support
|
||||||
@@ -240,7 +242,7 @@ enable_polkit = (polkit_opt != 'no')
|
|||||||
if enable_polkit
|
if enable_polkit
|
||||||
polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.97', not_found_message: 'PolicyKit development headers are required')
|
polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.97', not_found_message: 'PolicyKit development headers are required')
|
||||||
|
|
||||||
polkit_gobject_policydir = polkit_gobject_dep.get_pkgconfig_variable('policydir', define_variable: ['prefix', mm_prefix])
|
polkit_gobject_policydir = polkit_gobject_dep.get_variable(pkgconfig: 'policydir', pkgconfig_define: ['prefix', mm_prefix])
|
||||||
|
|
||||||
policy_conf = {'MM_DEFAULT_USER_POLICY': (polkit_opt == 'permissive' ? 'yes' : 'auth_self_keep')}
|
policy_conf = {'MM_DEFAULT_USER_POLICY': (polkit_opt == 'permissive' ? 'yes' : 'auth_self_keep')}
|
||||||
endif
|
endif
|
||||||
@@ -297,7 +299,7 @@ if enable_qrtr
|
|||||||
if qmi_glib_dep.type_name() == 'internal'
|
if qmi_glib_dep.type_name() == 'internal'
|
||||||
qmi_qrtr_supported = qmi_glib_proj.get_variable('enable_qrtr')
|
qmi_qrtr_supported = qmi_glib_proj.get_variable('enable_qrtr')
|
||||||
else
|
else
|
||||||
qmi_qrtr_supported = qmi_glib_dep.get_pkgconfig_variable('qmi_qrtr_supported')
|
qmi_qrtr_supported = qmi_glib_dep.get_variable(pkgconfig: 'qmi_qrtr_supported')
|
||||||
endif
|
endif
|
||||||
assert(qmi_qrtr_supported.to_int().is_odd(), 'Couldn\'t find QRTR support in qmi-glib.')
|
assert(qmi_qrtr_supported.to_int().is_odd(), 'Couldn\'t find QRTR support in qmi-glib.')
|
||||||
qrtr_glib_dep = dependency('qrtr-glib', version: '>= 1.0.0')
|
qrtr_glib_dep = dependency('qrtr-glib', version: '>= 1.0.0')
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ sources = files(
|
|||||||
'mm-cbm-part.c',
|
'mm-cbm-part.c',
|
||||||
'mm-charsets.c',
|
'mm-charsets.c',
|
||||||
'mm-error-helpers.c',
|
'mm-error-helpers.c',
|
||||||
|
'mm-location-cache.c',
|
||||||
'mm-log.c',
|
'mm-log.c',
|
||||||
'mm-log-object.c',
|
'mm-log-object.c',
|
||||||
'mm-modem-helpers.c',
|
'mm-modem-helpers.c',
|
||||||
@@ -80,11 +81,16 @@ if enable_systemd_journal
|
|||||||
private_deps += libsystemd_dep
|
private_deps += libsystemd_dep
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
c_args = [
|
||||||
|
'-DPKGSTATEDIR="@0@"'.format(mm_pkgsharedstatedir),
|
||||||
|
]
|
||||||
|
|
||||||
libhelpers = static_library(
|
libhelpers = static_library(
|
||||||
'helpers',
|
'helpers',
|
||||||
sources: sources + enums_sources,
|
sources: sources + enums_sources,
|
||||||
include_directories: incs,
|
include_directories: incs,
|
||||||
dependencies: deps + private_deps,
|
dependencies: deps + private_deps,
|
||||||
|
c_args: c_args,
|
||||||
)
|
)
|
||||||
|
|
||||||
libhelpers_dep = declare_dependency(
|
libhelpers_dep = declare_dependency(
|
||||||
@@ -506,6 +512,11 @@ install_data(
|
|||||||
install_dir: udev_rulesdir,
|
install_dir: udev_rulesdir,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# empty directory for cached position
|
||||||
|
install_emptydir(
|
||||||
|
mm_pkgsharedstatedir,
|
||||||
|
)
|
||||||
|
|
||||||
if enable_tests
|
if enable_tests
|
||||||
subdir('tests')
|
subdir('tests')
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
/* -*- 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) 2026 Andrey Skvortsov <andrej.skvortzov@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
|
||||||
|
#define _LIBMM_INSIDE_MM
|
||||||
|
#include <libmm-glib.h>
|
||||||
|
|
||||||
|
#include "mm-location-cache.h"
|
||||||
|
#include "mm-log-object.h"
|
||||||
|
|
||||||
|
#if !defined PKGSTATEDIR
|
||||||
|
# error PKGSTATEDIR is not defined
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LOCATION_STATE_FILE "location.ini"
|
||||||
|
#define LOCATION_LAST_POS_GROUP "last_position"
|
||||||
|
#define LOCATION_LAST_POS_LATITUDE_KEY "latitude"
|
||||||
|
#define LOCATION_LAST_POS_LONGITUDE_KEY "longitude"
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void log_object_iface_init (MMLogObjectInterface *iface);
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_EXTENDED (MMLocationCache, mm_location_cache, G_TYPE_OBJECT, 0,
|
||||||
|
G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
|
||||||
|
|
||||||
|
struct _MMLocationCachePrivate {
|
||||||
|
MMLocationGpsRaw *gps_raw;
|
||||||
|
gdouble last_latitude;
|
||||||
|
gdouble last_longitude;
|
||||||
|
gchar* filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
log_object_build_id (MMLogObject *_self)
|
||||||
|
{
|
||||||
|
return g_strdup ("location-cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_location_cache_load_from_file (MMLocationCache *self,
|
||||||
|
const gchar *file,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(GKeyFile) key_file = g_key_file_new ();
|
||||||
|
gdouble lat;
|
||||||
|
gdouble lon;
|
||||||
|
|
||||||
|
self->priv->last_latitude = MM_LOCATION_LATITUDE_UNKNOWN;
|
||||||
|
self->priv->last_longitude = MM_LOCATION_LONGITUDE_UNKNOWN;
|
||||||
|
|
||||||
|
if (!g_key_file_load_from_file (key_file, file, G_KEY_FILE_NONE, error)) {
|
||||||
|
g_prefix_error (error, "Error loading cached location from %s: ", file);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
lat = g_key_file_get_double (key_file,
|
||||||
|
LOCATION_LAST_POS_GROUP,
|
||||||
|
LOCATION_LAST_POS_LATITUDE_KEY,
|
||||||
|
error);
|
||||||
|
if (*error) {
|
||||||
|
g_prefix_error (error, "Error finding latitude in file: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
lon = g_key_file_get_double (key_file,
|
||||||
|
LOCATION_LAST_POS_GROUP,
|
||||||
|
LOCATION_LAST_POS_LONGITUDE_KEY,
|
||||||
|
error);
|
||||||
|
if (*error) {
|
||||||
|
g_prefix_error (error, "Error finding longitude in file: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->priv->last_latitude = lat;
|
||||||
|
self->priv->last_longitude = lon;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_location_cache_load (MMLocationCache *self,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return mm_location_cache_load_from_file (self, self->priv->filename, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_location_cache_save_to_file (MMLocationCache *self,
|
||||||
|
const gchar *file,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(GKeyFile) key_file = g_key_file_new ();
|
||||||
|
|
||||||
|
if ((self->priv->last_latitude == MM_LOCATION_LATITUDE_UNKNOWN) ||
|
||||||
|
(self->priv->last_longitude == MM_LOCATION_LONGITUDE_UNKNOWN)) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
key_file = g_key_file_new ();
|
||||||
|
g_key_file_set_double (key_file,
|
||||||
|
LOCATION_LAST_POS_GROUP,
|
||||||
|
LOCATION_LAST_POS_LATITUDE_KEY,
|
||||||
|
self->priv->last_latitude);
|
||||||
|
g_key_file_set_double (key_file,
|
||||||
|
LOCATION_LAST_POS_GROUP,
|
||||||
|
LOCATION_LAST_POS_LONGITUDE_KEY,
|
||||||
|
self->priv->last_longitude);
|
||||||
|
|
||||||
|
if (!g_key_file_save_to_file (key_file, file, error)) {
|
||||||
|
g_prefix_error (error, "Error saving cached location to %s", file);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_location_cache_save (MMLocationCache *self,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return mm_location_cache_save_to_file (self, self->priv->filename, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_location_cache_set_filename (MMLocationCache *self,
|
||||||
|
const gchar *file)
|
||||||
|
{
|
||||||
|
g_free (self->priv->filename);
|
||||||
|
self->priv->filename = g_strdup (file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_location_cache_update_from_lat_lon (MMLocationCache *self,
|
||||||
|
const double lat,
|
||||||
|
const double lon)
|
||||||
|
{
|
||||||
|
if (lat != MM_LOCATION_LATITUDE_UNKNOWN &&
|
||||||
|
lon != MM_LOCATION_LONGITUDE_UNKNOWN) {
|
||||||
|
self->priv->last_latitude = lat;
|
||||||
|
self->priv->last_longitude = lon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_location_cache_update_from_nmea (MMLocationCache *self,
|
||||||
|
const gchar *trace)
|
||||||
|
{
|
||||||
|
gdouble lat;
|
||||||
|
gdouble lon;
|
||||||
|
|
||||||
|
g_assert (self->priv->gps_raw != NULL);
|
||||||
|
if (mm_location_gps_raw_add_trace (self->priv->gps_raw, trace)) {
|
||||||
|
lat = mm_location_gps_raw_get_latitude (self->priv->gps_raw);
|
||||||
|
lon = mm_location_gps_raw_get_longitude (self->priv->gps_raw);
|
||||||
|
mm_location_cache_update_from_lat_lon (self, lat, lon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_location_cache_get_lat_lon (MMLocationCache *self,
|
||||||
|
double *lat,
|
||||||
|
double *lon)
|
||||||
|
{
|
||||||
|
*lat = self->priv->last_latitude;
|
||||||
|
*lon = self->priv->last_longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
MMLocationCache *
|
||||||
|
mm_location_cache_new (void)
|
||||||
|
{
|
||||||
|
return MM_LOCATION_CACHE (g_object_new (MM_TYPE_LOCATION_CACHE, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mm_location_cache_init (MMLocationCache *self)
|
||||||
|
{
|
||||||
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_LOCATION_CACHE, MMLocationCachePrivate);
|
||||||
|
|
||||||
|
self->priv->last_longitude = MM_LOCATION_LONGITUDE_UNKNOWN;
|
||||||
|
self->priv->last_latitude = MM_LOCATION_LATITUDE_UNKNOWN;
|
||||||
|
self->priv->gps_raw = mm_location_gps_raw_new ();
|
||||||
|
|
||||||
|
self->priv->filename = g_build_path (G_DIR_SEPARATOR_S, PKGSTATEDIR, LOCATION_STATE_FILE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
finalize (GObject *object)
|
||||||
|
{
|
||||||
|
MMLocationCache *self = MM_LOCATION_CACHE (object);
|
||||||
|
|
||||||
|
g_object_unref (self->priv->gps_raw);
|
||||||
|
g_free (self->priv->filename);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (mm_location_cache_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dispose (GObject *object)
|
||||||
|
{
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
MMLocationCache *self = MM_LOCATION_CACHE (object);
|
||||||
|
|
||||||
|
if (!mm_location_cache_save (self, &error))
|
||||||
|
mm_obj_warn (self, "%s", error->message);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (mm_location_cache_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
log_object_iface_init (MMLogObjectInterface *iface)
|
||||||
|
{
|
||||||
|
iface->build_id = log_object_build_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mm_location_cache_class_init (MMLocationCacheClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
g_type_class_add_private (object_class, sizeof (MMLocationCachePrivate));
|
||||||
|
|
||||||
|
object_class->finalize = finalize;
|
||||||
|
object_class->dispose = dispose;
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/* -*- 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) 2026 Andrey Skvortsov <andrej.skvortzov@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MM_LOCATION_CACHE_H
|
||||||
|
#define MM_LOCATION_CACHE_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#define MM_TYPE_LOCATION_CACHE (mm_location_cache_get_type ())
|
||||||
|
#define MM_LOCATION_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_LOCATION_CACHE, MMLocationCache))
|
||||||
|
#define MM_LOCATION_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_LOCATION_CACHE, MMLocationCacheClass))
|
||||||
|
#define MM_IS_LOCATION_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_LOCATION_CACHE))
|
||||||
|
#define MM_IS_LOCATION_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_LOCATION_CACHE))
|
||||||
|
#define MM_LOCATION_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_LOCATION_CACHE, MMLocationCacheClass))
|
||||||
|
|
||||||
|
typedef struct _MMLocationCache MMLocationCache;
|
||||||
|
typedef struct _MMLocationCacheClass MMLocationCacheClass;
|
||||||
|
typedef struct _MMLocationCachePrivate MMLocationCachePrivate;
|
||||||
|
|
||||||
|
struct _MMLocationCache {
|
||||||
|
GObject parent;
|
||||||
|
MMLocationCachePrivate *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MMLocationCacheClass {
|
||||||
|
GObjectClass parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType mm_location_cache_get_type (void);
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMLocationCache, g_object_unref)
|
||||||
|
|
||||||
|
MMLocationCache *mm_location_cache_new (void);
|
||||||
|
|
||||||
|
gboolean mm_location_cache_load_from_file (MMLocationCache *self, const gchar *file, GError **error);
|
||||||
|
gboolean mm_location_cache_load (MMLocationCache *self, GError **error);
|
||||||
|
gboolean mm_location_cache_save_to_file (MMLocationCache *self, const gchar *file, GError **error);
|
||||||
|
gboolean mm_location_cache_save (MMLocationCache *self, GError **error);
|
||||||
|
void mm_location_cache_set_filename (MMLocationCache *self, const gchar *file);
|
||||||
|
void mm_location_cache_update_from_lat_lon (MMLocationCache *self, const double lat, const double lon);
|
||||||
|
void mm_location_cache_update_from_nmea (MMLocationCache *self, const gchar *nmea);
|
||||||
|
void mm_location_cache_get_lat_lon (MMLocationCache *self, double *lat, double *lon);
|
||||||
|
|
||||||
|
#endif /* MM_LOCATION_CACHE_H */
|
||||||
|
|
||||||
+387
-9
@@ -30,6 +30,7 @@
|
|||||||
#include "mm-iface-modem.h"
|
#include "mm-iface-modem.h"
|
||||||
#include "mm-iface-modem-3gpp.h"
|
#include "mm-iface-modem-3gpp.h"
|
||||||
#include "mm-iface-modem-location.h"
|
#include "mm-iface-modem-location.h"
|
||||||
|
#include "mm-location-cache.h"
|
||||||
#include "mm-sim-qmi.h"
|
#include "mm-sim-qmi.h"
|
||||||
#include "mm-shared-qmi.h"
|
#include "mm-shared-qmi.h"
|
||||||
#include "mm-modem-helpers-qmi.h"
|
#include "mm-modem-helpers-qmi.h"
|
||||||
@@ -91,6 +92,9 @@ typedef struct {
|
|||||||
guint32 loc_assistance_data_max_file_size;
|
guint32 loc_assistance_data_max_file_size;
|
||||||
guint32 loc_assistance_data_max_part_size;
|
guint32 loc_assistance_data_max_part_size;
|
||||||
|
|
||||||
|
gulong loc_assistance_inject_time_req_indication_id;
|
||||||
|
gulong loc_assistance_inject_position_req_indication_id;
|
||||||
|
|
||||||
/* Carrier config helpers */
|
/* Carrier config helpers */
|
||||||
gboolean config_active_default;
|
gboolean config_active_default;
|
||||||
GArray *config_list;
|
GArray *config_list;
|
||||||
@@ -102,6 +106,9 @@ typedef struct {
|
|||||||
gulong uim_slot_status_indication_id;
|
gulong uim_slot_status_indication_id;
|
||||||
gulong uim_refresh_indication_id;
|
gulong uim_refresh_indication_id;
|
||||||
guint uim_refresh_start_timeout_id;
|
guint uim_refresh_start_timeout_id;
|
||||||
|
|
||||||
|
/* Cached last location from nmea */
|
||||||
|
MMLocationCache *location_cache;
|
||||||
} Private;
|
} Private;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -119,6 +126,10 @@ private_free (Private *priv)
|
|||||||
g_object_unref (priv->pds_client);
|
g_object_unref (priv->pds_client);
|
||||||
if (priv->loc_location_nmea_indication_id)
|
if (priv->loc_location_nmea_indication_id)
|
||||||
g_signal_handler_disconnect (priv->loc_client, priv->loc_location_nmea_indication_id);
|
g_signal_handler_disconnect (priv->loc_client, priv->loc_location_nmea_indication_id);
|
||||||
|
if (priv->loc_assistance_inject_time_req_indication_id)
|
||||||
|
g_signal_handler_disconnect (priv->loc_client, priv->loc_assistance_inject_time_req_indication_id);
|
||||||
|
if (priv->loc_assistance_inject_position_req_indication_id)
|
||||||
|
g_signal_handler_disconnect (priv->loc_client, priv->loc_assistance_inject_position_req_indication_id);
|
||||||
if (priv->loc_client)
|
if (priv->loc_client)
|
||||||
g_object_unref (priv->loc_client);
|
g_object_unref (priv->loc_client);
|
||||||
if (priv->uim_slot_status_indication_id)
|
if (priv->uim_slot_status_indication_id)
|
||||||
@@ -131,6 +142,7 @@ private_free (Private *priv)
|
|||||||
g_object_unref (priv->uim_client);
|
g_object_unref (priv->uim_client);
|
||||||
if (priv->uim_refresh_start_timeout_id)
|
if (priv->uim_refresh_start_timeout_id)
|
||||||
g_source_remove (priv->uim_refresh_start_timeout_id);
|
g_source_remove (priv->uim_refresh_start_timeout_id);
|
||||||
|
g_clear_object (&priv->location_cache);
|
||||||
if (priv->feature_nas_ssp_acquisition_order_preference_array)
|
if (priv->feature_nas_ssp_acquisition_order_preference_array)
|
||||||
g_array_unref (priv->feature_nas_ssp_acquisition_order_preference_array);
|
g_array_unref (priv->feature_nas_ssp_acquisition_order_preference_array);
|
||||||
g_strfreev (priv->loc_assistance_data_servers);
|
g_strfreev (priv->loc_assistance_data_servers);
|
||||||
@@ -140,7 +152,8 @@ private_free (Private *priv)
|
|||||||
static Private *
|
static Private *
|
||||||
get_private (MMSharedQmi *self)
|
get_private (MMSharedQmi *self)
|
||||||
{
|
{
|
||||||
Private *priv;
|
g_autoptr(GError) error = NULL;
|
||||||
|
Private *priv;
|
||||||
|
|
||||||
if (G_UNLIKELY (!private_quark))
|
if (G_UNLIKELY (!private_quark))
|
||||||
private_quark = g_quark_from_static_string (PRIVATE_TAG);
|
private_quark = g_quark_from_static_string (PRIVATE_TAG);
|
||||||
@@ -155,6 +168,10 @@ get_private (MMSharedQmi *self)
|
|||||||
priv->feature_nas_ssp_acquisition_order_preference = FEATURE_UNKNOWN;
|
priv->feature_nas_ssp_acquisition_order_preference = FEATURE_UNKNOWN;
|
||||||
priv->config_active_i = -1;
|
priv->config_active_i = -1;
|
||||||
|
|
||||||
|
priv->location_cache = mm_location_cache_new ();
|
||||||
|
if (!mm_location_cache_load (priv->location_cache, &error))
|
||||||
|
mm_obj_warn (priv->location_cache, "%s", error->message);
|
||||||
|
|
||||||
/* Setup parent class' MMIfaceModemLocation */
|
/* Setup parent class' MMIfaceModemLocation */
|
||||||
g_assert (MM_SHARED_QMI_GET_INTERFACE (self)->peek_parent_location_interface);
|
g_assert (MM_SHARED_QMI_GET_INTERFACE (self)->peek_parent_location_interface);
|
||||||
priv->iface_modem_location_parent = MM_SHARED_QMI_GET_INTERFACE (self)->peek_parent_location_interface (self);
|
priv->iface_modem_location_parent = MM_SHARED_QMI_GET_INTERFACE (self)->peek_parent_location_interface (self);
|
||||||
@@ -5156,13 +5173,17 @@ stop_gps_engine (MMSharedQmi *self,
|
|||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
GTask *task;
|
GTask *task;
|
||||||
Private *priv;
|
Private *priv;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
|
||||||
priv = get_private (self);
|
priv = get_private (self);
|
||||||
|
|
||||||
task = g_task_new (self, NULL, callback, user_data);
|
task = g_task_new (self, NULL, callback, user_data);
|
||||||
|
|
||||||
|
if (!mm_location_cache_save (priv->location_cache, &error))
|
||||||
|
mm_obj_warn (self, "%s", error->message);
|
||||||
|
|
||||||
if (priv->pds_client) {
|
if (priv->pds_client) {
|
||||||
QmiMessagePdsSetGpsServiceStateInput *input;
|
QmiMessagePdsSetGpsServiceStateInput *input;
|
||||||
|
|
||||||
@@ -5209,7 +5230,9 @@ pds_location_event_report_indication_cb (QmiClientPds *clie
|
|||||||
{
|
{
|
||||||
QmiPdsPositionSessionStatus session_status;
|
QmiPdsPositionSessionStatus session_status;
|
||||||
const gchar *nmea;
|
const gchar *nmea;
|
||||||
|
Private *priv;
|
||||||
|
|
||||||
|
priv = get_private (MM_SHARED_QMI (self));
|
||||||
if (qmi_indication_pds_event_report_output_get_position_session_status (
|
if (qmi_indication_pds_event_report_output_get_position_session_status (
|
||||||
output,
|
output,
|
||||||
&session_status,
|
&session_status,
|
||||||
@@ -5224,6 +5247,7 @@ pds_location_event_report_indication_cb (QmiClientPds *clie
|
|||||||
NULL)) {
|
NULL)) {
|
||||||
mm_obj_dbg (self, "[NMEA] %s", nmea);
|
mm_obj_dbg (self, "[NMEA] %s", nmea);
|
||||||
mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), nmea);
|
mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), nmea);
|
||||||
|
mm_location_cache_update_from_nmea (priv->location_cache, nmea);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5233,13 +5257,16 @@ loc_location_nmea_indication_cb (QmiClientLoc *client,
|
|||||||
MMSharedQmi *self)
|
MMSharedQmi *self)
|
||||||
{
|
{
|
||||||
const gchar *nmea = NULL;
|
const gchar *nmea = NULL;
|
||||||
|
Private *priv;
|
||||||
|
|
||||||
|
priv = get_private (MM_SHARED_QMI (self));
|
||||||
qmi_indication_loc_nmea_output_get_nmea_string (output, &nmea, NULL);
|
qmi_indication_loc_nmea_output_get_nmea_string (output, &nmea, NULL);
|
||||||
if (!nmea)
|
if (!nmea)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mm_obj_dbg (self, "[NMEA] %s", nmea);
|
mm_obj_dbg (self, "[NMEA] %s", nmea);
|
||||||
mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), nmea);
|
mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), nmea);
|
||||||
|
mm_location_cache_update_from_nmea (priv->location_cache, nmea);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -5670,6 +5697,7 @@ loc_start_ready (QmiClientLoc *client,
|
|||||||
{
|
{
|
||||||
QmiMessageLocRegisterEventsInput *input;
|
QmiMessageLocRegisterEventsInput *input;
|
||||||
QmiMessageLocStartOutput *output;
|
QmiMessageLocStartOutput *output;
|
||||||
|
guint64 event_mask;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|
||||||
output = qmi_client_loc_start_finish (client, res, &error);
|
output = qmi_client_loc_start_finish (client, res, &error);
|
||||||
@@ -5691,8 +5719,11 @@ loc_start_ready (QmiClientLoc *client,
|
|||||||
qmi_message_loc_start_output_unref (output);
|
qmi_message_loc_start_output_unref (output);
|
||||||
|
|
||||||
input = qmi_message_loc_register_events_input_new ();
|
input = qmi_message_loc_register_events_input_new ();
|
||||||
|
event_mask = QMI_LOC_EVENT_REGISTRATION_FLAG_NMEA |
|
||||||
|
QMI_LOC_EVENT_REGISTRATION_FLAG_INJECT_TIME_REQUEST |
|
||||||
|
QMI_LOC_EVENT_REGISTRATION_FLAG_INJECT_POSITION_REQUEST;
|
||||||
qmi_message_loc_register_events_input_set_event_registration_mask (
|
qmi_message_loc_register_events_input_set_event_registration_mask (
|
||||||
input, QMI_LOC_EVENT_REGISTRATION_FLAG_NMEA, NULL);
|
input, event_mask, NULL);
|
||||||
qmi_client_loc_register_events (client,
|
qmi_client_loc_register_events (client,
|
||||||
input,
|
input,
|
||||||
10,
|
10,
|
||||||
@@ -6714,6 +6745,338 @@ loc_location_get_predicted_orbits_data_source_indication_timed_out (GTask *task)
|
|||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Location: Inject UTC time */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
guint timeout_id;
|
||||||
|
gulong indication_id;
|
||||||
|
QmiClientLoc *client;
|
||||||
|
} InjectTimeContext;
|
||||||
|
|
||||||
|
static void
|
||||||
|
inject_time_context_cleanup_action (InjectTimeContext *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->timeout_id) {
|
||||||
|
g_source_remove (ctx->timeout_id);
|
||||||
|
ctx->timeout_id = 0;
|
||||||
|
}
|
||||||
|
if (ctx->indication_id) {
|
||||||
|
if (ctx->client)
|
||||||
|
g_signal_handler_disconnect (ctx->client, ctx->indication_id);
|
||||||
|
ctx->indication_id = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
inject_time_context_free (InjectTimeContext *ctx)
|
||||||
|
{
|
||||||
|
inject_time_context_cleanup_action (ctx);
|
||||||
|
g_clear_object (&ctx->client);
|
||||||
|
g_slice_free (InjectTimeContext, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
inject_time_finish_task (GTask *_task,
|
||||||
|
GError *error)
|
||||||
|
{
|
||||||
|
InjectTimeContext *ctx;
|
||||||
|
g_autoptr(GTask) task = _task;
|
||||||
|
|
||||||
|
ctx = g_task_get_task_data (task);
|
||||||
|
inject_time_context_cleanup_action (ctx);
|
||||||
|
if (error)
|
||||||
|
g_task_return_error (task, error);
|
||||||
|
else
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
loc_location_inject_time_indication_timed_out (GTask *task)
|
||||||
|
{
|
||||||
|
inject_time_finish_task (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
|
||||||
|
"Operation timed out"));
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
loc_location_inject_time_indication_cb (QmiClientLoc *client,
|
||||||
|
QmiIndicationLocInjectUtcTimeOutput *output,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
QmiLocIndicationStatus status;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (!qmi_indication_loc_inject_utc_time_output_get_indication_status (output, &status, &error))
|
||||||
|
g_prefix_error (&error, "QMI operation failed: ");
|
||||||
|
else
|
||||||
|
mm_error_from_qmi_loc_indication_status (status, &error);
|
||||||
|
inject_time_finish_task (task, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
loc_location_inject_time_ready (QmiClientLoc *client,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
g_autoptr(QmiMessageLocInjectUtcTimeOutput) output = NULL;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
output = qmi_client_loc_inject_utc_time_finish (client, res, &error);
|
||||||
|
if (!output) {
|
||||||
|
g_prefix_error (&error, "QMI operation failed: ");
|
||||||
|
inject_time_finish_task (task, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qmi_message_loc_inject_utc_time_output_get_result (output, &error)) {
|
||||||
|
inject_time_finish_task (task, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
loc_location_inject_time_req_indication_cb (QmiClientLoc *client,
|
||||||
|
QmiIndicationLocInjectTimeRequestOutput *output,
|
||||||
|
MMSharedQmi *self)
|
||||||
|
{
|
||||||
|
g_autoptr(QmiMessageLocInjectUtcTimeInput) input = NULL;
|
||||||
|
guint64 utc_ms = 0;
|
||||||
|
GTask *task;
|
||||||
|
InjectTimeContext *ctx;
|
||||||
|
|
||||||
|
task = g_task_new (self, NULL, NULL, NULL);
|
||||||
|
ctx = g_slice_new0 (InjectTimeContext);
|
||||||
|
g_task_set_task_data (task, ctx, (GDestroyNotify)inject_time_context_free);
|
||||||
|
|
||||||
|
ctx->client = g_object_ref (client);
|
||||||
|
|
||||||
|
input = qmi_message_loc_inject_utc_time_input_new ();
|
||||||
|
|
||||||
|
utc_ms = g_get_real_time () / 1000;
|
||||||
|
qmi_message_loc_inject_utc_time_input_set_utc_time (input, utc_ms, NULL);
|
||||||
|
qmi_message_loc_inject_utc_time_input_set_time_uncertainty (input, 1000, NULL);
|
||||||
|
qmi_message_loc_inject_utc_time_input_set_time_source (input, QMI_LOC_INJECTED_TIME_SOURCE_UNKNOWN, NULL);
|
||||||
|
|
||||||
|
ctx->timeout_id = g_timeout_add_seconds (10,
|
||||||
|
(GSourceFunc) loc_location_inject_time_indication_timed_out,
|
||||||
|
task);
|
||||||
|
|
||||||
|
ctx->indication_id = g_signal_connect (client,
|
||||||
|
"inject-utc-time",
|
||||||
|
G_CALLBACK (loc_location_inject_time_indication_cb),
|
||||||
|
task);
|
||||||
|
|
||||||
|
qmi_client_loc_inject_utc_time (
|
||||||
|
QMI_CLIENT_LOC (ctx->client),
|
||||||
|
input,
|
||||||
|
10,
|
||||||
|
NULL, /* cancellable */
|
||||||
|
(GAsyncReadyCallback)loc_location_inject_time_ready,
|
||||||
|
task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Location: Inject position */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
guint timeout_id;
|
||||||
|
gulong indication_id;
|
||||||
|
QmiClientLoc *client;
|
||||||
|
} InjectPositionContext;
|
||||||
|
|
||||||
|
static void
|
||||||
|
inject_position_context_cleanup_action (InjectPositionContext *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->timeout_id) {
|
||||||
|
g_source_remove (ctx->timeout_id);
|
||||||
|
ctx->timeout_id = 0;
|
||||||
|
}
|
||||||
|
if (ctx->indication_id) {
|
||||||
|
if (ctx->client)
|
||||||
|
g_signal_handler_disconnect (ctx->client, ctx->indication_id);
|
||||||
|
ctx->indication_id = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
inject_position_context_free (InjectPositionContext *ctx)
|
||||||
|
{
|
||||||
|
inject_position_context_cleanup_action (ctx);
|
||||||
|
g_clear_object (&ctx->client);
|
||||||
|
g_slice_free (InjectPositionContext, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
inject_position_finish_task (GTask *_task,
|
||||||
|
GError *error)
|
||||||
|
{
|
||||||
|
InjectPositionContext *ctx;
|
||||||
|
g_autoptr(GTask) task = _task;
|
||||||
|
|
||||||
|
ctx = g_task_get_task_data (task);
|
||||||
|
inject_position_context_cleanup_action (ctx);
|
||||||
|
if (error)
|
||||||
|
g_task_return_error (task, error);
|
||||||
|
else
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
loc_location_inject_position_indication_timed_out (GTask *task)
|
||||||
|
{
|
||||||
|
inject_position_finish_task (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
|
||||||
|
"Operation timed out"));
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
loc_location_inject_position_indication_cb (QmiClientLoc *client,
|
||||||
|
QmiIndicationLocInjectPositionOutput *output,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
QmiLocIndicationStatus status;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (!qmi_indication_loc_inject_position_output_get_indication_status (output, &status, &error))
|
||||||
|
g_prefix_error (&error, "QMI operation failed: ");
|
||||||
|
else
|
||||||
|
mm_error_from_qmi_loc_indication_status (status, &error);
|
||||||
|
inject_position_finish_task (task, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
loc_location_inject_position_ready (QmiClientLoc *client,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
g_autoptr(QmiMessageLocInjectPositionOutput) output = NULL;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
output = qmi_client_loc_inject_position_finish (client, res, &error);
|
||||||
|
if (!output) {
|
||||||
|
g_prefix_error (&error, "QMI operation failed: ");
|
||||||
|
inject_position_finish_task (task, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qmi_message_loc_inject_position_output_get_result (output, &error)) {
|
||||||
|
inject_position_finish_task (task, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
loc_location_inject_position_req_indication_cb (QmiClientLoc *client,
|
||||||
|
QmiIndicationLocInjectPositionRequestOutput *output,
|
||||||
|
MMSharedQmi *self)
|
||||||
|
{
|
||||||
|
g_autoptr(QmiMessageLocInjectPositionInput) input = NULL;
|
||||||
|
InjectPositionContext *ctx;
|
||||||
|
Private *priv;
|
||||||
|
GTask *task;
|
||||||
|
gdouble lat;
|
||||||
|
gdouble lon;
|
||||||
|
guint64 utc_ms = 0;
|
||||||
|
gdouble last_lat;
|
||||||
|
gdouble last_lon;
|
||||||
|
guint64 timestamp;
|
||||||
|
|
||||||
|
qmi_indication_loc_inject_position_request_output_get_latitude (output, &lat, NULL);
|
||||||
|
qmi_indication_loc_inject_position_request_output_get_longitude (output, &lon, NULL);
|
||||||
|
qmi_indication_loc_inject_position_request_output_get_utc_timestamp (output, ×tamp, NULL);
|
||||||
|
|
||||||
|
/* check whether modem already has position */
|
||||||
|
if ((lat != 0.0) && (lon != 0.0) && (timestamp != 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
priv = get_private (MM_SHARED_QMI (self));
|
||||||
|
mm_location_cache_get_lat_lon (priv->location_cache, &last_lat, &last_lon);
|
||||||
|
if ((last_lat == MM_LOCATION_LATITUDE_UNKNOWN) ||
|
||||||
|
(last_lon == MM_LOCATION_LONGITUDE_UNKNOWN))
|
||||||
|
return;
|
||||||
|
|
||||||
|
task = g_task_new (self, NULL, NULL, NULL);
|
||||||
|
ctx = g_slice_new0 (InjectPositionContext);
|
||||||
|
g_task_set_task_data (task, ctx, (GDestroyNotify)inject_position_context_free);
|
||||||
|
|
||||||
|
ctx->client = g_object_ref (client);
|
||||||
|
|
||||||
|
input = qmi_message_loc_inject_position_input_new ();
|
||||||
|
|
||||||
|
qmi_message_loc_inject_position_input_set_latitude (input, last_lat, NULL);
|
||||||
|
qmi_message_loc_inject_position_input_set_longitude (input, last_lon, NULL);
|
||||||
|
qmi_message_loc_inject_position_input_set_position_source (input, QMI_LOC_POSITION_SOURCE_OTHER, NULL);
|
||||||
|
|
||||||
|
// it's not recommended to advertise accuracy better than 1000 meters to the modem
|
||||||
|
qmi_message_loc_inject_position_input_set_horizontal_uncertainty_circular (input, 1000, NULL);
|
||||||
|
qmi_message_loc_inject_position_input_set_horizontal_confidence (input, 68, NULL);
|
||||||
|
|
||||||
|
utc_ms = g_get_real_time () / 1000;
|
||||||
|
qmi_message_loc_inject_position_input_set_utc_timestamp (input, utc_ms, NULL);
|
||||||
|
|
||||||
|
ctx->timeout_id = g_timeout_add_seconds (10,
|
||||||
|
(GSourceFunc) loc_location_inject_position_indication_timed_out,
|
||||||
|
task);
|
||||||
|
|
||||||
|
ctx->indication_id = g_signal_connect (client,
|
||||||
|
"inject-position",
|
||||||
|
G_CALLBACK (loc_location_inject_position_indication_cb),
|
||||||
|
task);
|
||||||
|
|
||||||
|
qmi_client_loc_inject_position (
|
||||||
|
QMI_CLIENT_LOC (ctx->client),
|
||||||
|
input,
|
||||||
|
10,
|
||||||
|
NULL, /* cancellable */
|
||||||
|
(GAsyncReadyCallback)loc_location_inject_position_ready,
|
||||||
|
task);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
loc_register_event_inject_req_ready (QmiClientLoc *client,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
g_autoptr(QmiMessageLocRegisterEventsOutput) output = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
MMSharedQmi *self;
|
||||||
|
Private *priv;
|
||||||
|
|
||||||
|
output = qmi_client_loc_register_events_finish (client, res, &error);
|
||||||
|
if (!output) {
|
||||||
|
mm_obj_warn (client, "QMI operation failed: %s", error->message);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qmi_message_loc_register_events_output_get_result (output, &error)) {
|
||||||
|
mm_obj_warn (client, "Couldn't not register tracking events: %s", error->message);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
self = g_task_get_source_object (task);
|
||||||
|
priv = get_private (self);
|
||||||
|
|
||||||
|
g_assert (!priv->loc_assistance_inject_time_req_indication_id);
|
||||||
|
priv->loc_assistance_inject_time_req_indication_id =
|
||||||
|
g_signal_connect (client,
|
||||||
|
"inject-time-request",
|
||||||
|
G_CALLBACK (loc_location_inject_time_req_indication_cb),
|
||||||
|
self);
|
||||||
|
|
||||||
|
g_assert (!priv->loc_assistance_inject_position_req_indication_id);
|
||||||
|
priv->loc_assistance_inject_position_req_indication_id =
|
||||||
|
g_signal_connect (client,
|
||||||
|
"inject-position-request",
|
||||||
|
G_CALLBACK (loc_location_inject_position_req_indication_cb),
|
||||||
|
self);
|
||||||
|
|
||||||
|
out:
|
||||||
|
g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_XTRA);
|
||||||
|
g_object_unref (task);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
loc_location_get_predicted_orbits_data_source_indication_cb (QmiClientLoc *client,
|
loc_location_get_predicted_orbits_data_source_indication_cb (QmiClientLoc *client,
|
||||||
QmiIndicationLocGetPredictedOrbitsDataSourceOutput *output,
|
QmiIndicationLocGetPredictedOrbitsDataSourceOutput *output,
|
||||||
@@ -6772,13 +7135,28 @@ loc_location_get_predicted_orbits_data_source_indication_cb (QmiClientLoc
|
|||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (error)
|
if (error) {
|
||||||
g_task_return_error (task, error);
|
g_task_return_error (task, error);
|
||||||
else if (!supported)
|
g_object_unref (task);
|
||||||
|
} else if (!supported) {
|
||||||
g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE);
|
g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE);
|
||||||
else
|
g_object_unref (task);
|
||||||
g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_XTRA);
|
} else {
|
||||||
g_object_unref (task);
|
g_autoptr(QmiMessageLocRegisterEventsInput) input = NULL;
|
||||||
|
guint64 event_mask;
|
||||||
|
|
||||||
|
event_mask = QMI_LOC_EVENT_REGISTRATION_FLAG_INJECT_TIME_REQUEST |
|
||||||
|
QMI_LOC_EVENT_REGISTRATION_FLAG_INJECT_POSITION_REQUEST;
|
||||||
|
input = qmi_message_loc_register_events_input_new ();
|
||||||
|
qmi_message_loc_register_events_input_set_event_registration_mask (
|
||||||
|
input, event_mask, NULL);
|
||||||
|
qmi_client_loc_register_events (client,
|
||||||
|
input,
|
||||||
|
10,
|
||||||
|
NULL,
|
||||||
|
(GAsyncReadyCallback) loc_register_event_inject_req_ready,
|
||||||
|
task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ test_units = {
|
|||||||
'charsets': libhelpers_dep,
|
'charsets': libhelpers_dep,
|
||||||
'error-helpers': libhelpers_dep,
|
'error-helpers': libhelpers_dep,
|
||||||
'kernel-device-helpers': libkerneldevice_dep,
|
'kernel-device-helpers': libkerneldevice_dep,
|
||||||
|
'location-cache': libhelpers_dep,
|
||||||
'modem-helpers': libhelpers_dep,
|
'modem-helpers': libhelpers_dep,
|
||||||
'port-scheduler': libport_dep,
|
'port-scheduler': libport_dep,
|
||||||
'sms-part-3gpp': libhelpers_dep,
|
'sms-part-3gpp': libhelpers_dep,
|
||||||
|
|||||||
@@ -0,0 +1,453 @@
|
|||||||
|
/*****************************************************************************/
|
||||||
|
/* -*- 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) 2026 Andrey Skvortsov <andrej.skvortzov@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
#define _LIBMM_INSIDE_MM
|
||||||
|
#include <libmm-glib.h>
|
||||||
|
#include "mm-log-test.h"
|
||||||
|
#include "mm-location-cache.h"
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static const gdouble reset_lat = 0.1;
|
||||||
|
static const gdouble reset_lon = 0.2;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const gchar *file_content;
|
||||||
|
const gdouble expect_lat;
|
||||||
|
const gdouble expect_lon;
|
||||||
|
const gboolean expect_success;
|
||||||
|
} LoadTestcase;
|
||||||
|
|
||||||
|
static const LoadTestcase load_testcases[] = {
|
||||||
|
{
|
||||||
|
.file_content = "[last_position]\n"
|
||||||
|
"latitude=48.137\n"
|
||||||
|
"longitude=11.575\n",
|
||||||
|
.expect_lat = 48.137,
|
||||||
|
.expect_lon = 11.575,
|
||||||
|
.expect_success = TRUE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.file_content = "[last_position]\n"
|
||||||
|
"longitude=11.575\n"
|
||||||
|
"latitude=48.137\n",
|
||||||
|
.expect_lat = 48.137,
|
||||||
|
.expect_lon = 11.575,
|
||||||
|
.expect_success = TRUE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.file_content = "[last_position]\n"
|
||||||
|
"latitude=-27.142\n"
|
||||||
|
"longitude=-109.314\n",
|
||||||
|
.expect_lat = -27.142,
|
||||||
|
.expect_lon = -109.314,
|
||||||
|
.expect_success = TRUE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.file_content = "[last_position]\n"
|
||||||
|
"latitude=48.137\n",
|
||||||
|
.expect_lat = MM_LOCATION_LATITUDE_UNKNOWN,
|
||||||
|
.expect_lon = MM_LOCATION_LONGITUDE_UNKNOWN,
|
||||||
|
.expect_success = FALSE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.file_content = "[last_position]\n"
|
||||||
|
"longitude=11.575\n",
|
||||||
|
.expect_lat = MM_LOCATION_LATITUDE_UNKNOWN,
|
||||||
|
.expect_lon = MM_LOCATION_LONGITUDE_UNKNOWN,
|
||||||
|
.expect_success = FALSE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.file_content = "[last_position]\n",
|
||||||
|
.expect_lat = MM_LOCATION_LATITUDE_UNKNOWN,
|
||||||
|
.expect_lon = MM_LOCATION_LONGITUDE_UNKNOWN,
|
||||||
|
.expect_success = FALSE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.file_content = "To be, or not to be, that is the question...\n",
|
||||||
|
.expect_lat = MM_LOCATION_LATITUDE_UNKNOWN,
|
||||||
|
.expect_lon = MM_LOCATION_LONGITUDE_UNKNOWN,
|
||||||
|
.expect_success = FALSE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_location_cache_load (void)
|
||||||
|
{
|
||||||
|
g_autoptr(MMLocationCache) cache = NULL;
|
||||||
|
gdouble get_lat;
|
||||||
|
gdouble get_lon;
|
||||||
|
g_autofree gchar *filename = NULL;
|
||||||
|
g_autoptr(GFile) loc = NULL;
|
||||||
|
g_autoptr(GIOStream) stream = NULL;
|
||||||
|
g_autoptr(GError) err = NULL;
|
||||||
|
gulong i;
|
||||||
|
gboolean ret;
|
||||||
|
|
||||||
|
cache = mm_location_cache_new ();
|
||||||
|
g_assert_nonnull (cache);
|
||||||
|
|
||||||
|
loc = g_file_new_tmp (NULL, (GFileIOStream **)&stream, &err);
|
||||||
|
filename = g_file_get_path (loc);
|
||||||
|
g_assert_no_error (err);
|
||||||
|
g_assert_nonnull (filename);
|
||||||
|
|
||||||
|
mm_location_cache_set_filename (cache, filename);
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (load_testcases); i++) {
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
LoadTestcase test = load_testcases[i];
|
||||||
|
|
||||||
|
g_debug ("load test %ld", i);
|
||||||
|
g_file_replace_contents (loc,
|
||||||
|
test.file_content,
|
||||||
|
strlen (test.file_content),
|
||||||
|
NULL, /* etag */
|
||||||
|
FALSE, /* make backup */
|
||||||
|
G_FILE_CREATE_NONE, /* flags */
|
||||||
|
NULL, /* new etag */
|
||||||
|
NULL, /* cancellable */
|
||||||
|
&error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
/* reset coordinates */
|
||||||
|
mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon);
|
||||||
|
ret = mm_location_cache_load_from_file (cache, filename, &error);
|
||||||
|
g_assert (ret == test.expect_success);
|
||||||
|
g_assert (test.expect_success ? error == NULL : error != NULL);
|
||||||
|
|
||||||
|
mm_location_cache_get_lat_lon (cache, &get_lat, &get_lon);
|
||||||
|
g_assert_cmpfloat_with_epsilon (test.expect_lat, get_lat, 0.01);
|
||||||
|
g_assert_cmpfloat_with_epsilon (test.expect_lon, get_lon, 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try to load missing file */
|
||||||
|
g_file_delete (loc, NULL, NULL);
|
||||||
|
mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon);
|
||||||
|
ret = mm_location_cache_load_from_file (cache, filename, &err);
|
||||||
|
g_assert (err != NULL);
|
||||||
|
|
||||||
|
mm_location_cache_get_lat_lon (cache, &get_lat, &get_lon);
|
||||||
|
g_assert_cmpfloat_with_epsilon (MM_LOCATION_LATITUDE_UNKNOWN, get_lat, 0.01);
|
||||||
|
g_assert_cmpfloat_with_epsilon (MM_LOCATION_LONGITUDE_UNKNOWN, get_lon, 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const gchar *nmea;
|
||||||
|
const gdouble set_lat;
|
||||||
|
const gdouble set_lon;
|
||||||
|
const gdouble expect_lat;
|
||||||
|
const gdouble expect_lon;
|
||||||
|
} UpdateTestcase;
|
||||||
|
|
||||||
|
static const UpdateTestcase update_testcases[] = {
|
||||||
|
{
|
||||||
|
.nmea = NULL,
|
||||||
|
.set_lat = 48.137,
|
||||||
|
.set_lon = 11.575,
|
||||||
|
.expect_lat = 48.137,
|
||||||
|
.expect_lon = 11.575,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.nmea = NULL,
|
||||||
|
.set_lat = 48.137,
|
||||||
|
.set_lon = MM_LOCATION_LONGITUDE_UNKNOWN,
|
||||||
|
.expect_lat = reset_lat,
|
||||||
|
.expect_lon = reset_lon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.nmea = NULL,
|
||||||
|
.set_lat = MM_LOCATION_LATITUDE_UNKNOWN,
|
||||||
|
.set_lon = 11.575,
|
||||||
|
.expect_lat = reset_lat,
|
||||||
|
.expect_lon = reset_lon,
|
||||||
|
},
|
||||||
|
/* only GGA NMEA sentences are supported by current implementation */
|
||||||
|
{
|
||||||
|
.nmea = "$GNGLL,4808.2200,N,01134.5000,E,012345.000,A,A*40",
|
||||||
|
.set_lat = MM_LOCATION_LATITUDE_UNKNOWN,
|
||||||
|
.set_lon = MM_LOCATION_LONGITUDE_UNKNOWN,
|
||||||
|
.expect_lat = reset_lat,
|
||||||
|
.expect_lon = reset_lon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.nmea = "$GNRMC,012345.000,A,4808.2200,N,01134.5000,E,1.0,300.5,100817,3.6,E,A,V*6B",
|
||||||
|
.set_lat = MM_LOCATION_LATITUDE_UNKNOWN,
|
||||||
|
.set_lon = MM_LOCATION_LONGITUDE_UNKNOWN,
|
||||||
|
.expect_lat = reset_lat,
|
||||||
|
.expect_lon = reset_lon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.nmea = "$GNGGA,012345.000,4808.2200,N,01134.5000,E,1,12,1.1,-14.1,M,32.0,M,,*5E",
|
||||||
|
.set_lat = MM_LOCATION_LATITUDE_UNKNOWN,
|
||||||
|
.set_lon = MM_LOCATION_LONGITUDE_UNKNOWN,
|
||||||
|
.expect_lat = 48.137,
|
||||||
|
.expect_lon = 11.575,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.nmea = NULL,
|
||||||
|
.set_lat = -27.142,
|
||||||
|
.set_lon = -109.314,
|
||||||
|
.expect_lat = -27.142,
|
||||||
|
.expect_lon = -109.314,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.nmea = "$GNGLL,2708.5200,S,10918.8400,W,012345.000,A,A*4E",
|
||||||
|
.set_lat = MM_LOCATION_LATITUDE_UNKNOWN,
|
||||||
|
.set_lon = MM_LOCATION_LONGITUDE_UNKNOWN,
|
||||||
|
.expect_lat = reset_lat,
|
||||||
|
.expect_lon = reset_lon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.nmea = "$GNRMC,012345.000,A,2708.5200,S,10918.8400,W,1.0,300.5,100817,3.6,E,A,V*65",
|
||||||
|
.set_lat = MM_LOCATION_LATITUDE_UNKNOWN,
|
||||||
|
.set_lon = MM_LOCATION_LONGITUDE_UNKNOWN,
|
||||||
|
.expect_lat = reset_lat,
|
||||||
|
.expect_lon = reset_lon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.nmea = "$GNGGA,012345.000,2708.5200,S,10918.8400,W,1,12,1.1,-14.1,M,32.0,M,,*50",
|
||||||
|
.set_lat = MM_LOCATION_LATITUDE_UNKNOWN,
|
||||||
|
.set_lon = MM_LOCATION_LONGITUDE_UNKNOWN,
|
||||||
|
.expect_lat = -27.142,
|
||||||
|
.expect_lon = -109.314,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_location_cache_update (void)
|
||||||
|
{
|
||||||
|
g_autoptr(MMLocationCache) cache = NULL;
|
||||||
|
gulong i;
|
||||||
|
|
||||||
|
cache = mm_location_cache_new ();
|
||||||
|
g_assert_nonnull (cache);
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (update_testcases); i++) {
|
||||||
|
gdouble get_lat = 0;
|
||||||
|
gdouble get_lon = 0;
|
||||||
|
UpdateTestcase test = update_testcases[i];
|
||||||
|
|
||||||
|
g_debug ("update test %ld", i);
|
||||||
|
|
||||||
|
/* reset coordinates */
|
||||||
|
mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon);
|
||||||
|
|
||||||
|
if (test.nmea)
|
||||||
|
mm_location_cache_update_from_nmea (cache, test.nmea);
|
||||||
|
else
|
||||||
|
mm_location_cache_update_from_lat_lon (cache, test.set_lat, test.set_lon);
|
||||||
|
mm_location_cache_get_lat_lon (cache, &get_lat, &get_lon);
|
||||||
|
g_assert_cmpfloat_with_epsilon (test.expect_lat, get_lat, 0.01);
|
||||||
|
g_assert_cmpfloat_with_epsilon (test.expect_lon, get_lon, 0.01);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
get_temp_filename (void)
|
||||||
|
{
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
gchar *filename;
|
||||||
|
gint fd;
|
||||||
|
|
||||||
|
fd = g_file_open_tmp (NULL, &filename, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (filename);
|
||||||
|
|
||||||
|
g_close (fd, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
g_unlink (filename);
|
||||||
|
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_location_cache_save (void)
|
||||||
|
{
|
||||||
|
g_autoptr(MMLocationCache) cache = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
g_autofree gchar *filename = NULL;
|
||||||
|
g_autofree gchar *path = NULL;
|
||||||
|
gboolean ret;
|
||||||
|
|
||||||
|
cache = mm_location_cache_new ();
|
||||||
|
g_assert_nonnull (cache);
|
||||||
|
|
||||||
|
filename = get_temp_filename ();
|
||||||
|
|
||||||
|
// write empty cache
|
||||||
|
g_unlink (filename);
|
||||||
|
ret = mm_location_cache_save_to_file (cache, filename, &error);
|
||||||
|
g_assert_true (ret);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
ret = g_file_test (filename, G_FILE_TEST_EXISTS);
|
||||||
|
g_assert_false (ret);
|
||||||
|
|
||||||
|
// write non-empty cache
|
||||||
|
g_unlink (filename);
|
||||||
|
mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon);
|
||||||
|
ret = mm_location_cache_save_to_file (cache, filename, &error);
|
||||||
|
g_assert_true (ret);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
ret = g_file_test (filename, G_FILE_TEST_EXISTS);
|
||||||
|
g_assert_true (ret);
|
||||||
|
g_unlink (filename);
|
||||||
|
|
||||||
|
// write to non-existing path
|
||||||
|
path = g_build_path (G_DIR_SEPARATOR_S, "missing", "X", "Y", "Z", "location.ini", NULL);
|
||||||
|
ret = mm_location_cache_save_to_file (cache, path, &error);
|
||||||
|
g_assert_false (ret);
|
||||||
|
g_assert (error != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const gdouble set_lat;
|
||||||
|
const gdouble set_lon;
|
||||||
|
} SaveLoadTestcase;
|
||||||
|
|
||||||
|
static const SaveLoadTestcase save_load_testcases[] = {
|
||||||
|
{
|
||||||
|
.set_lat = 48.137,
|
||||||
|
.set_lon = 11.575,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.set_lat = -27.142,
|
||||||
|
.set_lon = -109.314,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_location_cache_save_load (void)
|
||||||
|
{
|
||||||
|
g_autoptr(MMLocationCache) cache = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
g_autofree gchar *filename = NULL;
|
||||||
|
gboolean ret;
|
||||||
|
gulong i;
|
||||||
|
|
||||||
|
cache = mm_location_cache_new ();
|
||||||
|
g_assert_nonnull (cache);
|
||||||
|
|
||||||
|
filename = get_temp_filename ();
|
||||||
|
mm_location_cache_set_filename (cache, filename);
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (save_load_testcases); i++) {
|
||||||
|
gdouble get_lat = 0;
|
||||||
|
gdouble get_lon = 0;
|
||||||
|
SaveLoadTestcase test = save_load_testcases[i];
|
||||||
|
|
||||||
|
g_debug ("save/load test %ld", i);
|
||||||
|
|
||||||
|
mm_location_cache_update_from_lat_lon (cache, test.set_lat, test.set_lon);
|
||||||
|
g_unlink (filename);
|
||||||
|
ret = mm_location_cache_save (cache, &error);
|
||||||
|
g_assert_true (ret);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
ret = g_file_test (filename, G_FILE_TEST_EXISTS);
|
||||||
|
g_assert_true (ret);
|
||||||
|
|
||||||
|
mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon);
|
||||||
|
ret = mm_location_cache_load (cache, &error);
|
||||||
|
g_assert_true (ret);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
mm_location_cache_get_lat_lon (cache, &get_lat, &get_lon);
|
||||||
|
g_assert_cmpfloat_with_epsilon (test.set_lat, get_lat, 0.01);
|
||||||
|
g_assert_cmpfloat_with_epsilon (test.set_lon, get_lon, 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_unlink (filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_location_cache_create_dispose (void)
|
||||||
|
{
|
||||||
|
MMLocationCache *cache = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
g_autofree gchar *filename = NULL;
|
||||||
|
gboolean ret;
|
||||||
|
gdouble get_lat = 0;
|
||||||
|
gdouble get_lon = 0;
|
||||||
|
|
||||||
|
cache = mm_location_cache_new ();
|
||||||
|
g_assert_nonnull (cache);
|
||||||
|
|
||||||
|
// to check later, that MMLocationCache object is finalized
|
||||||
|
g_object_add_weak_pointer (G_OBJECT (cache), (gpointer *) &cache);
|
||||||
|
|
||||||
|
filename = get_temp_filename ();
|
||||||
|
mm_location_cache_set_filename (cache, filename);
|
||||||
|
mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon);
|
||||||
|
|
||||||
|
// drop the last reference and save cached location
|
||||||
|
g_object_unref (cache);
|
||||||
|
g_assert_null (cache);
|
||||||
|
|
||||||
|
// check whether file is created
|
||||||
|
ret = g_file_test (filename, G_FILE_TEST_EXISTS);
|
||||||
|
g_assert_true (ret);
|
||||||
|
|
||||||
|
// check file content
|
||||||
|
cache = mm_location_cache_new ();
|
||||||
|
g_assert_nonnull (cache);
|
||||||
|
|
||||||
|
mm_location_cache_set_filename (cache, filename);
|
||||||
|
ret = mm_location_cache_load (cache, &error);
|
||||||
|
g_assert_true (ret);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
mm_location_cache_get_lat_lon (cache, &get_lat, &get_lon);
|
||||||
|
g_assert_cmpfloat_with_epsilon (reset_lat, get_lat, 0.01);
|
||||||
|
g_assert_cmpfloat_with_epsilon (reset_lon, get_lon, 0.01);
|
||||||
|
|
||||||
|
g_object_unref (cache);
|
||||||
|
g_unlink (filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
setlocale (LC_ALL, "");
|
||||||
|
|
||||||
|
g_test_init (&argc, &argv, NULL);
|
||||||
|
|
||||||
|
g_test_add_func ("/MM/location-cache/update", test_location_cache_update);
|
||||||
|
g_test_add_func ("/MM/location-cache/load", test_location_cache_load);
|
||||||
|
g_test_add_func ("/MM/location-cache/save", test_location_cache_save);
|
||||||
|
g_test_add_func ("/MM/location-cache/save_load", test_location_cache_save_load);
|
||||||
|
g_test_add_func ("/MM/location-cache/create_dispose", test_location_cache_create_dispose);
|
||||||
|
|
||||||
|
return g_test_run ();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user