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.
This commit is contained in:
@@ -22,9 +22,14 @@
|
|||||||
|
|
||||||
#define SUPPORT_CHECKED_TAG "time-support-checked-tag"
|
#define SUPPORT_CHECKED_TAG "time-support-checked-tag"
|
||||||
#define SUPPORTED_TAG "time-supported-tag"
|
#define SUPPORTED_TAG "time-supported-tag"
|
||||||
|
#define NETWORK_TIMEZONE_CANCELLABLE_TAG "time-network-timezone-cancellable"
|
||||||
|
|
||||||
static GQuark support_checked_quark;
|
static GQuark support_checked_quark;
|
||||||
static GQuark supported_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;
|
typedef struct _DisablingContext DisablingContext;
|
||||||
static void interface_disabling_step (DisablingContext *ctx);
|
static void interface_disabling_step (DisablingContext *ctx);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DISABLING_STEP_FIRST,
|
DISABLING_STEP_FIRST,
|
||||||
|
DISABLING_STEP_CANCEL_NETWORK_TIMEZONE_UPDATE,
|
||||||
DISABLING_STEP_LAST
|
DISABLING_STEP_LAST
|
||||||
} DisablingStep;
|
} DisablingStep;
|
||||||
|
|
||||||
@@ -102,6 +346,26 @@ interface_disabling_step (DisablingContext *ctx)
|
|||||||
/* Fall down to next step */
|
/* Fall down to next step */
|
||||||
ctx->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:
|
case DISABLING_STEP_LAST:
|
||||||
/* We are done without errors! */
|
/* We are done without errors! */
|
||||||
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
|
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
|
||||||
@@ -129,6 +393,7 @@ static void interface_enabling_step (EnablingContext *ctx);
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ENABLING_STEP_FIRST,
|
ENABLING_STEP_FIRST,
|
||||||
|
ENABLING_STEP_SETUP_NETWORK_TIMEZONE_RETRIEVAL,
|
||||||
ENABLING_STEP_LAST
|
ENABLING_STEP_LAST
|
||||||
} EnablingStep;
|
} EnablingStep;
|
||||||
|
|
||||||
@@ -182,6 +447,26 @@ mm_iface_modem_time_enable_finish (MMIfaceModemTime *self,
|
|||||||
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
|
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
static void
|
||||||
interface_enabling_step (EnablingContext *ctx)
|
interface_enabling_step (EnablingContext *ctx)
|
||||||
{
|
{
|
||||||
@@ -190,6 +475,32 @@ interface_enabling_step (EnablingContext *ctx)
|
|||||||
/* Fall down to next step */
|
/* Fall down to next step */
|
||||||
ctx->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:
|
case ENABLING_STEP_LAST:
|
||||||
/* We are done without errors! */
|
/* We are done without errors! */
|
||||||
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
|
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
|
||||||
|
@@ -38,6 +38,14 @@ struct _MMIfaceModemTime {
|
|||||||
gboolean (* check_support_finish) (MMIfaceModemTime *self,
|
gboolean (* check_support_finish) (MMIfaceModemTime *self,
|
||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
GError **error);
|
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);
|
GType mm_iface_modem_time_get_type (void);
|
||||||
|
Reference in New Issue
Block a user