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 <aleksandermj@google.com>
This commit is contained in:
Aleksander Morgado
2025-06-18 22:10:54 +00:00
parent 0b2d7d6388
commit 7d4784ed28
3 changed files with 57 additions and 26 deletions
+2 -21
View File
@@ -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;
+53 -5
View File
@@ -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,
+2
View File
@@ -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 */