diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2014-07-15 10:59:52 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2014-07-15 11:04:53 +0200 |
commit | d686e9535bf9bb29556b1e815a16de523c5be37b (patch) | |
tree | 133397cbab8f442f030eda7d3a51af7e5f9728ec | |
parent | 4c95afec4e65ddfc249dac5570d5024c03a2730e (diff) | |
download | ModemManager-d686e9535bf9bb29556b1e815a16de523c5be37b.tar.gz |
examples: new location watcher examplealeksander/limited-location-user
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | examples/Makefile.am | 5 | ||||
-rw-r--r-- | examples/location-watcher/Makefile.am | 17 | ||||
-rw-r--r-- | examples/location-watcher/location-watcher.c | 538 |
5 files changed, 562 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore index 5888e9d9e..42ba16d24 100644 --- a/.gitignore +++ b/.gitignore @@ -163,5 +163,7 @@ plugins/test-modem-helpers-icera* plugins/test-modem-helpers-mbm* plugins/test-service-* +examples/location-watcher/location-watcher + TAGS ABOUT-NLS diff --git a/configure.ac b/configure.ac index 322d534d7..6e3c875ec 100644 --- a/configure.ac +++ b/configure.ac @@ -320,6 +320,7 @@ cli/Makefile examples/Makefile examples/modem-watcher-python/Makefile examples/modem-watcher-javascript/Makefile +examples/location-watcher/Makefile ]) AC_OUTPUT diff --git a/examples/Makefile.am b/examples/Makefile.am index 53b0b70e6..070b3e098 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1 +1,4 @@ -SUBDIRS = modem-watcher-python modem-watcher-javascript +SUBDIRS = \ + location-watcher \ + modem-watcher-python \ + modem-watcher-javascript diff --git a/examples/location-watcher/Makefile.am b/examples/location-watcher/Makefile.am new file mode 100644 index 000000000..b23ffd3d9 --- /dev/null +++ b/examples/location-watcher/Makefile.am @@ -0,0 +1,17 @@ +noinst_PROGRAMS = location-watcher + +location_watcher_CPPFLAGS = \ + $(MMCLI_CFLAGS) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/libmm-glib \ + -I${top_srcdir}/libmm-glib/generated \ + -I${top_builddir}/libmm-glib/generated + +location_watcher_SOURCES = \ + location-watcher.c + +location_watcher_LDADD = \ + $(MMCLI_LIBS) \ + $(top_builddir)/libmm-glib/libmm-glib.la diff --git a/examples/location-watcher/location-watcher.c b/examples/location-watcher/location-watcher.c new file mode 100644 index 000000000..5056d4ebf --- /dev/null +++ b/examples/location-watcher/location-watcher.c @@ -0,0 +1,538 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * location-watcher -- Watch location information from ModemManager + * + * 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 3 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es> + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <locale.h> +#include <string.h> + +#include <glib.h> +#include <gio/gio.h> + +#define _LIBMM_INSIDE_MMCLI +#include <libmm-glib.h> + +/* Globals */ +static GMainLoop *loop; +static MMManager *manager; +static GDBusConnection *connection; +static GHashTable *location_db; + +/* Context */ +static gboolean no_setup_flag; +static gboolean with_getter_flag; +static gboolean with_property_flag; +static GOptionEntry entries[] = { + { "no-setup", 'n', 0, G_OPTION_ARG_NONE, &no_setup_flag, + "No location setup, just watch", + NULL + }, + { "with-getter", 'g', 0, G_OPTION_ARG_NONE, &with_getter_flag, + "Use getter to query location (default)", + NULL + }, + { "with-property", 'p', 0, G_OPTION_ARG_NONE, &with_property_flag, + "Read location from the property, don't use getter", + NULL + }, + { NULL } +}; + +typedef struct { + MMModem *modem; + guint state_changed_id; + MMModemLocation *location; + guint location_updated_id; +} LocationInfo; + +static void +location_info_free (LocationInfo *info) +{ + if (!info) + return; + + if (g_signal_handler_is_connected (info->location, info->location_updated_id)) { + g_signal_handler_disconnect (info->location, info->location_updated_id); + if (!no_setup_flag) + mm_modem_location_setup_sync (info->location, + MM_MODEM_LOCATION_SOURCE_NONE, + FALSE, /* signal location */ + NULL, /* cancellable */ + NULL); + } + g_object_unref (info->location); + + if (g_signal_handler_is_connected (info->modem, info->state_changed_id)) + g_signal_handler_disconnect (info->modem, info->state_changed_id); + g_object_unref (info->modem); + + g_slice_free (LocationInfo, info); +} + +static void +signals_handler (int signum) +{ + if (loop && g_main_loop_is_running (loop)) { + g_printerr ("%s\n", + "cancelling the main loop...\n"); + g_main_loop_quit (loop); + } +} + +static void +untrack_location (MMModemLocation *location) +{ + g_hash_table_remove (location_db, mm_modem_location_get_path (location)); +} + +static void +location_print_and_free (MMModemLocation *location, + MMModemLocationSource mask, + MMLocation3gpp *location_3gpp, + MMLocationGpsNmea *location_gps_nmea, + MMLocationGpsRaw *location_gps_raw, + MMLocationCdmaBs *location_cdma_bs) +{ + if (location_3gpp) { + if (mask & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI) + g_print ("[%s,3GPP] MCC/MNC: '%u/%u', LAC: '%lu', CID: '%lu'\n", + mm_modem_location_get_path (location), + mm_location_3gpp_get_mobile_country_code (location_3gpp), + mm_location_3gpp_get_mobile_network_code (location_3gpp), + mm_location_3gpp_get_location_area_code (location_3gpp), + mm_location_3gpp_get_cell_id (location_3gpp)); + g_object_unref (location_3gpp); + } + + if (location_gps_nmea) { + if (mask & MM_MODEM_LOCATION_SOURCE_GPS_NMEA) { + gchar *full; + + full = mm_location_gps_nmea_build_full (location_gps_nmea); + g_print ("[%s,GPS NMEA]:\n" + "%s\n", + mm_modem_location_get_path (location), + full); + g_free (full); + } + g_object_unref (location_gps_nmea); + } + + if (location_gps_raw) { + if (mask & MM_MODEM_LOCATION_SOURCE_GPS_RAW) + g_print ("[%s GPS raw]: UTC: '%s', Lat: '%lf', Lon: '%lf', Alt: '%lf'\n", + mm_modem_location_get_path (location), + mm_location_gps_raw_get_utc_time (location_gps_raw), + mm_location_gps_raw_get_longitude (location_gps_raw), + mm_location_gps_raw_get_latitude (location_gps_raw), + mm_location_gps_raw_get_altitude (location_gps_raw)); + g_object_unref (location_gps_raw); + } + + if (location_cdma_bs) { + if (mask & MM_MODEM_LOCATION_SOURCE_CDMA_BS) + g_print ("[%s CDMA BS]: Lat: '%lf', Lon: '%lf'\n", + mm_modem_location_get_path (location), + mm_location_cdma_bs_get_latitude (location_cdma_bs), + mm_location_cdma_bs_get_longitude (location_cdma_bs)); + g_object_unref (location_cdma_bs); + } +} + +static void +get_full_ready (MMModemLocation *location, + GAsyncResult *res, + gpointer user_data) +{ + guint mask; + GError *error = NULL; + MMLocation3gpp *location_3gpp = NULL; + MMLocationGpsNmea *location_gps_nmea = NULL; + MMLocationGpsRaw *location_gps_raw = NULL; + MMLocationCdmaBs *location_cdma_bs = NULL; + + mask = GPOINTER_TO_UINT (user_data); + + if (!mm_modem_location_get_full_finish (location, + res, + &location_3gpp, + &location_gps_nmea, + &location_gps_raw, + &location_cdma_bs, + &error)) { + g_printerr ("[%s] error: couldn't retrieve full location info: %s", + mm_modem_location_get_path (location), + error->message); + return; + } + + location_print_and_free (location, + (MMModemLocationSource) mask, + location_3gpp, + location_gps_nmea, + location_gps_raw, + location_cdma_bs); +} + +static void +location_updated_signal_cb (MMModemLocation *location, + MMModemLocationSource mask) +{ + gchar *str; + + str = mm_modem_location_source_build_string_from_mask (mask); + g_print ("[%s] location update notification: '%s'\n", + mm_modem_location_get_path (location), + str); + g_free (str); + + mm_modem_location_get_full (location, + NULL, /* cancellable */ + (GAsyncReadyCallback) get_full_ready, + GUINT_TO_POINTER (mask)); +} + +static void +location_property_updated_cb (MMModemLocation *location, + GParamSpec *spec) +{ + /* Read directly the property? */ + GVariantIter iter; + MMModemLocationSource source; + GVariant *value; + GVariant *dictionary = NULL; + MMLocation3gpp *location_3gpp = NULL; + MMLocationGpsNmea *location_gps_nmea = NULL; + MMLocationGpsRaw *location_gps_raw = NULL; + MMLocationCdmaBs *location_cdma_bs = NULL; + MMModemLocationSource mask; + + g_object_get (location, "location", &dictionary, NULL); + if (!dictionary) { + g_printerr ("[%s] error: couldn't retrieve full location info: unable to read property", + mm_modem_location_get_path (location)); + return; + } + + g_variant_iter_init (&iter, dictionary); + mask = MM_MODEM_LOCATION_SOURCE_NONE; + while (g_variant_iter_next (&iter, "{uv}", &source, &value)) { + GError *error = NULL; + + mask |= source; + + switch (source) { + case MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI: + location_3gpp = mm_location_3gpp_new_from_string_variant (value, &error); + break; + case MM_MODEM_LOCATION_SOURCE_GPS_NMEA: + location_gps_nmea = mm_location_gps_nmea_new_from_string_variant (value, &error); + break; + case MM_MODEM_LOCATION_SOURCE_GPS_RAW: + location_gps_raw = mm_location_gps_raw_new_from_dictionary (value, &error); + break; + case MM_MODEM_LOCATION_SOURCE_CDMA_BS: + location_cdma_bs = mm_location_cdma_bs_new_from_dictionary (value, &error); + break; + default: + g_warn_if_reached (); + break; + } + + if (error) { + gchar *str; + + str = mm_modem_location_source_build_string_from_mask (source); + g_printerr ("[%s] error reading '%s' location: %s", + mm_modem_location_get_path (location), + str, + error->message); + g_error_free (error); + } + g_variant_unref (value); + } + + location_print_and_free (location, + mask, + location_3gpp, + location_gps_nmea, + location_gps_raw, + location_cdma_bs); +} + +static void +location_setup_ready (MMModemLocation *location, + GAsyncResult *res) +{ + GError *error = NULL; + + if (!mm_modem_location_setup_finish (location, res, &error)) { + g_printerr ("[%s] error: couldn't setup location: '%s'\n", + mm_modem_location_get_path (location), + error->message); + g_error_free (error); + g_main_loop_quit (loop); + return; + } + + g_print ("[%s] location setup updated successfully\n", + mm_modem_location_get_path (location)); +} + +static void +state_changed_cb (MMModem *modem, + MMModemState old_state, + MMModemState new_state, + MMModemStateChangeReason reason) +{ + LocationInfo *info; + + info = g_hash_table_lookup (location_db, mm_modem_get_path (modem)); + if (!info) + return; + + /* If going into enabled state, enable all location info */ + if (old_state < MM_MODEM_STATE_ENABLED && new_state >= MM_MODEM_STATE_ENABLED) { + gboolean signal_location = FALSE; + + g_print ("[%s] enabling location...\n", + mm_modem_location_get_path (info->location)); + + /* Track updates */ + if (with_property_flag) { + signal_location = TRUE; + info->location_updated_id = g_signal_connect (info->location, + "notify::location", + G_CALLBACK (location_property_updated_cb), + NULL); + } else if (with_getter_flag) + info->location_updated_id = g_signal_connect (info->location, + "location-updated", + G_CALLBACK (location_updated_signal_cb), + NULL); + else + g_assert_not_reached (); + + /* Request to enable all location capabilities */ + if (!no_setup_flag) + mm_modem_location_setup (info->location, + mm_modem_location_get_capabilities (info->location), + signal_location, + NULL, /* cancellable */ + (GAsyncReadyCallback) location_setup_ready, + NULL); + return; + } + + /* If going out of enabled state, disable all location info */ + if (old_state >= MM_MODEM_STATE_ENABLED && new_state < MM_MODEM_STATE_ENABLED) { + g_print ("[%s] disabling location...\n", + mm_modem_location_get_path (info->location)); + /* Request to disable all location capabilities */ + if (!no_setup_flag) + mm_modem_location_setup (info->location, + MM_MODEM_LOCATION_SOURCE_NONE, + FALSE, /* signal location */ + NULL, /* cancellable */ + (GAsyncReadyCallback) location_setup_ready, + NULL); + /* Untrack updates */ + if (g_signal_handler_is_connected (info->location, info->location_updated_id)) + g_signal_handler_disconnect (info->location, info->location_updated_id); + info->location_updated_id = 0; + return; + } +} + +static void +track_location (MMModem *modem, + MMModemLocation *location) +{ + MMModemLocationSource capabilities; + gchar *str; + LocationInfo *info; + + capabilities = mm_modem_location_get_capabilities (location); + str = mm_modem_location_source_build_string_from_mask (capabilities); + g_print ("[%s] found modem with location capabilities: '%s'\n", + mm_modem_location_get_path (location), + str); + g_free (str); + + /* Keep location info */ + info = g_slice_new0 (LocationInfo); + info->modem = g_object_ref (modem); + info->state_changed_id = g_signal_connect (modem, + "state-changed", + G_CALLBACK (state_changed_cb), + NULL); + info->location = g_object_ref (location); + g_hash_table_replace (location_db, + g_strdup (mm_modem_location_get_path (location)), + info); + + /* If modem is already enabled, trigger location enable */ + if (mm_modem_get_state (modem) >= MM_MODEM_STATE_ENABLED) + state_changed_cb (modem, + MM_MODEM_STATE_UNKNOWN, + mm_modem_get_state (modem), + MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); +} + +static void +interface_added_cb (MMManager *man, + MMObject *obj, + GDBusInterface *interface, + gpointer user_data) +{ + if (!MM_IS_MODEM_LOCATION (interface)) { + g_print ("unneeded interface added...\n"); + return; + } + + g_print ("location interface added...\n"); + track_location (mm_object_peek_modem (obj), MM_MODEM_LOCATION (interface)); +} + +static void +interface_removed_cb (MMManager *man, + MMObject *obj, + GDBusInterface *interface, + gpointer user_data) +{ + if (!MM_IS_MODEM_LOCATION (interface)) + return; + untrack_location (MM_MODEM_LOCATION (interface)); +} + +static void +manager_new_ready (GObject *source, + GAsyncResult *res) +{ + GError *error = NULL; + GList *initial_objects, *l; + + manager = mm_manager_new_finish (res, &error); + if (!manager) { + g_printerr ("error: couldn't create manager: %s\n", + error ? error->message : "unknown error"); + exit (EXIT_FAILURE); + } + + g_print ("manager acquired...\n"); + + /* Process initial objects */ + initial_objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager)); + for (l = initial_objects; l; l = g_list_next (l)) { + MMObject *obj; + MMModemLocation *location; + + obj = MM_OBJECT (l->data); + location = mm_object_peek_modem_location (obj); + if (location) + track_location (mm_object_peek_modem (obj), location); + } + g_list_free_full (initial_objects, g_object_unref); + + /* Connect for interface updates */ + g_signal_connect (manager, + "interface-added", + G_CALLBACK (interface_added_cb), + NULL); + g_signal_connect (manager, + "interface-removed", + G_CALLBACK (interface_removed_cb), + NULL); +} + +static void +bus_get_ready (GObject *source, + GAsyncResult *res) +{ + GError *error = NULL; + + connection = g_bus_get_finish (res, &error); + if (!connection) { + g_printerr ("error: couldn't get bus: %s\n", + error ? error->message : "unknown error"); + exit (EXIT_FAILURE); + } + + g_print ("system bus acquired...\n"); + + mm_manager_new (connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + NULL, /* cancellable */ + (GAsyncReadyCallback) manager_new_ready, + NULL); +} + +gint +main (gint argc, gchar **argv) +{ + GOptionContext *context; + + setlocale (LC_ALL, ""); + + g_type_init (); + + /* Setup option context, process it and destroy it */ + context = g_option_context_new ("- Watch location through ModemManager"); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free (context); + + if (with_getter_flag && with_property_flag) { + g_printerr ("error: cannot use --with-getter and --with-property at the same time\n"); + return EXIT_FAILURE; + } + + /* If none explicitly given, assume getter */ + if (!with_getter_flag && !with_property_flag) + with_getter_flag = TRUE; + + /* Setup signals */ + signal (SIGINT, signals_handler); + signal (SIGHUP, signals_handler); + signal (SIGTERM, signals_handler); + + /* Setup tracking table (key: path, value: location interface) */ + location_db = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) location_info_free); + + /* Setup dbus connection to use */ + g_bus_get (G_BUS_TYPE_SYSTEM, + NULL, /* cancellable */ + (GAsyncReadyCallback) bus_get_ready, + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + /* Clean exit */ + g_main_loop_unref (loop); + g_object_unref (connection); + g_hash_table_unref (location_db); + return EXIT_SUCCESS; +} |