diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2012-03-05 15:20:21 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-03-16 14:53:20 +0100 |
commit | 6e753996293bc0c397b896f7db5fad328237adc5 (patch) | |
tree | 19483dd9d1600083daf5672ae70439ace57e6694 | |
parent | df4e7131240c960d1c351d21e82aad0810188580 (diff) |
iface-modem-time: load network timezone information
Following the same logic as in the original implementation, we try to load the
network timezone information only after being registered, and also with up to N
retries. The async operation in charge of the loading can be cancelled
gracefully, i.e. if the interface is disabled before we get ever registered.
-rw-r--r-- | src/mm-iface-modem-time.c | 315 | ||||
-rw-r--r-- | src/mm-iface-modem-time.h | 14 |
2 files changed, 324 insertions, 5 deletions
diff --git a/src/mm-iface-modem-time.c b/src/mm-iface-modem-time.c index 8352048a..a7763fff 100644 --- a/src/mm-iface-modem-time.c +++ b/src/mm-iface-modem-time.c @@ -20,11 +20,16 @@ #include "mm-iface-modem-time.h" #include "mm-log.h" -#define SUPPORT_CHECKED_TAG "time-support-checked-tag" -#define SUPPORTED_TAG "time-supported-tag" +#define SUPPORT_CHECKED_TAG "time-support-checked-tag" +#define SUPPORTED_TAG "time-supported-tag" +#define NETWORK_TIMEZONE_CANCELLABLE_TAG "time-network-timezone-cancellable" static GQuark support_checked_quark; static GQuark supported_quark; +static GQuark network_timezone_cancellable_quark; + +#define TIMEZONE_POLL_INTERVAL_SEC 5 +#define TIMEZONE_POLL_RETRIES 6 /*****************************************************************************/ @@ -36,11 +41,250 @@ mm_iface_modem_time_bind_simple_status (MMIfaceModemTime *self, /*****************************************************************************/ +typedef struct { + MMIfaceModemTime *self; + GSimpleAsyncResult *result; + GCancellable *cancellable; + gulong cancelled_id; + gulong state_changed_id; + guint network_timezone_poll_id; + guint network_timezone_poll_retries; +} UpdateNetworkTimezoneContext; + +static gboolean timezone_poll_cb (UpdateNetworkTimezoneContext *ctx); + +static void +update_network_timezone_context_complete_and_free (UpdateNetworkTimezoneContext *ctx) +{ + g_simple_async_result_complete (ctx->result); + g_object_unref (ctx->result); + g_object_unref (ctx->cancellable); + g_object_unref (ctx->self); + g_free (ctx); +} + +static gboolean +update_network_timezone_finish (MMIfaceModemTime *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +cancelled (GCancellable *cancellable, + UpdateNetworkTimezoneContext *ctx) +{ + /* If waiting to get registered, disconnect signal */ + if (ctx->state_changed_id) + g_signal_handler_disconnect (ctx->self, + ctx->state_changed_id); + + /* If waiting in the timeout loop, remove the timeout */ + else if (ctx->network_timezone_poll_id) + g_source_remove (ctx->network_timezone_poll_id); + + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_CANCELLED, + "Network timezone loading cancelled"); + update_network_timezone_context_complete_and_free (ctx); +} + +static void +update_network_timezone_dictionary (MMIfaceModemTime *self, + MMNetworkTimezone *tz) +{ + MmGdbusModemTime *skeleton = NULL; + GVariant *dictionary; + + g_object_get (self, + MM_IFACE_MODEM_TIME_DBUS_SKELETON, &skeleton, + NULL); + g_assert (skeleton != NULL); + + dictionary = mm_network_timezone_get_dictionary (tz); + mm_gdbus_modem_time_set_network_timezone (skeleton, dictionary); + if (dictionary) + g_variant_unref (dictionary); + + g_object_unref (skeleton); +} + +static void +load_network_timezone_ready (MMIfaceModemTime *self, + GAsyncResult *res, + UpdateNetworkTimezoneContext *ctx) +{ + GError *error = NULL; + MMNetworkTimezone *tz; + + if (g_cancellable_is_cancelled (ctx->cancellable)) { + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_CANCELLED, + "Finished network timezone loading, " + "but cancelled meanwhile"); + update_network_timezone_context_complete_and_free (ctx); + return; + } + + /* Finish the async operation */ + tz = MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone_finish (self, + res, + &error); + if (error) { + /* Retry? */ + ctx->network_timezone_poll_retries--; + + /* Fatal if no more retries */ + if (ctx->network_timezone_poll_retries == 0) { + g_simple_async_result_take_error (ctx->result, error); + update_network_timezone_context_complete_and_free (ctx); + return; + } + + /* Otherwise, reconnect cancellable and relaunch timeout to query a bit + * later */ + ctx->cancelled_id = g_cancellable_connect (ctx->cancellable, + G_CALLBACK (cancelled), + ctx, + NULL); + ctx->network_timezone_poll_id = g_timeout_add_seconds (TIMEZONE_POLL_INTERVAL_SEC, + (GSourceFunc)timezone_poll_cb, + ctx); + + g_error_free (error); + return; + } + + /* Got final result properly, update the property in the skeleton */ + update_network_timezone_dictionary (ctx->self, tz); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + update_network_timezone_context_complete_and_free (ctx); + g_object_unref (tz); +} + +static gboolean +timezone_poll_cb (UpdateNetworkTimezoneContext *ctx) +{ + ctx->network_timezone_poll_id = 0; + + /* Before we launch the async loading of the network timezone, + * we disconnect the cancellable signal. We don't want to get + * signaled while waiting to finish this async method, we'll + * check the cancellable afterwards instead. */ + g_cancellable_disconnect (ctx->cancellable, + ctx->cancelled_id); + ctx->cancelled_id = 0; + + MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->load_network_timezone ( + ctx->self, + (GAsyncReadyCallback)load_network_timezone_ready, + ctx); + + return FALSE; +} + +static void +start_timezone_poll (UpdateNetworkTimezoneContext *ctx) +{ + /* Setup loop to query current timezone, don't do it right away. + * Note that we're passing the context reference to the loop. */ + ctx->network_timezone_poll_retries = TIMEZONE_POLL_RETRIES; + ctx->network_timezone_poll_id = g_timeout_add_seconds (TIMEZONE_POLL_INTERVAL_SEC, + (GSourceFunc)timezone_poll_cb, + ctx); +} + +static void +state_changed (MMIfaceModemTime *self, + GParamSpec *spec, + UpdateNetworkTimezoneContext *ctx) +{ + MMModemState state = MM_MODEM_STATE_UNKNOWN; + + g_object_get (self, + MM_IFACE_MODEM_STATE, &state, + NULL); + + /* We're waiting to get registered */ + if (state < MM_MODEM_STATE_REGISTERED) + return; + + /* Got registered, disconnect signal */ + if (ctx->state_changed_id) { + g_signal_handler_disconnect (self, + ctx->state_changed_id); + ctx->state_changed_id = 0; + } + + /* Once we know we're registered, start timezone poll */ + start_timezone_poll (ctx); +} + +static void +update_network_timezone (MMIfaceModemTime *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + UpdateNetworkTimezoneContext *ctx; + MMModemState state = MM_MODEM_STATE_UNKNOWN; + + /* If loading network timezone not supported, just finish here */ + if (!MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone || + !MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone_finish) { + g_simple_async_report_error_in_idle (G_OBJECT (self), + callback, + user_data, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Loading network timezone is not supported"); + return; + } + + ctx = g_new0 (UpdateNetworkTimezoneContext, 1); + ctx->self = g_object_ref (self); + ctx->cancellable = g_object_ref (cancellable); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + update_network_timezone); + + /* Note: we don't expect to get cancelled by any other thread, so no + * need to check if we're cancelled just after connecting to the + * cancelled signal */ + ctx->cancelled_id = g_cancellable_connect (ctx->cancellable, + G_CALLBACK (cancelled), + ctx, + NULL); + + g_object_get (self, + MM_IFACE_MODEM_STATE, &state, + NULL); + + /* Already registered? */ + if (state >= MM_MODEM_STATE_REGISTERED) { + /* Once we know we're registered, start timezone poll */ + start_timezone_poll (ctx); + } else { + /* Want to get notified when modem state changes */ + ctx->state_changed_id = g_signal_connect (ctx->self, + "notify::" MM_IFACE_MODEM_STATE, + G_CALLBACK (state_changed), + ctx); + } +} + +/*****************************************************************************/ + typedef struct _DisablingContext DisablingContext; static void interface_disabling_step (DisablingContext *ctx); typedef enum { DISABLING_STEP_FIRST, + DISABLING_STEP_CANCEL_NETWORK_TIMEZONE_UPDATE, DISABLING_STEP_LAST } DisablingStep; @@ -102,6 +346,26 @@ interface_disabling_step (DisablingContext *ctx) /* Fall down to next step */ ctx->step++; + case DISABLING_STEP_CANCEL_NETWORK_TIMEZONE_UPDATE: { + if (G_LIKELY (network_timezone_cancellable_quark)) { + GCancellable *cancellable = NULL; + + cancellable = g_object_get_qdata (G_OBJECT (ctx->self), + network_timezone_cancellable_quark); + + /* If network timezone loading is currently running, abort it */ + if (cancellable) { + g_cancellable_cancel (cancellable); + g_object_set_qdata (G_OBJECT (ctx->self), + network_timezone_cancellable_quark, + NULL); + } + } + + /* Fall down to next step */ + ctx->step++; + } + case DISABLING_STEP_LAST: /* We are done without errors! */ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); @@ -129,6 +393,7 @@ static void interface_enabling_step (EnablingContext *ctx); typedef enum { ENABLING_STEP_FIRST, + ENABLING_STEP_SETUP_NETWORK_TIMEZONE_RETRIEVAL, ENABLING_STEP_LAST } EnablingStep; @@ -183,6 +448,26 @@ mm_iface_modem_time_enable_finish (MMIfaceModemTime *self, } static void +update_network_timezone_ready (MMIfaceModemTime *self, + GAsyncResult *res) +{ + GError *error = NULL; + + if (!update_network_timezone_finish (self, res, &error)) { + if (!g_error_matches (error, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED)) + mm_dbg ("Couldn't update network timezone: '%s'", error->message); + g_error_free (error); + } + + /* Cleanup our cancellable in the context */ + g_object_set_qdata (G_OBJECT (self), + network_timezone_cancellable_quark, + NULL); +} + +static void interface_enabling_step (EnablingContext *ctx) { switch (ctx->step) { @@ -190,6 +475,32 @@ interface_enabling_step (EnablingContext *ctx) /* Fall down to next step */ ctx->step++; + case ENABLING_STEP_SETUP_NETWORK_TIMEZONE_RETRIEVAL: { + GCancellable *cancellable; + + /* We'll create a cancellable which is valid as long as we're updating + * network timezone, and we set it as context */ + cancellable = g_cancellable_new (); + if (G_UNLIKELY (!network_timezone_cancellable_quark)) + network_timezone_cancellable_quark = (g_quark_from_static_string ( + NETWORK_TIMEZONE_CANCELLABLE_TAG)); + g_object_set_qdata_full (G_OBJECT (ctx->self), + network_timezone_cancellable_quark, + cancellable, + (GDestroyNotify)g_object_unref); + + update_network_timezone (ctx->self, + cancellable, + (GAsyncReadyCallback)update_network_timezone_ready, + NULL); + + /* NOTE!!!! We'll leave the timezone network update operation + * running, we don't wait for it to finish */ + + /* Fall down to next step */ + ctx->step++; + } + case ENABLING_STEP_LAST: /* We are done without errors! */ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); diff --git a/src/mm-iface-modem-time.h b/src/mm-iface-modem-time.h index c9e82f36..013d5ab9 100644 --- a/src/mm-iface-modem-time.h +++ b/src/mm-iface-modem-time.h @@ -35,9 +35,17 @@ struct _MMIfaceModemTime { void (* check_support) (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data); - gboolean (*check_support_finish) (MMIfaceModemTime *self, - GAsyncResult *res, - GError **error); + gboolean (* check_support_finish) (MMIfaceModemTime *self, + GAsyncResult *res, + GError **error); + + /* Loading of the network timezone property */ + void (* load_network_timezone) (MMIfaceModemTime *self, + GAsyncReadyCallback callback, + gpointer user_data); + MMNetworkTimezone * (* load_network_timezone_finish) (MMIfaceModemTime *self, + GAsyncResult *res, + GError **error); }; GType mm_iface_modem_time_get_type (void); |