iface-modem: allow cancellability during unlock required checks

The process doing the unlock required checks may need a lot of retries
e.g. to decide whether a SIM card is available or not. If we do a
quick SIM eject, so the unlock required check starts looping, and then
insert the SIM again, we expect the loop to be cancelled right away,
so that the new modem object can be reprobed without any interference
from the old modem object.

We now take the modem-wide cancellable and bind it to the GTask in
mm_iface_modem_update_lock_info(), and we pass it down to every
sub-step of the async logic in the operation. We also plug the
cancellable to the delayed retries in the interface logic, to allow
aborting the checks right away
This commit is contained in:
Aleksander Morgado
2023-12-01 10:14:45 +00:00
committed by Aleksander Morgado
parent dc4b1ec0f3
commit 5fb2856645
5 changed files with 143 additions and 70 deletions
+16 -17
View File
@@ -1518,12 +1518,12 @@ load_unlock_required_context_free (LoadUnlockRequiredContext *ctx)
}
static MMModemLock
modem_load_unlock_required_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
modem_load_unlock_required_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
GError *inner_error = NULL;
gssize value;
gssize value;
value = g_task_propagate_int (G_TASK (res), &inner_error);
if (inner_error) {
@@ -1534,9 +1534,9 @@ modem_load_unlock_required_finish (MMIfaceModem *self,
}
static void
pin_query_ready (MbimDevice *device,
pin_query_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
GTask *task)
{
MbimMessage *response;
GError *error = NULL;
@@ -1575,8 +1575,6 @@ pin_query_ready (MbimDevice *device,
mbim_message_unref (response);
}
static gboolean wait_for_sim_ready (GTask *task);
static void
unlock_required_subscriber_ready_state_ready (MbimDevice *device,
GAsyncResult *res,
@@ -1714,7 +1712,7 @@ unlock_required_subscriber_ready_state_ready (MbimDevice *device,
mbim_device_command (device,
message,
10,
NULL,
g_task_get_cancellable (task),
(GAsyncReadyCallback)pin_query_ready,
task);
mbim_message_unref (message);
@@ -1740,7 +1738,7 @@ wait_for_sim_ready (GTask *task)
mbim_device_command (ctx->device,
message,
10,
NULL,
g_task_get_cancellable (task),
(GAsyncReadyCallback)unlock_required_subscriber_ready_state_ready,
task);
mbim_message_unref (message);
@@ -1748,14 +1746,15 @@ wait_for_sim_ready (GTask *task)
}
static void
modem_load_unlock_required (MMIfaceModem *self,
gboolean last_attempt,
GAsyncReadyCallback callback,
gpointer user_data)
modem_load_unlock_required (MMIfaceModem *self,
gboolean last_attempt,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
LoadUnlockRequiredContext *ctx;
MbimDevice *device;
GTask *task;
MbimDevice *device;
GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
@@ -1764,7 +1763,7 @@ modem_load_unlock_required (MMIfaceModem *self,
ctx->device = g_object_ref (device);
ctx->last_attempt = last_attempt;
task = g_task_new (self, NULL, callback, user_data);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)load_unlock_required_context_free);
wait_for_sim_ready (task);
+22 -16
View File
@@ -863,9 +863,9 @@ typedef struct {
} LoadUnlockRequiredContext;
static MMModemLock
modem_load_unlock_required_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
modem_load_unlock_required_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
GError *inner_error = NULL;
gssize value;
@@ -883,7 +883,7 @@ static void load_unlock_required_context_step (GTask *task);
static void
unlock_required_uim_get_card_status_ready (QmiClientUim *client,
GAsyncResult *res,
GTask *task)
GTask *task)
{
MMBroadbandModemQmi *self;
LoadUnlockRequiredContext *ctx;
@@ -928,7 +928,7 @@ unlock_required_uim_get_card_status_ready (QmiClientUim *client,
static void
dms_uim_get_pin_status_ready (QmiClientDms *client,
GAsyncResult *res,
GTask *task)
GTask *task)
{
MMBroadbandModemQmi *self;
LoadUnlockRequiredContext *ctx;
@@ -1025,14 +1025,19 @@ dms_uim_get_pin_status_ready (QmiClientDms *client,
static void
load_unlock_required_context_step (GTask *task)
{
MMBroadbandModemQmi *self;
MMBroadbandModemQmi *self;
LoadUnlockRequiredContext *ctx;
GError *error = NULL;
QmiClient *client;
GError *error = NULL;
QmiClient *client;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
switch (ctx->step) {
case LOAD_UNLOCK_REQUIRED_STEP_FIRST:
ctx->step++;
@@ -1066,7 +1071,7 @@ load_unlock_required_context_step (GTask *task)
qmi_client_dms_uim_get_pin_status (QMI_CLIENT_DMS (client),
NULL,
5,
NULL,
g_task_get_cancellable (task),
(GAsyncReadyCallback) dms_uim_get_pin_status_ready,
task);
return;
@@ -1090,7 +1095,7 @@ load_unlock_required_context_step (GTask *task)
qmi_client_uim_get_card_status (QMI_CLIENT_UIM (client),
NULL,
5,
NULL,
g_task_get_cancellable (task),
(GAsyncReadyCallback) unlock_required_uim_get_card_status_ready,
task);
return;
@@ -1101,19 +1106,20 @@ load_unlock_required_context_step (GTask *task)
}
static void
modem_load_unlock_required (MMIfaceModem *self,
gboolean last_attempt,
GAsyncReadyCallback callback,
gpointer user_data)
modem_load_unlock_required (MMIfaceModem *self,
gboolean last_attempt,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
LoadUnlockRequiredContext *ctx;
GTask *task;
GTask *task;
ctx = g_new0 (LoadUnlockRequiredContext, 1);
ctx->step = LOAD_UNLOCK_REQUIRED_STEP_FIRST;
ctx->last_attempt = last_attempt;
task = g_task_new (self, NULL, callback, user_data);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, g_free);
load_unlock_required_context_step (task);
+6 -5
View File
@@ -1529,14 +1529,15 @@ cpin_query_ready (MMIfaceModem *self,
}
static void
modem_load_unlock_required (MMIfaceModem *self,
gboolean last_attempt,
GAsyncReadyCallback callback,
gpointer user_data)
modem_load_unlock_required (MMIfaceModem *self,
gboolean last_attempt,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
task = g_task_new (self, cancellable, callback, user_data);
/* CDMA-only modems don't need this */
if (mm_iface_modem_is_cdma_only (self)) {
+84 -18
View File
@@ -571,11 +571,20 @@ mm_iface_modem_abort_invocation_if_state_not_reached (MMIfaceModem *sel
#define UNLOAD_REQUIRED_RETRY_TIMEOUT_SECS 2
typedef struct {
guint retries;
guint max_retries;
guint timeout_id;
guint retries;
guint max_retries;
guint timeout_id;
gulong cancellable_id;
} InternalLoadUnlockRequiredContext;
static void
internal_load_unlock_required_context_free (InternalLoadUnlockRequiredContext *ctx)
{
g_assert (!ctx->timeout_id);
g_assert (!ctx->cancellable_id);
g_slice_free (InternalLoadUnlockRequiredContext, ctx);
}
static MMModemLock
internal_load_unlock_required_finish (MMIfaceModem *self,
GAsyncResult *res,
@@ -601,11 +610,40 @@ load_unlock_required_again (GTask *task)
ctx = g_task_get_task_data (task);
ctx->timeout_id = 0;
g_assert (ctx->cancellable_id);
g_cancellable_disconnect (g_task_get_cancellable (task), ctx->cancellable_id);
ctx->cancellable_id = 0;
/* Retry the step */
internal_load_unlock_required_context_step (task);
return G_SOURCE_REMOVE;
}
static void
load_unlock_required_again_cancelled (GCancellable *cancellable,
GTask *task)
{
InternalLoadUnlockRequiredContext *ctx;
MMIfaceModem *self;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
ctx->cancellable_id = 0;
if (ctx->timeout_id) {
g_source_remove (ctx->timeout_id);
ctx->timeout_id = 0;
}
mm_obj_dbg (self, "unlock required check retries cancelled");
if (!g_task_return_error_if_cancelled (task))
g_assert_not_reached ();
g_object_unref (task);
}
static void
load_unlock_required_ready (MMIfaceModem *self,
GAsyncResult *res,
@@ -655,6 +693,18 @@ load_unlock_required_ready (MMIfaceModem *self,
else
mm_obj_info (self, "retrying (%u/%u) unlock required check", ctx->retries, ctx->max_retries);
/* Ownership of the task will be shared between the timeout and the cancellable. As soon as one
* of them is triggered, it should cancel the other. */
g_assert (ctx->cancellable_id == 0);
ctx->cancellable_id = g_cancellable_connect (g_task_get_cancellable (task),
(GCallback) load_unlock_required_again_cancelled,
task,
NULL);
/* Do nothing if already cancelled, the callback will already be called */
if (!ctx->cancellable_id)
return;
g_assert (ctx->timeout_id == 0);
ctx->timeout_id = g_timeout_add_seconds (UNLOAD_REQUIRED_RETRY_TIMEOUT_SECS,
(GSourceFunc)load_unlock_required_again,
@@ -686,10 +736,18 @@ internal_load_unlock_required_context_step (GTask *task)
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
/* Don't run a new check if we were already cancelled */
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
g_assert (ctx->cancellable_id == 0);
g_assert (ctx->timeout_id == 0);
MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required (
self,
(ctx->retries >= ctx->max_retries), /* last_attempt? */
g_task_get_cancellable (task),
(GAsyncReadyCallback) load_unlock_required_ready,
task);
}
@@ -708,17 +766,18 @@ load_unlock_required_max_retries (MMIfaceModem *self)
static void
internal_load_unlock_required (MMIfaceModem *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
InternalLoadUnlockRequiredContext *ctx;
GTask *task;
ctx = g_new0 (InternalLoadUnlockRequiredContext, 1);
ctx->max_retries = load_unlock_required_max_retries (self);
task = g_task_new (self, cancellable, callback, user_data);
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, g_free);
ctx = g_slice_new0 (InternalLoadUnlockRequiredContext);
ctx->max_retries = load_unlock_required_max_retries (self);
g_task_set_task_data (task, ctx, (GDestroyNotify)internal_load_unlock_required_context_free);
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required ||
!MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish) {
@@ -3725,10 +3784,9 @@ typedef struct {
static void
update_lock_info_context_free (UpdateLockInfoContext *ctx)
{
g_assert (ctx->saved_error == NULL);
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
/* saved error may exist if we were cancelled */
g_clear_pointer (&ctx->saved_error, g_error_free);
g_clear_object (&ctx->skeleton);
g_slice_free (UpdateLockInfoContext, ctx);
}
@@ -3855,12 +3913,18 @@ internal_load_unlock_required_ready (MMIfaceModem *self,
static void
update_lock_info_context_step (GTask *task)
{
MMIfaceModem *self;
MMIfaceModem *self;
UpdateLockInfoContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
if (g_task_return_error_if_cancelled (task)) {
mm_obj_dbg (self, "lock info update cancelled");
g_object_unref (task);
return;
}
switch (ctx->step) {
case UPDATE_LOCK_INFO_CONTEXT_STEP_FIRST:
/* We need the skeleton around */
@@ -3881,6 +3945,7 @@ update_lock_info_context_step (GTask *task)
/* If we're already unlocked, we're done */
internal_load_unlock_required (
self,
g_task_get_cancellable (task),
(GAsyncReadyCallback)internal_load_unlock_required_ready,
task);
return;
@@ -3946,20 +4011,20 @@ update_lock_info_context_step (GTask *task)
}
void
mm_iface_modem_update_lock_info (MMIfaceModem *self,
MMModemLock known_lock,
GAsyncReadyCallback callback,
gpointer user_data)
mm_iface_modem_update_lock_info (MMIfaceModem *self,
MMModemLock known_lock,
GAsyncReadyCallback callback,
gpointer user_data)
{
UpdateLockInfoContext *ctx;
GTask *task;
GTask *task;
ctx = g_slice_new0 (UpdateLockInfoContext);
/* If the given lock is known, we will avoid re-asking for it */
ctx->lock = known_lock;
task = g_task_new (self, NULL, callback, user_data);
task = g_task_new (self, mm_base_modem_peek_cancellable (MM_BASE_MODEM (self)), callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)update_lock_info_context_free);
g_object_get (self,
@@ -4683,6 +4748,7 @@ interface_syncing_step (GTask *task)
MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required (
self,
FALSE,
NULL,
(GAsyncReadyCallback)sync_sim_lock_ready,
task);
return;
+15 -14
View File
@@ -122,13 +122,14 @@ struct _MMIfaceModem {
GError **error);
/* Loading of the UnlockRequired property */
void (*load_unlock_required) (MMIfaceModem *self,
gboolean last_attempt,
GAsyncReadyCallback callback,
gpointer user_data);
MMModemLock (*load_unlock_required_finish) (MMIfaceModem *self,
GAsyncResult *res,
GError **error);
void (*load_unlock_required) (MMIfaceModem *self,
gboolean last_attempt,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
MMModemLock (*load_unlock_required_finish) (MMIfaceModem *self,
GAsyncResult *res,
GError **error);
/* Loading of the UnlockRetries property */
void (*load_unlock_retries) (MMIfaceModem *self,
@@ -516,13 +517,13 @@ gboolean mm_iface_modem_set_power_state_finish (MMIfaceModem *self,
* It will not only return the lock status, but also set the property values
* in the DBus interface. If 'known_lock' is given, that lock status will be
* assumed. */
void mm_iface_modem_update_lock_info (MMIfaceModem *self,
MMModemLock known_lock,
GAsyncReadyCallback callback,
gpointer user_data);
MMModemLock mm_iface_modem_update_lock_info_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error);
void mm_iface_modem_update_lock_info (MMIfaceModem *self,
MMModemLock known_lock,
GAsyncReadyCallback callback,
gpointer user_data);
MMModemLock mm_iface_modem_update_lock_info_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error);
MMModemLock mm_iface_modem_get_unlock_required (MMIfaceModem *self);
MMUnlockRetries *mm_iface_modem_get_unlock_retries (MMIfaceModem *self);