mm-iface-modem: add check_for_sim_swap method and enable steps

When in low-power mode, some modems will not dispatch unsolicited
notifications, such as for SIM hot swapping. There is code in
MMBroadbandModemTelit to handle this by checking the SIM identifier
during modem power up against the identifier cached in the SIM
D-Bus object. If they're different, the SIM has likely been
swapped while we were powered down.

We can move this code out to MMBroadbandModem because it doesn't
actually rely on any Telit-specific details, and invoke it from
MMIfaceModem via a new method.

BUG=b:69392264

Change-Id: Iae0eab94a5330a625067b19e992eee3aec1e2fce
Reviewed-on: https://chromium-review.googlesource.com/832384
Reviewed-by: Ben Chan <benchan@chromium.org>
Commit-Queue: Eric Caruso <ejcaruso@chromium.org>
Tested-by: Eric Caruso <ejcaruso@chromium.org>
diff --git a/plugins/telit/mm-broadband-modem-telit.c b/plugins/telit/mm-broadband-modem-telit.c
index 995ae6a..08917ca 100644
--- a/plugins/telit/mm-broadband-modem-telit.c
+++ b/plugins/telit/mm-broadband-modem-telit.c
@@ -911,20 +911,6 @@
 
 /*****************************************************************************/
 /* Modem after power up (Modem interface) */
-typedef enum {
-    AFTER_POWER_UP_STEP_FIRST,
-    AFTER_POWER_UP_STEP_GET_SIM_IDENTIFIER,
-    AFTER_POWER_UP_STEP_ENABLE_QSS_PARSING,
-    AFTER_POWER_UP_STEP_LAST
-} ModemAfterPowerUpStep;
-
-typedef struct {
-    gboolean has_sim_changed;
-    guint retries;
-    ModemAfterPowerUpStep step;
-} AfterPowerUpContext;
-
-static void after_power_up_step (GTask *task);
 
 static gboolean
 modem_after_power_up_finish (MMIfaceModem *self,
@@ -935,122 +921,20 @@
 }
 
 static void
-load_sim_identifier_ready (MMBaseSim *sim,
-                           GAsyncResult *res,
-                           GTask *task)
-{
-    AfterPowerUpContext *ctx;
-    GError *error = NULL;
-    gchar *current_simid;
-    const gchar *cached_simid;
-
-    ctx = g_task_get_task_data (task);
-
-    cached_simid = (gchar *)mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (sim));
-    current_simid = mm_base_sim_load_sim_identifier_finish (sim, res, &error);
-
-    if (error) {
-        if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
-            mm_warn ("could not load SIM identifier: %s", error->message);
-            g_clear_error (&error);
-            goto out;
-        }
-
-        if (ctx->retries-- > 0) {
-            mm_warn ("could not load SIM identifier: %s (%d retries left)",
-                     error->message, ctx->retries);
-            g_clear_error (&error);
-            g_timeout_add_seconds (1, (GSourceFunc) after_power_up_step, task);
-            return;
-        }
-
-        mm_warn ("could not load SIM identifier: %s", error->message);
-        g_clear_error (&error);
-        goto out;
-    }
-
-    if (g_strcmp0 (current_simid, cached_simid) != 0) {
-        mm_warn ("sim identifier has changed: possible SIM swap during power down/low");
-        ctx->has_sim_changed = TRUE;
-    }
-
-out:
-    g_free (current_simid);
-    ctx->step++;
-    after_power_up_step (task);
-}
-
-static void
-after_power_up_step (GTask *task)
-{
-    AfterPowerUpContext *ctx;
-    MMBroadbandModemTelit *self;
-
-    ctx = g_task_get_task_data (task);
-    self = g_task_get_source_object (task);
-
-    switch (ctx->step) {
-    case AFTER_POWER_UP_STEP_FIRST:
-        /* Fall back on next step */
-        ctx->step++;
-
-    case AFTER_POWER_UP_STEP_GET_SIM_IDENTIFIER: {
-        MMBaseSim *sim = NULL;
-
-        g_object_get (MM_BROADBAND_MODEM_TELIT (self),
-                      MM_IFACE_MODEM_SIM, &sim,
-                      NULL);
-        if (!sim) {
-            g_task_return_new_error (task,
-                                     MM_CORE_ERROR,
-                                     MM_CORE_ERROR_FAILED,
-                                     "could not acquire sim object");
-            g_object_unref (task);
-            return;
-        }
-
-        mm_base_sim_load_sim_identifier (sim,
-                                         (GAsyncReadyCallback)load_sim_identifier_ready,
-                                         task);
-        g_object_unref (sim);
-        return;
-    }
-
-    case AFTER_POWER_UP_STEP_ENABLE_QSS_PARSING:
-        mm_dbg ("Stop ignoring #QSS");
-        self->priv->parse_qss = TRUE;
-
-        /* Fall back on next step */
-        ctx->step++;
-    case AFTER_POWER_UP_STEP_LAST:
-        if (ctx->has_sim_changed)
-            mm_broadband_modem_update_sim_hot_swap_detected (MM_BROADBAND_MODEM (self));
-
-        g_task_return_boolean (task, TRUE);
-        g_object_unref (task);
-        break;
-    default:
-        g_assert_not_reached ();
-    }
-}
-
-static void
 modem_after_power_up (MMIfaceModem *self,
                       GAsyncReadyCallback callback,
                       gpointer user_data)
 {
     GTask *task;
-    AfterPowerUpContext *ctx;
-
-    ctx = g_new0 (AfterPowerUpContext, 1);
-    ctx->step = AFTER_POWER_UP_STEP_FIRST;
-    ctx->has_sim_changed = FALSE;
-    ctx->retries = 3;
+    MMBroadbandModemTelit *modem = MM_BROADBAND_MODEM_TELIT (self);
 
     task = g_task_new (self, NULL, callback, user_data);
-    g_task_set_task_data (task, ctx, (GDestroyNotify) g_free);
 
-    after_power_up_step (task);
+    mm_dbg ("Stop ignoring #QSS");
+    modem->priv->parse_qss = TRUE;
+
+    g_task_return_boolean (task, TRUE);
+    g_object_unref (task);
 }
 
 /*****************************************************************************/
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index 3d96372..7cf50c6 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -3347,6 +3347,132 @@
 }
 
 /*****************************************************************************/
+/* Reprobing the modem if the SIM changed across a power-off or power-down */
+
+typedef struct {
+    MMBaseSim *sim;
+    guint retries;
+} SimSwapContext;
+
+static gboolean load_sim_identifier (GTask *task);
+
+static void
+sim_swap_context_free (SimSwapContext *ctx)
+{
+    g_clear_object (&ctx->sim);
+    g_slice_free (SimSwapContext, ctx);
+}
+
+static gboolean
+modem_check_for_sim_swap_finish (MMIfaceModem *self,
+                                 GAsyncResult *res,
+                                 GError **error)
+{
+    return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+load_sim_identifier_ready (MMBaseSim *sim,
+                           GAsyncResult *res,
+                           GTask *task)
+{
+    MMBroadbandModem *self;
+    SimSwapContext *ctx;
+    const gchar *cached_simid;
+    gchar *current_simid;
+    GError *error = NULL;
+
+    self = MM_BROADBAND_MODEM (g_task_get_source_object (task));
+    ctx = g_task_get_task_data (task);
+    cached_simid = mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (sim));
+    current_simid = MM_BASE_SIM_GET_CLASS (sim)->load_sim_identifier_finish (sim, res, &error);
+
+    if (error) {
+        if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+            g_task_return_error (task, error);
+            goto out;
+        }
+
+        if (ctx->retries > 0) {
+            mm_warn ("could not load SIM identifier: %s (%d retries left)",
+                     error->message, ctx->retries);
+            --ctx->retries;
+            g_clear_error (&error);
+            g_timeout_add_seconds (1, (GSourceFunc) load_sim_identifier, task);
+            return;
+        }
+
+        mm_warn ("could not load SIM identifier: %s", error->message);
+        g_task_return_error (task, error);
+        goto out;
+    }
+
+    if (g_strcmp0 (current_simid, cached_simid) != 0) {
+        mm_info ("sim identifier has changed: possible SIM swap during power down/low");
+        mm_broadband_modem_update_sim_hot_swap_detected (self);
+    }
+
+    g_task_return_boolean (task, TRUE);
+
+out:
+    g_free (current_simid);
+    g_object_unref (task);
+}
+
+static gboolean
+load_sim_identifier (GTask *task)
+{
+    SimSwapContext *ctx = g_task_get_task_data (task);
+
+    MM_BASE_SIM_GET_CLASS (ctx->sim)->load_sim_identifier (
+        ctx->sim,
+        (GAsyncReadyCallback)load_sim_identifier_ready,
+        task);
+
+    return G_SOURCE_REMOVE;
+}
+
+static void
+modem_check_for_sim_swap (MMIfaceModem *self,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+    GTask *task;
+    SimSwapContext *ctx;
+
+    mm_dbg ("Checking if SIM was swapped...");
+
+    task = g_task_new (self, NULL, callback, user_data);
+    ctx = g_slice_new0 (SimSwapContext);
+    ctx->retries = 3;
+    g_task_set_task_data (task, ctx, (GDestroyNotify)sim_swap_context_free);
+
+    g_object_get (self,
+                  MM_IFACE_MODEM_SIM, &ctx->sim,
+                  NULL);
+    if (!ctx->sim) {
+        g_task_return_new_error (task,
+                                 MM_CORE_ERROR,
+                                 MM_CORE_ERROR_FAILED,
+                                 "could not acquire sim object");
+        g_object_unref (task);
+        return;
+    }
+
+    if (!MM_BASE_SIM_GET_CLASS (ctx->sim)->load_sim_identifier ||
+        !MM_BASE_SIM_GET_CLASS (ctx->sim)->load_sim_identifier_finish) {
+        g_task_return_new_error (task,
+                                 MM_CORE_ERROR,
+                                 MM_CORE_ERROR_FAILED,
+                                 "sim identifier could not be loaded");
+        g_object_unref (task);
+        return;
+    }
+
+    load_sim_identifier (task);
+}
+
+/*****************************************************************************/
 /* Sending a command to the modem (Modem interface) */
 
 static const gchar *
@@ -10757,6 +10883,8 @@
     /* Enabling steps */
     iface->modem_power_up = modem_power_up;
     iface->modem_power_up_finish = modem_power_up_finish;
+    iface->check_for_sim_swap = modem_check_for_sim_swap;
+    iface->check_for_sim_swap_finish = modem_check_for_sim_swap_finish;
     iface->setup_flow_control = modem_setup_flow_control;
     iface->setup_flow_control_finish = modem_setup_flow_control_finish;
     iface->load_supported_charsets = modem_load_supported_charsets;
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index afb3be3..7236e30 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -3557,6 +3557,7 @@
 typedef enum {
     ENABLING_STEP_FIRST,
     ENABLING_STEP_SET_POWER_STATE,
+    ENABLING_STEP_CHECK_FOR_SIM_SWAP,
     ENABLING_STEP_FLOW_CONTROL,
     ENABLING_STEP_SUPPORTED_CHARSETS,
     ENABLING_STEP_CHARSET,
@@ -3607,6 +3608,25 @@
 }
 
 static void
+check_for_sim_swap_ready (MMIfaceModem *self,
+                          GAsyncResult *res,
+                          GTask *task)
+{
+    EnablingContext *ctx;
+    GError *error = NULL;
+
+    if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish (self, res, &error)) {
+        mm_warn ("Error checking if SIM was swapped: '%s'", error->message);
+        g_error_free (error);
+    }
+
+    /* Go on to next step */
+    ctx = g_task_get_task_data (task);
+    ctx->step++;
+    interface_enabling_step (task);
+}
+
+static void
 setup_flow_control_ready (MMIfaceModem *self,
                           GAsyncResult *res,
                           GTask *task)
@@ -3709,6 +3729,18 @@
                                         task);
         return;
 
+    case ENABLING_STEP_CHECK_FOR_SIM_SWAP:
+        if (MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap &&
+            MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish) {
+            MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap (
+                self,
+                (GAsyncReadyCallback)check_for_sim_swap_ready,
+                task);
+            return;
+        }
+        /* Fall down to next step */
+        ctx->step++;
+
     case ENABLING_STEP_FLOW_CONTROL:
         if (MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control &&
             MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control_finish) {
diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h
index d25fbd4..177bf91 100644
--- a/src/mm-iface-modem.h
+++ b/src/mm-iface-modem.h
@@ -281,6 +281,16 @@
                                              GAsyncResult *res,
                                              GError **error);
 
+    /* Asynchronous check to see if the SIM was swapped.
+     * Useful for when the modem changes power states since we might
+     * not get the relevant notifications from the modem. */
+    void (*check_for_sim_swap) (MMIfaceModem *self,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data);
+    gboolean (*check_for_sim_swap_finish) (MMIfaceModem *self,
+                                           GAsyncResult *res,
+                                           GError **error);
+
     /* Asynchronous flow control setup */
     void (*setup_flow_control) (MMIfaceModem *self,
                                 GAsyncReadyCallback callback,