From 7d4784ed28cfd4da0532a9e9d0ec02c6751003fc Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Wed, 18 Jun 2025 21:03:49 +0000 Subject: [PATCH] iface-modem: ignore SIM events during a controlled SIM slot switch If there is an ongoing SIM slot switch, we must ignore all SIM hotswap related events happening during the process. The flag that is set in the Modem interface private info to track this is never reset back to FALSE on a successful SIM slot switch, it will be set for as long as the modem object exists. After the successful SIM slot switch, a new modem reprobe will be requested, which leads to the old modem object to be disposed (the one that had the flag set) and a new modem object created. The logic that already existed in the MBIM modem object is now generalized in the Modem interface itself so that SIM events reported via other protocols are also properly ignored. Signed-off-by: Aleksander Morgado --- src/mm-broadband-modem-mbim.c | 23 ++------------ src/mm-iface-modem.c | 58 ++++++++++++++++++++++++++++++++--- src/mm-iface-modem.h | 2 ++ 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c index a7dc8dca..ee11d223 100644 --- a/src/mm-broadband-modem-mbim.c +++ b/src/mm-broadband-modem-mbim.c @@ -191,7 +191,6 @@ struct _MMBroadbandModemMbimPrivate { /* Multi-SIM support */ guint32 executor_index; guint active_slot_index; - gboolean pending_sim_slot_switch_action; MMUnlockRetries *unlock_retries; }; @@ -5511,7 +5510,7 @@ basic_connect_notification_subscriber_ready_status (MMBroadbandModemMbim *self, g_autoptr(GError) error = NULL; gboolean active_sim_event = FALSE; - if (self->priv->pending_sim_slot_switch_action) { + if (mm_iface_modem_is_primary_sim_slot_switch_ongoing (MM_IFACE_MODEM (self))) { mm_obj_dbg (self, "ignoring slot status change"); return; } @@ -6216,7 +6215,7 @@ ms_basic_connect_extensions_notification_slot_info_status (MMBroadbandModemMbim return; } - if (self->priv->pending_sim_slot_switch_action) { + if (mm_iface_modem_is_primary_sim_slot_switch_ongoing (MM_IFACE_MODEM (self))) { mm_obj_dbg (self, "ignoring slot status change in SIM slot %d: %s", slot_index + 1, mbim_uicc_slot_state_get_string (slot_state)); return; } @@ -10009,8 +10008,6 @@ set_device_slot_mappings_ready (MbimDevice *device, self = g_task_get_source_object (task); - g_assert (self->priv->pending_sim_slot_switch_action); - /* the slot index in MM starts at 1 */ slot_number = GPOINTER_TO_UINT (g_task_get_task_data (task)) - 1; @@ -10021,7 +10018,6 @@ set_device_slot_mappings_ready (MbimDevice *device, &map_count, &slot_mappings, &error)) { - self->priv->pending_sim_slot_switch_action = FALSE; g_task_return_error (task, error); g_object_unref (task); return; @@ -10030,13 +10026,9 @@ set_device_slot_mappings_ready (MbimDevice *device, for (i = 0; i < map_count; i++) { if (i == self->priv->executor_index) { if (slot_number != slot_mappings[i]->slot) { - self->priv->pending_sim_slot_switch_action = FALSE; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "SIM slot switch to '%u' failed", slot_number); } else { - /* Keep pending_sim_slot_switch_action flag TRUE to cleanly ignore SIM related indications - * during slot switching, We don't want SIM related indications received trigger the update - * of SimSlots property, which may not be what we want as the modem object is being shutdown */ self->priv->active_slot_index = slot_number + 1; g_task_return_boolean (task, TRUE); } @@ -10046,7 +10038,6 @@ set_device_slot_mappings_ready (MbimDevice *device, } } - self->priv->pending_sim_slot_switch_action = FALSE; g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "Can't find executor index '%u'", self->priv->executor_index); g_object_unref (task); @@ -10098,16 +10089,6 @@ before_set_query_device_slot_mappings_ready (MbimDevice *device, } } - /* Flag a pending SIM slot switch operation, so that we can ignore slot state updates - * during the process. */ - if (self->priv->pending_sim_slot_switch_action) { - g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, - "there is already an ongoing SIM slot switch operation"); - g_object_unref (task); - return; - } - self->priv->pending_sim_slot_switch_action = TRUE; - for (i = 0; i < map_count; i++) { if (i == self->priv->executor_index) slot_mappings[i]->slot = slot_number; diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c index 1af2666b..0e14d77c 100644 --- a/src/mm-iface-modem.c +++ b/src/mm-iface-modem.c @@ -92,6 +92,10 @@ typedef struct { /* Timer that tracks when the last power operation request was * performed, so that we can throttle the requests to the modem. */ GTimer *power_state_timer; + + /* Flag indicating whether a primary SIM slot switch operation is + * ongoing */ + gboolean ongoing_primary_sim_slot_switch; } Private; static void @@ -350,10 +354,10 @@ after_sim_event_disable_ready (MMBaseModem *self, mm_base_modem_set_valid (self, FALSE); } -void -mm_iface_modem_process_sim_event (MMIfaceModem *self) +static void +iface_modem_process_sim_event_internal (MMIfaceModem *self) { - mm_obj_info (self, "Processing SIM event"); + mm_obj_info (self, "processing SIM event"); if (MM_IFACE_MODEM_GET_IFACE (self)->cleanup_sim_hot_swap) MM_IFACE_MODEM_GET_IFACE (self)->cleanup_sim_hot_swap (self); @@ -369,6 +373,19 @@ mm_iface_modem_process_sim_event (MMIfaceModem *self) NULL); } +void +mm_iface_modem_process_sim_event (MMIfaceModem *self) +{ + Private *priv; + + priv = get_private (self); + if (priv->ongoing_primary_sim_slot_switch) { + mm_obj_info (self, "ignoring SIM event during a SIM slot switch"); + return; + } + iface_modem_process_sim_event_internal (self); +} + /*****************************************************************************/ void @@ -1351,6 +1368,17 @@ handle_list_bearers (MmGdbusModem *skeleton, /*****************************************************************************/ +gboolean +mm_iface_modem_is_primary_sim_slot_switch_ongoing (MMIfaceModem *self) +{ + Private *priv; + + priv = get_private (self); + return priv->ongoing_primary_sim_slot_switch; +} + +/*****************************************************************************/ + typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; @@ -1372,24 +1400,34 @@ set_primary_sim_slot_ready (MMIfaceModem *self, GAsyncResult *res, HandleSetPrimarySimSlotContext *ctx) { - g_autoptr(GError) error = NULL; + g_autoptr(GError) error = NULL; + Private *priv; + + priv = get_private (self); + g_assert (priv->ongoing_primary_sim_slot_switch); if (!MM_IFACE_MODEM_GET_IFACE (self)->set_primary_sim_slot_finish (self, res, &error)) { /* If the implementation returns EXISTS, we're already in the requested SIM slot, * so we can safely return a success on the operation and skip the reprobing */ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS)) { mm_obj_warn (self, "failed setting primary SIM slot '%u': %s", ctx->requested_sim_slot, error->message); + priv->ongoing_primary_sim_slot_switch = FALSE; mm_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&error)); handle_set_primary_sim_slot_context_free (ctx); return; } mm_obj_dbg (self, "ignored request to set primary SIM slot '%u': already set", ctx->requested_sim_slot); + priv->ongoing_primary_sim_slot_switch = FALSE; } else { /* Notify about the SIM swap, which will disable and reprobe the device. * There is no need to update the PrimarySimSlot property, as this value will be * reloaded automatically during the reprobe. */ mm_obj_info (self, "primary SIM slot '%u' set", ctx->requested_sim_slot); - mm_iface_modem_process_sim_event (self); + /* Process SIM event without checking the ongoing_primary_sim_slot_switch flag, as + * we are the ones who set it in this operation. This flag is kept enabled for as + * long as the modem object exists! Once the reprobe happens, a new modem object + * will be created. */ + iface_modem_process_sim_event_internal (self); } mm_gdbus_modem_complete_set_primary_sim_slot (ctx->skeleton, ctx->invocation); @@ -1401,6 +1439,7 @@ handle_set_primary_sim_slot_auth_ready (MMIfaceAuth *self, GAsyncResult *res, HandleSetPrimarySimSlotContext *ctx) { + Private *priv; GError *error = NULL; const gchar *const *sim_slot_paths; @@ -1428,7 +1467,16 @@ handle_set_primary_sim_slot_auth_ready (MMIfaceAuth *self, return; } + priv = get_private (MM_IFACE_MODEM (self)); + if (priv->ongoing_primary_sim_slot_switch) { + mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, + "SIM slot switch operation already ongoing."); + handle_set_primary_sim_slot_context_free (ctx); + return; + } + mm_obj_info (self, "processing user request to set primary SIM slot '%u'...", ctx->requested_sim_slot); + priv->ongoing_primary_sim_slot_switch = TRUE; MM_IFACE_MODEM_GET_IFACE (self)->set_primary_sim_slot (MM_IFACE_MODEM (self), ctx->requested_sim_slot, (GAsyncReadyCallback)set_primary_sim_slot_ready, diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h index 18c7e9ea..4bd6ac19 100644 --- a/src/mm-iface-modem.h +++ b/src/mm-iface-modem.h @@ -620,4 +620,6 @@ void mm_iface_modem_modify_sim (MMIfaceModem *self, void mm_iface_modem_process_sim_event (MMIfaceModem *self); +gboolean mm_iface_modem_is_primary_sim_slot_switch_ongoing (MMIfaceModem *self); + #endif /* MM_IFACE_MODEM_H */