Merge remote-tracking branch 'cros/upstream' into 'cros/master'
diff --git a/cli/mmcli.c b/cli/mmcli.c
index f507753..7349b21 100644
--- a/cli/mmcli.c
+++ b/cli/mmcli.c
@@ -392,7 +392,7 @@
     } else if (mmcli_modem_messaging_options_enabled ()) {
         mmcli_modem_messaging_shutdown ();
     } else if (mmcli_modem_voice_options_enabled ()) {
-           mmcli_modem_voice_shutdown ();
+        mmcli_modem_voice_shutdown ();
     } else if (mmcli_modem_time_options_enabled ()) {
         mmcli_modem_time_shutdown ();
     } else if (mmcli_modem_firmware_options_enabled ()) {
diff --git a/libmm-glib/mm-common-helpers.c b/libmm-glib/mm-common-helpers.c
index 7bd845b..f99e58d 100644
--- a/libmm-glib/mm-common-helpers.c
+++ b/libmm-glib/mm-common-helpers.c
@@ -219,6 +219,12 @@
     return mm_common_sms_storages_array_to_variant (NULL, 0);
 }
 
+static void
+clear_modem_port_info (MMModemPortInfo *info)
+{
+    g_free (info->name);
+}
+
 GArray *
 mm_common_ports_variant_to_garray (GVariant *variant)
 {
@@ -232,6 +238,7 @@
 
         if (n > 0) {
             array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemPortInfo), n);
+            g_array_set_clear_func (array, (GDestroyNotify) clear_modem_port_info);
             for (i = 0; i < n; i++) {
                 MMModemPortInfo info;
 
diff --git a/libmm-glib/mm-modem-simple.c b/libmm-glib/mm-modem-simple.c
index 910f06b..9338c45 100644
--- a/libmm-glib/mm-modem-simple.c
+++ b/libmm-glib/mm-modem-simple.c
@@ -171,6 +171,7 @@
                                 "g-object-path",    bearer_path,
                                 "g-interface-name", "org.freedesktop.ModemManager1.Bearer",
                                 NULL);
+    g_free (bearer_path);
 }
 
 /**
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.c b/plugins/cinterion/mm-modem-helpers-cinterion.c
index 4f52761..e9c1e0d 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.c
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.c
@@ -247,43 +247,6 @@
  *   +CNMI: (0,1,2),(0,1),(0,2),(0),(1)
  */
 
-static GArray *
-read_number_list (const gchar *str)
-{
-    GError *inner_error = NULL;
-    GArray *out = NULL;
-    GRegex *r;
-    GMatchInfo *match_info;
-
-    if (!str)
-        return NULL;
-
-    r = g_regex_new ("(\\d),?", G_REGEX_UNGREEDY, 0, NULL);
-    g_assert (r != NULL);
-
-    g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
-    while (!inner_error && g_match_info_matches (match_info)) {
-        guint aux;
-
-        if (mm_get_uint_from_match_info (match_info, 1, &aux)) {
-            if (!out)
-                out = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3);
-            g_array_append_val (out, aux);
-        }
-        g_match_info_next (match_info, &inner_error);
-    }
-
-    if (inner_error) {
-        mm_warn ("Unexpected error matching +CNMI response: '%s'", inner_error->message);
-        g_error_free (inner_error);
-    }
-
-    g_match_info_free (match_info);
-    g_regex_unref (r);
-
-    return out;
-}
-
 gboolean
 mm_cinterion_parse_cnmi_test (const gchar *response,
                               GArray **supported_mode,
@@ -296,6 +259,11 @@
     GRegex *r;
     GMatchInfo *match_info;
     GError *inner_error = NULL;
+    GArray *tmp_supported_mode = NULL;
+    GArray *tmp_supported_mt = NULL;
+    GArray *tmp_supported_bm = NULL;
+    GArray *tmp_supported_ds = NULL;
+    GArray *tmp_supported_bfr = NULL;
 
     if (!response) {
         g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response");
@@ -313,77 +281,76 @@
             gchar *str;
 
             str = mm_get_string_unquoted_from_match_info (match_info, 1);
-            *supported_mode = read_number_list (str);
+            tmp_supported_mode = mm_parse_uint_list (str, &inner_error);
             g_free (str);
+            if (inner_error)
+                goto out;
         }
         if (supported_mt) {
             gchar *str;
 
             str = mm_get_string_unquoted_from_match_info (match_info, 2);
-            *supported_mt = read_number_list (str);
+            tmp_supported_mt = mm_parse_uint_list (str, &inner_error);
             g_free (str);
+            if (inner_error)
+                goto out;
         }
         if (supported_bm) {
             gchar *str;
 
             str = mm_get_string_unquoted_from_match_info (match_info, 3);
-            *supported_bm = read_number_list (str);
+            tmp_supported_bm = mm_parse_uint_list (str, &inner_error);
             g_free (str);
+            if (inner_error)
+                goto out;
         }
         if (supported_ds) {
             gchar *str;
 
             str = mm_get_string_unquoted_from_match_info (match_info, 4);
-            *supported_ds = read_number_list (str);
+            tmp_supported_ds = mm_parse_uint_list (str, &inner_error);
             g_free (str);
+            if (inner_error)
+                goto out;
         }
         if (supported_bfr) {
             gchar *str;
 
             str = mm_get_string_unquoted_from_match_info (match_info, 5);
-            *supported_bfr = read_number_list (str);
+            tmp_supported_bfr = mm_parse_uint_list (str, &inner_error);
             g_free (str);
+            if (inner_error)
+                goto out;
         }
     }
 
+out:
+
     if (match_info)
         g_match_info_free (match_info);
     g_regex_unref (r);
 
-    if ((supported_mode && *supported_mode == NULL) ||
-        (supported_mt   && *supported_mt == NULL) ||
-        (supported_bm   && *supported_bm == NULL) ||
-        (supported_ds   && *supported_ds == NULL) ||
-        (supported_bfr  && *supported_bfr == NULL))
-        inner_error = g_error_new (MM_CORE_ERROR,
-                                   MM_CORE_ERROR_FAILED,
-                                   "Error parsing +CNMI=? response");
-
     if (inner_error) {
-        if (supported_mode && *supported_mode) {
-            g_array_unref (*supported_mode);
-            *supported_mode = NULL;
-        }
-        if (supported_mt && *supported_mt) {
-            g_array_unref (*supported_mt);
-            *supported_mt = NULL;
-        }
-        if (supported_bm && *supported_bm) {
-            g_array_unref (*supported_bm);
-            *supported_bm = NULL;
-        }
-        if (supported_ds && *supported_ds) {
-            g_array_unref (*supported_ds);
-            *supported_ds = NULL;
-        }
-        if (supported_bfr && *supported_bfr) {
-            g_array_unref (*supported_bfr);
-            *supported_bfr = NULL;
-        }
+        g_clear_pointer (&tmp_supported_mode, g_array_unref);
+        g_clear_pointer (&tmp_supported_mt,   g_array_unref);
+        g_clear_pointer (&tmp_supported_bm,   g_array_unref);
+        g_clear_pointer (&tmp_supported_ds,   g_array_unref);
+        g_clear_pointer (&tmp_supported_bfr,  g_array_unref);
         g_propagate_error (error, inner_error);
         return FALSE;
     }
 
+    if (supported_mode)
+        *supported_mode = tmp_supported_mode;
+    if (supported_mt)
+        *supported_mt = tmp_supported_mt;
+    if (supported_bm)
+        *supported_bm = tmp_supported_bm;
+    if (supported_ds)
+        *supported_ds = tmp_supported_ds;
+    if (supported_bfr)
+        *supported_bfr = tmp_supported_bfr;
+
     return TRUE;
 }
 
diff --git a/plugins/cinterion/tests/test-modem-helpers-cinterion.c b/plugins/cinterion/tests/test-modem-helpers-cinterion.c
index 022fec3..e31a672 100644
--- a/plugins/cinterion/tests/test-modem-helpers-cinterion.c
+++ b/plugins/cinterion/tests/test-modem-helpers-cinterion.c
@@ -330,6 +330,55 @@
     g_array_unref (expected_bfr);
 }
 
+static void
+test_cnmi_other (void)
+{
+    GArray *expected_mode;
+    GArray *expected_mt;
+    GArray *expected_bm;
+    GArray *expected_ds;
+    GArray *expected_bfr;
+    guint val;
+    const gchar *response =
+        "+CNMI: (0-3),(0,1),(0,2,3),(0,2),(1)\r\n"
+        "\r\n";
+
+    expected_mode = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3);
+    val = 0, g_array_append_val (expected_mode, val);
+    val = 1, g_array_append_val (expected_mode, val);
+    val = 2, g_array_append_val (expected_mode, val);
+    val = 3, g_array_append_val (expected_mode, val);
+
+    expected_mt = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2);
+    val = 0, g_array_append_val (expected_mt, val);
+    val = 1, g_array_append_val (expected_mt, val);
+
+    expected_bm = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2);
+    val = 0, g_array_append_val (expected_bm, val);
+    val = 2, g_array_append_val (expected_bm, val);
+    val = 3, g_array_append_val (expected_bm, val);
+
+    expected_ds = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
+    val = 0, g_array_append_val (expected_ds, val);
+    val = 2, g_array_append_val (expected_ds, val);
+
+    expected_bfr = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
+    val = 1, g_array_append_val (expected_bfr, val);
+
+    common_test_cnmi (response,
+                      expected_mode,
+                      expected_mt,
+                      expected_bm,
+                      expected_ds,
+                      expected_bfr);
+
+    g_array_unref (expected_mode);
+    g_array_unref (expected_mt);
+    g_array_unref (expected_bm);
+    g_array_unref (expected_ds);
+    g_array_unref (expected_bfr);
+}
+
 /*****************************************************************************/
 /* Test ^SWWAN read */
 
@@ -548,6 +597,7 @@
     g_test_add_func ("/MM/cinterion/scfg/response/2g",        test_scfg_response_2g);
     g_test_add_func ("/MM/cinterion/scfg/response/2g/ucs2",   test_scfg_response_2g_ucs2);
     g_test_add_func ("/MM/cinterion/cnmi/phs8",               test_cnmi_phs8);
+    g_test_add_func ("/MM/cinterion/cnmi/other",              test_cnmi_other);
     g_test_add_func ("/MM/cinterion/swwan/pls8",              test_swwan_pls8);
     g_test_add_func ("/MM/cinterion/sind/response/simstatus", test_sind_response_simstatus);
 
diff --git a/src/mm-device.c b/src/mm-device.c
index 6750a0b..803e64d 100644
--- a/src/mm-device.c
+++ b/src/mm-device.c
@@ -71,6 +71,7 @@
 
     /* The Modem object for this device */
     MMBaseModem *modem;
+    gulong       modem_valid_id;
 
     /* When exported, a reference to the object manager */
     GDBusObjectManagerServer *object_manager;
@@ -293,6 +294,20 @@
 
 /*****************************************************************************/
 
+static void
+clear_modem (MMDevice *self)
+{
+    if (self->priv->modem_valid_id) {
+        g_signal_handler_disconnect (self->priv->modem, self->priv->modem_valid_id);
+        self->priv->modem_valid_id = 0;
+    }
+
+    /* Run dispose before unref-ing, in order to cleanup the SIM object,
+     * if any (which also holds a reference to the modem object) */
+    g_object_run_dispose (G_OBJECT (self->priv->modem));
+    g_clear_object (&(self->priv->modem));
+}
+
 void
 mm_device_remove_modem (MMDevice  *self)
 {
@@ -300,11 +315,7 @@
         return;
 
     unexport_modem (self);
-
-    /* Run dispose before unref-ing, in order to cleanup the SIM object,
-     * if any (which also holds a reference to the modem object) */
-    g_object_run_dispose (G_OBJECT (self->priv->modem));
-    g_clear_object (&(self->priv->modem));
+    clear_modem (self);
     g_clear_object (&(self->priv->object_manager));
 }
 
@@ -391,10 +402,10 @@
         self->priv->object_manager = g_object_ref (object_manager);
 
         /* We want to get notified when the modem becomes valid/invalid */
-        g_signal_connect (self->priv->modem,
-                          "notify::" MM_BASE_MODEM_VALID,
-                          G_CALLBACK (modem_valid),
-                          self);
+        self->priv->modem_valid_id = g_signal_connect (self->priv->modem,
+                                                       "notify::" MM_BASE_MODEM_VALID,
+                                                       G_CALLBACK (modem_valid),
+                                                       self);
     }
 
     return !!self->priv->modem;
@@ -582,7 +593,7 @@
         self->priv->plugin = g_value_dup_object (value);
         break;
     case PROP_MODEM:
-        g_clear_object (&(self->priv->modem));
+        clear_modem (self);
         self->priv->modem = g_value_dup_object (value);
         break;
     case PROP_HOTPLUGGED:
@@ -635,7 +646,8 @@
     g_clear_object (&(self->priv->plugin));
     g_list_free_full (self->priv->port_probes, (GDestroyNotify)g_object_unref);
     g_list_free_full (self->priv->ignored_port_probes, (GDestroyNotify)g_object_unref);
-    g_clear_object (&(self->priv->modem));
+
+    clear_modem (self);
 
     G_OBJECT_CLASS (mm_device_parent_class)->dispose (object);
 }