diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index 7ee68d37a7639db2f58eeb3b85dcb999ec903092..7c7250e692e052e26b99f6350032856996ed22c2 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -157,6 +157,8 @@ struct _MMBroadbandModemPrivate {
     MM3gppCmerMode modem_cmer_enable_mode;
     MM3gppCmerMode modem_cmer_disable_mode;
     MM3gppCmerInd modem_cmer_ind;
+    gboolean modem_cgerep_support_checked;
+    gboolean modem_cgerep_supported;
     MMFlowControl flow_control;
 
     /*<--- Modem 3GPP interface --->*/
@@ -2662,6 +2664,269 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
     return g_task_propagate_boolean (G_TASK (res), error);
 }
 
+static void
+bearer_report_disconnected (MMBaseBearer *bearer,
+                            gpointer      user_data)
+{
+    guint cid;
+
+    cid = GPOINTER_TO_UINT (user_data);
+
+    /* If we're told to disconnect a single context and this is not the
+     * bearer associated to that context, ignore operation */
+    if (cid > 0 &&
+        MM_IS_BROADBAND_BEARER (bearer) &&
+        mm_broadband_bearer_get_3gpp_cid (MM_BROADBAND_BEARER (bearer)) != cid)
+        return;
+
+    /* If already disconnected, ignore operation */
+    if (mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_DISCONNECTED)
+        return;
+
+    mm_info ("Bearer %s: explicitly disconnected", mm_base_bearer_get_path (bearer));
+    mm_base_bearer_report_connection_status (bearer, MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+}
+
+static void
+bearer_list_report_disconnections (MMBroadbandModem *self,
+                                   guint             cid)
+{
+    MMBearerList *list = NULL;
+
+    g_object_get (self,
+                  MM_IFACE_MODEM_BEARER_LIST, &list,
+                  NULL);
+
+    /* If empty bearer list, nothing else to do */
+    if (!list)
+        return;
+
+    mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_report_disconnected, GUINT_TO_POINTER (cid));
+    g_object_unref (list);
+}
+
+static void
+cgev_process_detach (MMBroadbandModem *self,
+                     MM3gppCgev        type)
+{
+    switch (type) {
+    case MM_3GPP_CGEV_NW_DETACH:
+        mm_info ("network forced PS detach: all contexts have been deactivated");
+        bearer_list_report_disconnections (self, 0);
+        break;
+    case MM_3GPP_CGEV_ME_DETACH:
+        mm_info ("mobile equipment forced PS detach: all contexts have been deactivated");
+        bearer_list_report_disconnections (self, 0);
+        break;
+    default:
+        g_assert_not_reached ();
+    }
+}
+
+static void
+cgev_process_primary (MMBroadbandModem *self,
+                      MM3gppCgev        type,
+                      const gchar      *str)
+{
+    GError *error = NULL;
+    guint   cid = 0;
+
+    if (!mm_3gpp_parse_cgev_indication_primary (str, type, &cid, &error)) {
+        mm_warn ("couldn't parse cid info from +CGEV indication '%s': %s", str, error->message);
+        g_error_free (error);
+        return;
+    }
+
+    switch (type) {
+    case MM_3GPP_CGEV_NW_ACT_PRIMARY:
+        mm_info ("network request to activate context (cid %u)", cid);
+        break;
+    case MM_3GPP_CGEV_ME_ACT_PRIMARY:
+        mm_info ("mobile equipment request to activate context (cid %u)", cid);
+        break;
+    case MM_3GPP_CGEV_NW_DEACT_PRIMARY:
+        mm_info ("network request to deactivate context (cid %u)", cid);
+        bearer_list_report_disconnections (self, cid);
+        break;
+    case MM_3GPP_CGEV_ME_DEACT_PRIMARY:
+        mm_info ("mobile equipment request to deactivate context (cid %u)", cid);
+        bearer_list_report_disconnections (self, cid);
+        break;
+    default:
+        g_assert_not_reached ();
+        break;
+    }
+}
+
+static void
+cgev_process_secondary (MMBroadbandModem *self,
+                        MM3gppCgev        type,
+                        const gchar      *str)
+{
+    GError *error = NULL;
+    guint   p_cid = 0;
+    guint   cid = 0;
+
+    if (!mm_3gpp_parse_cgev_indication_secondary (str, type, &p_cid, &cid, NULL, &error)) {
+        mm_warn ("couldn't parse p_cid/cid info from +CGEV indication '%s': %s", str, error->message);
+        g_error_free (error);
+        return;
+    }
+
+    switch (type) {
+    case MM_3GPP_CGEV_NW_ACT_SECONDARY:
+        mm_info ("network request to activate secondary context (cid %u, primary cid %u)", cid, p_cid);
+        break;
+    case MM_3GPP_CGEV_ME_ACT_SECONDARY:
+        mm_info ("mobile equipment request to activate secondary context (cid %u, primary cid %u)", cid, p_cid);
+        break;
+    case MM_3GPP_CGEV_NW_DEACT_SECONDARY:
+        mm_info ("network request to deactivate secondary context (cid %u, primary cid %u)", cid, p_cid);
+        bearer_list_report_disconnections (self, cid);
+        break;
+    case MM_3GPP_CGEV_ME_DEACT_SECONDARY:
+        mm_info ("mobile equipment request to deactivate secondary context (cid %u, primary cid %u)", cid, p_cid);
+        bearer_list_report_disconnections (self, cid);
+        break;
+    default:
+        g_assert_not_reached ();
+        break;
+    }
+}
+
+static void
+cgev_process_pdp (MMBroadbandModem *self,
+                  MM3gppCgev        type,
+                  const gchar      *str)
+{
+    GError *error = NULL;
+    gchar  *pdp_type = NULL;
+    gchar  *pdp_addr = NULL;
+    guint   cid = 0;
+
+    if (!mm_3gpp_parse_cgev_indication_pdp (str, type, &pdp_type, &pdp_addr, &cid, &error)) {
+        mm_warn ("couldn't parse PDP info from +CGEV indication '%s': %s", str, error->message);
+        g_error_free (error);
+        return;
+    }
+
+    switch (type) {
+    case MM_3GPP_CGEV_REJECT:
+        mm_info ("network request to activate context (type %s, address %s) has been automatically rejected", pdp_type, pdp_addr);
+        break;
+    case MM_3GPP_CGEV_NW_REACT:
+        /* NOTE: we don't currently notify about automatic reconnections like this one */
+        if (cid)
+            mm_info ("network request to reactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid);
+        else
+            mm_info ("network request to reactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr);
+        break;
+    case MM_3GPP_CGEV_NW_DEACT_PDP:
+        if (cid) {
+            mm_info ("network request to deactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid);
+            bearer_list_report_disconnections (self, cid);
+        } else
+            mm_info ("network request to deactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr);
+        break;
+    case MM_3GPP_CGEV_ME_DEACT_PDP:
+        if (cid) {
+            mm_info ("mobile equipment request to deactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid);
+            bearer_list_report_disconnections (self, cid);
+        } else
+            mm_info ("mobile equipment request to deactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr);
+        break;
+    default:
+        g_assert_not_reached ();
+        break;
+    }
+
+    g_free (pdp_addr);
+    g_free (pdp_type);
+}
+
+static void
+cgev_received (MMPortSerialAt   *port,
+               GMatchInfo       *info,
+               MMBroadbandModem *self)
+{
+    gchar      *str;
+    MM3gppCgev  type;
+
+    str = mm_get_string_unquoted_from_match_info (info, 1);
+    if (!str)
+        return;
+
+    type = mm_3gpp_parse_cgev_indication_action (str);
+
+    switch (type) {
+    case MM_3GPP_CGEV_NW_DETACH:
+    case MM_3GPP_CGEV_ME_DETACH:
+        cgev_process_detach (self, type);
+        break;
+    case MM_3GPP_CGEV_NW_ACT_PRIMARY:
+    case MM_3GPP_CGEV_ME_ACT_PRIMARY:
+    case MM_3GPP_CGEV_NW_DEACT_PRIMARY:
+    case MM_3GPP_CGEV_ME_DEACT_PRIMARY:
+        cgev_process_primary (self, type, str);
+        break;
+    case MM_3GPP_CGEV_NW_ACT_SECONDARY:
+    case MM_3GPP_CGEV_ME_ACT_SECONDARY:
+    case MM_3GPP_CGEV_NW_DEACT_SECONDARY:
+    case MM_3GPP_CGEV_ME_DEACT_SECONDARY:
+        cgev_process_secondary (self, type, str);
+        break;
+    case MM_3GPP_CGEV_NW_DEACT_PDP:
+    case MM_3GPP_CGEV_ME_DEACT_PDP:
+    case MM_3GPP_CGEV_REJECT:
+    case MM_3GPP_CGEV_NW_REACT:
+        cgev_process_pdp (self, type, str);
+        break;
+    case MM_3GPP_CGEV_NW_CLASS:
+    case MM_3GPP_CGEV_ME_CLASS:
+    case MM_3GPP_CGEV_NW_MODIFY:
+    case MM_3GPP_CGEV_ME_MODIFY:
+        /* ignore */
+        break;
+    default:
+        mm_dbg ("unhandled +CGEV indication: %s", str);
+        break;
+    }
+
+    g_free (str);
+}
+
+static void
+set_cgev_unsolicited_events_handlers (MMBroadbandModem *self,
+                                      gboolean          enable)
+{
+    MMPortSerialAt *ports[2];
+    GRegex *cgev_regex;
+    guint i;
+
+    cgev_regex = mm_3gpp_cgev_regex_get ();
+    ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+    ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+    /* Enable unsolicited events in given port */
+    for (i = 0; i < 2; i++) {
+        if (!ports[i])
+            continue;
+
+        /* Set/unset unsolicited CGEV event handler */
+        mm_dbg ("(%s) %s 3GPP +CGEV unsolicited events handlers",
+                mm_port_get_device (MM_PORT (ports[i])),
+                enable ? "Setting" : "Removing");
+        mm_port_serial_at_add_unsolicited_msg_handler (
+            ports[i],
+            cgev_regex,
+            enable ? (MMPortSerialAtUnsolicitedMsgFn) cgev_received : NULL,
+            enable ? self : NULL,
+            NULL);
+    }
+
+    g_regex_unref (cgev_regex);
+}
+
 static void
 ciev_received (MMPortSerialAt *port,
                GMatchInfo *info,
@@ -2701,8 +2966,8 @@ ciev_received (MMPortSerialAt *port,
 }
 
 static void
-set_unsolicited_events_handlers (MMBroadbandModem *self,
-                                 gboolean enable)
+set_ciev_unsolicited_events_handlers (MMBroadbandModem *self,
+                                      gboolean          enable)
 {
     MMPortSerialAt *ports[2];
     GRegex *ciev_regex;
@@ -2718,7 +2983,7 @@ set_unsolicited_events_handlers (MMBroadbandModem *self,
             continue;
 
         /* Set/unset unsolicited CIEV event handler */
-        mm_dbg ("(%s) %s 3GPP unsolicited events handlers",
+        mm_dbg ("(%s) %s 3GPP +CIEV unsolicited events handlers",
                 mm_port_get_device (MM_PORT (ports[i])),
                 enable ? "Setting" : "Removing");
         mm_port_serial_at_add_unsolicited_msg_handler (
@@ -2733,9 +2998,51 @@ set_unsolicited_events_handlers (MMBroadbandModem *self,
 }
 
 static void
-cmer_format_check_ready (MMBroadbandModem   *self,
-                         GAsyncResult       *res,
-                         GTask *task)
+support_checked_setup_unsolicited_events (GTask *task)
+{
+    MMBroadbandModem *self;
+
+    self = g_task_get_source_object (task);
+
+    if (self->priv->modem_cind_supported)
+        set_ciev_unsolicited_events_handlers (self, TRUE);
+
+    if (self->priv->modem_cgerep_supported)
+        set_cgev_unsolicited_events_handlers (self, TRUE);
+
+    g_task_return_boolean (task, TRUE);
+    g_object_unref (task);
+}
+
+static void check_and_setup_3gpp_urc_support (GTask *task);
+
+static void
+cgerep_format_check_ready (MMBroadbandModem *self,
+                           GAsyncResult     *res,
+                           GTask            *task)
+{
+    GError *error = NULL;
+    const gchar *result;
+
+    result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+    if (!result) {
+        mm_dbg ("+CGEREP check failed, marking packet domain event reporting as unsupported: '%s'", error->message);
+        g_error_free (error);
+        goto out;
+    }
+
+    mm_dbg ("Modem supports packet domain event reporting");
+    self->priv->modem_cgerep_supported = TRUE;
+
+out:
+    /* go on with remaining checks */
+    check_and_setup_3gpp_urc_support (task);
+}
+
+static void
+cmer_format_check_ready (MMBroadbandModem *self,
+                         GAsyncResult     *res,
+                         GTask            *task)
 {
     MM3gppCmerMode  supported_modes = MM_3GPP_CMER_MODE_NONE;
     MM3gppCmerInd   supported_inds = MM_3GPP_CMER_IND_NONE;
@@ -2747,9 +3054,7 @@ cmer_format_check_ready (MMBroadbandModem   *self,
     if (error || !mm_3gpp_parse_cmer_test_response (result, &supported_modes, &supported_inds, &error)) {
         mm_dbg ("+CMER check failed, marking indications as unsupported: '%s'", error->message);
         g_error_free (error);
-        g_task_return_boolean (task, TRUE);
-        g_object_unref (task);
-        return;
+        goto out;
     }
 
     aux = mm_3gpp_cmer_mode_build_string_from_mask (supported_modes);
@@ -2789,17 +3094,15 @@ cmer_format_check_ready (MMBroadbandModem   *self,
     mm_dbg ("+CMER indication setting: %s", aux);
     g_free (aux);
 
-    /* Now, keep on setting up the ports */
-    set_unsolicited_events_handlers (self, TRUE);
-
-    g_task_return_boolean (task, TRUE);
-    g_object_unref (task);
+out:
+    /* go on with remaining checks */
+    check_and_setup_3gpp_urc_support (task);
 }
 
 static void
 cind_format_check_ready (MMBroadbandModem *self,
-                         GAsyncResult *res,
-                         GTask *task)
+                         GAsyncResult     *res,
+                         GTask            *task)
 {
     GHashTable *indicators = NULL;
     GError *error = NULL;
@@ -2812,8 +3115,8 @@ cind_format_check_ready (MMBroadbandModem *self,
         /* unsupported indications */
         mm_dbg ("+CIND check failed, marking indications as unsupported: '%s'", error->message);
         g_error_free (error);
-        g_task_return_boolean (task, TRUE);
-        g_object_unref (task);
+        /* go on with remaining checks */
+        check_and_setup_3gpp_urc_support (task);
         return;
     }
 
@@ -2866,16 +3169,13 @@ cind_format_check_ready (MMBroadbandModem *self,
 }
 
 static void
-modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *_self,
-                                     GAsyncReadyCallback callback,
-                                     gpointer user_data)
+check_and_setup_3gpp_urc_support (GTask *task)
 {
-    MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
-    GTask *task;
+    MMBroadbandModem *self;
 
-    task = g_task_new (self, NULL, callback, user_data);
+    self = g_task_get_source_object (task);
 
-    /* Load supported indicators */
+    /* Check support for +CIEV indications, managed with +CIND/+CMER */
     if (!self->priv->modem_cind_support_checked) {
         mm_dbg ("Checking indicator support...");
         self->priv->modem_cind_support_checked = TRUE;
@@ -2888,27 +3188,48 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *_self,
         return;
     }
 
-    /* If supported, go on */
-    if (self->priv->modem_cind_supported)
-        set_unsolicited_events_handlers (self, TRUE);
+    /* Check support for +CGEV indications, managed with +CGEREP */
+    if (!self->priv->modem_cgerep_support_checked) {
+        mm_dbg ("Checking packet domain event reporting...");
+        self->priv->modem_cgerep_support_checked = TRUE;
+        mm_base_modem_at_command (MM_BASE_MODEM (self),
+                                  "+CGEREP=?",
+                                  3,
+                                  TRUE,
+                                  (GAsyncReadyCallback)cgerep_format_check_ready,
+                                  task);
+        return;
+    }
 
-    g_task_return_boolean (task, TRUE);
-    g_object_unref (task);
+    support_checked_setup_unsolicited_events (task);
 }
 
 static void
-modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *_self,
-                                       GAsyncReadyCallback callback,
-                                       gpointer user_data)
+modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp    *self,
+                                     GAsyncReadyCallback  callback,
+                                     gpointer             user_data)
+{
+    GTask *task;
+
+    task = g_task_new (self, NULL, callback, user_data);
+    check_and_setup_3gpp_urc_support (task);
+}
+
+static void
+modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp    *_self,
+                                       GAsyncReadyCallback  callback,
+                                       gpointer             user_data)
 {
     MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
     GTask *task;
 
     task = g_task_new (self, NULL, callback, user_data);
 
-    /* If supported, go on */
     if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported)
-        set_unsolicited_events_handlers (self, FALSE);
+        set_ciev_unsolicited_events_handlers (self, FALSE);
+
+    if (self->priv->modem_cgerep_supported)
+        set_cgev_unsolicited_events_handlers (self, FALSE);
 
     g_task_return_boolean (task, TRUE);
     g_object_unref (task);
@@ -2918,16 +3239,26 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *_self,
 /* Enabling/disabling unsolicited events (3GPP interface) */
 
 typedef struct {
-    gchar *command;
-    gboolean enable;
-    gboolean cmer_primary_done;
-    gboolean cmer_secondary_done;
+    gboolean        enable;
+    MMPortSerialAt *primary;
+    MMPortSerialAt *secondary;
+    gchar          *cmer_command;
+    gboolean        cmer_primary_done;
+    gboolean        cmer_secondary_done;
+    gchar          *cgerep_command;
+    gboolean        cgerep_primary_done;
+    gboolean        cgerep_secondary_done;
 } UnsolicitedEventsContext;
 
 static void
 unsolicited_events_context_free (UnsolicitedEventsContext *ctx)
 {
-    g_free (ctx->command);
+    if (ctx->secondary)
+        g_object_unref (ctx->secondary);
+    if (ctx->primary)
+        g_object_unref (ctx->primary);
+    g_free (ctx->cgerep_command);
+    g_free (ctx->cmer_command);
     g_free (ctx);
 }
 
@@ -2943,53 +3274,70 @@ static void run_unsolicited_events_setup (GTask *task);
 
 static void
 unsolicited_events_setup_ready (MMBroadbandModem *self,
-                                GAsyncResult *res,
-                                GTask *task)
+                                GAsyncResult     *res,
+                                GTask            *task)
 {
     UnsolicitedEventsContext *ctx;
-    GError *error = NULL;
-
-    mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
-    if (!error) {
-        /* Run on next port, if any */
-        run_unsolicited_events_setup (task);
-        return;
-    }
+    GError                   *error = NULL;
 
     ctx = g_task_get_task_data (task);
 
-    mm_dbg ("Couldn't %s event reporting: '%s'",
-            ctx->enable ? "enable" : "disable",
-            error->message);
-    g_error_free (error);
-    /* Consider this operation complete, ignoring errors */
-    g_task_return_boolean (task, TRUE);
-    g_object_unref (task);
+    if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) {
+        mm_dbg ("Couldn't %s event reporting: '%s'",
+                ctx->enable ? "enable" : "disable",
+                error->message);
+        g_error_free (error);
+    }
+
+    /* Continue on next port/command */
+    run_unsolicited_events_setup (task);
 }
 
 static void
 run_unsolicited_events_setup (GTask *task)
 {
-    MMBroadbandModem *self;
+    MMBroadbandModem         *self;
     UnsolicitedEventsContext *ctx;
-    MMPortSerialAt *port = NULL;
+    MMPortSerialAt           *port = NULL;
+    const gchar              *command = NULL;
 
     self = g_task_get_source_object (task);
     ctx = g_task_get_task_data (task);
 
-    if (!ctx->cmer_primary_done) {
+    /* CMER on primary port */
+    if (!ctx->cmer_primary_done && ctx->cmer_command && ctx->primary) {
+        mm_dbg ("Enabling +CIND event reporting in primary port...");
         ctx->cmer_primary_done = TRUE;
-        port = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
-    } else if (!ctx->cmer_secondary_done) {
+        command = ctx->cmer_command;
+        port = ctx->primary;
+    }
+    /* CMER on secondary port */
+    else if (!ctx->cmer_secondary_done && ctx->cmer_command && ctx->secondary) {
+        mm_dbg ("Enabling +CIND event reporting in secondary port...");
         ctx->cmer_secondary_done = TRUE;
-        port = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+        command = ctx->cmer_command;
+        port = ctx->secondary;
+    }
+    /* CGEREP on primary port */
+    else if (!ctx->cgerep_primary_done && ctx->cgerep_command && ctx->primary) {
+        mm_dbg ("Enabling +CGEV event reporting in primary port...");
+        ctx->cgerep_primary_done = TRUE;
+        command = ctx->cgerep_command;
+        port = ctx->primary;
+    }
+    /* CGEREP on secondary port */
+    else if (!ctx->cgerep_secondary_done && ctx->cgerep_command && ctx->secondary) {
+        mm_dbg ("Enabling +CGEV event reporting in secondary port...");
+        ctx->cgerep_secondary_done = TRUE;
+        port = ctx->secondary;
+        command = ctx->cgerep_command;
     }
 
     /* Enable unsolicited events in given port */
-    if (port) {
+    if (port && command) {
         mm_base_modem_at_command_full (MM_BASE_MODEM (self),
                                        port,
-                                       ctx->command,
+                                       command,
                                        3,
                                        FALSE,
                                        FALSE, /* raw */
@@ -2999,7 +3347,7 @@ run_unsolicited_events_setup (GTask *task)
         return;
     }
 
-    /* If no more ports, we're fully done now */
+    /* Fully done now */
     g_task_return_boolean (task, TRUE);
     g_object_unref (task);
 }
@@ -3009,34 +3357,25 @@ modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *_self,
                                       GAsyncReadyCallback callback,
                                       gpointer user_data)
 {
-    MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
-    GTask *task;
+    MMBroadbandModem         *self = MM_BROADBAND_MODEM (_self);
+    GTask                    *task;
+    UnsolicitedEventsContext *ctx;
 
     task = g_task_new (self, NULL, callback, user_data);
 
-    /* If supported, go on */
-    if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) {
-        gchar *cmd;
-
-        /* If CMER command available, launch it */
-        cmd = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_enable_mode, self->priv->modem_cmer_ind);
-        if (cmd) {
-            UnsolicitedEventsContext *ctx;
-
-            ctx = g_new0 (UnsolicitedEventsContext, 1);
-            ctx->enable = TRUE;
-            ctx->command = cmd;
+    ctx = g_new0 (UnsolicitedEventsContext, 1);
+    ctx->enable = TRUE;
+    ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+    ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+    g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free);
 
-            g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free);
+    if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported)
+        ctx->cmer_command = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_enable_mode, self->priv->modem_cmer_ind);
 
-            run_unsolicited_events_setup (task);
-            return;
-        }
-        mm_dbg ("Skipping +CMER enable command: not supported");
-    }
+    if (self->priv->modem_cgerep_support_checked && self->priv->modem_cgerep_supported)
+        ctx->cgerep_command = g_strdup ("+CGEREP=2");
 
-    g_task_return_boolean (task, TRUE);
-    g_object_unref (task);
+    run_unsolicited_events_setup (task);
 }
 
 static void
@@ -3044,33 +3383,24 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *_self,
                                        GAsyncReadyCallback callback,
                                        gpointer user_data)
 {
-    MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
-    GTask *task;
+    MMBroadbandModem         *self = MM_BROADBAND_MODEM (_self);
+    GTask                    *task;
+    UnsolicitedEventsContext *ctx;
 
     task = g_task_new (self, NULL, callback, user_data);
 
-    /* If CIND supported, go on */
-    if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) {
-        gchar *cmd;
-
-        /* If CMER command available, launch it */
-        cmd = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_disable_mode, MM_3GPP_CMER_IND_NONE);
-        if (cmd) {
-            UnsolicitedEventsContext *ctx;
-
-            ctx = g_new0 (UnsolicitedEventsContext, 1);
-            ctx->command = cmd;
+    ctx = g_new0 (UnsolicitedEventsContext, 1);
+    ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+    ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+    g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free);
 
-            g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free);
+    if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported)
+        ctx->cmer_command = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_disable_mode, MM_3GPP_CMER_IND_NONE);
 
-            run_unsolicited_events_setup (task);
-            return;
-        }
-        mm_dbg ("Skipping +CMER disable command: not supported");
-    }
+    if (self->priv->modem_cgerep_support_checked && self->priv->modem_cgerep_supported)
+        ctx->cgerep_command = g_strdup ("+CGEREP=0");
 
-    g_task_return_boolean (task, TRUE);
-    g_object_unref (task);
+    run_unsolicited_events_setup (task);
 }
 
 /*****************************************************************************/
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index 1da47a987a0569c24cba80387a9c8a298b85864f..c999637aeb170abd543dadb4bb4eea161d0a7b76 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -864,6 +864,17 @@ mm_3gpp_ciev_regex_get (void)
 
 /*************************************************************************/
 
+GRegex *
+mm_3gpp_cgev_regex_get (void)
+{
+    return g_regex_new ("\\r\\n\\+CGEV:\\s*(.*)\\r\\n",
+                        G_REGEX_RAW | G_REGEX_OPTIMIZE,
+                        0,
+                        NULL);
+}
+
+/*************************************************************************/
+
 GRegex *
 mm_3gpp_cusd_regex_get (void)
 {
@@ -3264,6 +3275,310 @@ done:
     return array;
 }
 
+/*************************************************************************/
+/* +CGEV indication parser
+ *
+ * We provide full parsing support, including parameters, for these messages:
+ *    +CGEV: NW DETACH
+ *    +CGEV: ME DETACH
+ *    +CGEV: NW PDN ACT <cid>
+ *    +CGEV: ME PDN ACT <cid>[,<reason>[,<cid_other>]]
+ *    +CGEV: NW ACT <p_cid>, <cid>, <event_type>
+ *    +CGEV: ME ACT <p_cid>, <cid>, <event_type>
+ *    +CGEV: NW DEACT <PDP_type>, <PDP_addr>, [<cid>]
+ *    +CGEV: ME DEACT <PDP_type>, <PDP_addr>, [<cid>]
+ *    +CGEV: NW PDN DEACT <cid>
+ *    +CGEV: ME PDN DEACT <cid>
+ *    +CGEV: NW DEACT <p_cid>, <cid>, <event_type>
+ *    +CGEV: ME DEACT <p_cid>, <cid>, <event_type>
+ *    +CGEV: REJECT <PDP_type>, <PDP_addr>
+ *    +CGEV: NW REACT <PDP_type>, <PDP_addr>, [<cid>]
+ *
+ * We don't provide parameter parsing for these messages:
+ *    +CGEV: NW CLASS <class>
+ *    +CGEV: ME CLASS <class>
+ *    +CGEV: NW MODIFY <cid>, <change_reason>, <event_type>
+ *    +CGEV: ME MODIFY <cid>, <change_reason>, <event_type>
+ */
+
+static gboolean
+deact_secondary (const gchar *str)
+{
+    /* We need to detect the ME/NW DEACT format.
+     * Either,
+     *    +CGEV: NW DEACT <PDP_type>, <PDP_addr>, [<cid>]
+     *    +CGEV: ME DEACT <PDP_type>, <PDP_addr>, [<cid>]
+     * or,
+     *    +CGEV: NW DEACT <p_cid>, <cid>, <event_type>
+     *    +CGEV: ME DEACT <p_cid>, <cid>, <event_type>
+     */
+    str = strstr (str, "DEACT") + 5;
+    while (*str == ' ')
+        str++;
+
+    /* We will look for <p_cid> because we know it's NUMERIC */
+    return g_ascii_isdigit (*str);
+}
+
+MM3gppCgev
+mm_3gpp_parse_cgev_indication_action (const gchar *str)
+{
+    str = mm_strip_tag (str, "+CGEV:");
+    if (g_str_has_prefix (str, "NW DETACH"))
+        return MM_3GPP_CGEV_NW_DETACH;
+    if (g_str_has_prefix (str, "ME DETACH"))
+        return MM_3GPP_CGEV_ME_DETACH;
+    if (g_str_has_prefix (str, "NW CLASS"))
+        return MM_3GPP_CGEV_NW_CLASS;
+    if (g_str_has_prefix (str, "ME CLASS"))
+        return MM_3GPP_CGEV_ME_CLASS;
+    if (g_str_has_prefix (str, "NW PDN ACT"))
+        return MM_3GPP_CGEV_NW_ACT_PRIMARY;
+    if (g_str_has_prefix (str, "ME PDN ACT"))
+        return MM_3GPP_CGEV_ME_ACT_PRIMARY;
+    if (g_str_has_prefix (str, "NW ACT"))
+        return MM_3GPP_CGEV_NW_ACT_SECONDARY;
+    if (g_str_has_prefix (str, "ME ACT"))
+        return MM_3GPP_CGEV_ME_ACT_SECONDARY;
+    if (g_str_has_prefix (str, "NW DEACT"))
+        return (deact_secondary (str) ? MM_3GPP_CGEV_NW_DEACT_SECONDARY : MM_3GPP_CGEV_NW_DEACT_PDP);
+    if (g_str_has_prefix (str, "ME DEACT"))
+        return (deact_secondary (str) ? MM_3GPP_CGEV_ME_DEACT_SECONDARY : MM_3GPP_CGEV_ME_DEACT_PDP);
+    if (g_str_has_prefix (str, "NW PDN DEACT"))
+        return MM_3GPP_CGEV_NW_DEACT_PRIMARY;
+    if (g_str_has_prefix (str, "ME PDN DEACT"))
+        return MM_3GPP_CGEV_ME_DEACT_PRIMARY;
+    if (g_str_has_prefix (str, "NW MODIFY"))
+        return MM_3GPP_CGEV_NW_MODIFY;
+    if (g_str_has_prefix (str, "ME MODIFY"))
+        return MM_3GPP_CGEV_ME_MODIFY;
+    if (g_str_has_prefix (str, "NW REACT"))
+        return MM_3GPP_CGEV_NW_REACT;
+    if (g_str_has_prefix (str, "REJECT"))
+        return MM_3GPP_CGEV_REJECT;
+    return MM_3GPP_CGEV_UNKNOWN;
+}
+
+/*
+ * +CGEV: NW DEACT <PDP_type>, <PDP_addr>, [<cid>]
+ * +CGEV: ME DEACT <PDP_type>, <PDP_addr>, [<cid>]
+ * +CGEV: REJECT <PDP_type>, <PDP_addr>
+ * +CGEV: NW REACT <PDP_type>, <PDP_addr>, [<cid>]
+ */
+gboolean
+mm_3gpp_parse_cgev_indication_pdp (const gchar  *str,
+                                   MM3gppCgev    type,
+                                   gchar       **out_pdp_type,
+                                   gchar       **out_pdp_addr,
+                                   guint        *out_cid,
+                                   GError      **error)
+{
+    GRegex     *r;
+    GMatchInfo *match_info = NULL;
+    GError     *inner_error = NULL;
+    gchar      *pdp_type = NULL;
+    gchar      *pdp_addr = NULL;
+    guint       cid = 0;
+
+    g_assert (type == MM_3GPP_CGEV_REJECT   ||
+              type == MM_3GPP_CGEV_NW_REACT ||
+              type == MM_3GPP_CGEV_NW_DEACT_PDP ||
+              type == MM_3GPP_CGEV_ME_DEACT_PDP);
+
+    r = g_regex_new ("(?:"
+                     "REJECT|"
+                     "NW REACT|"
+                     "NW DEACT|ME DEACT"
+                     ")\\s*([^,]*),\\s*([^,]*)(?:,\\s*([0-9]+))?", 0, 0, NULL);
+
+    str = mm_strip_tag (str, "+CGEV:");
+    g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
+    if (inner_error)
+        goto out;
+
+    if (!g_match_info_matches (match_info)) {
+        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response");
+        goto out;
+    }
+
+    if (out_pdp_type && !(pdp_type = mm_get_string_unquoted_from_match_info (match_info, 1))) {
+        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing PDP type");
+        goto out;
+    }
+
+    if (out_pdp_addr && !(pdp_addr = mm_get_string_unquoted_from_match_info (match_info, 2))) {
+        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing PDP addr");
+        goto out;
+    }
+
+    /* CID is optional */
+    if (out_cid &&
+        (g_match_info_get_match_count (match_info) >= 4) &&
+        !mm_get_uint_from_match_info (match_info, 3, &cid)) {
+        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing CID");
+        goto out;
+    }
+
+out:
+    if (match_info)
+        g_match_info_free (match_info);
+    g_regex_unref (r);
+
+    if (inner_error) {
+        g_free (pdp_type);
+        g_free (pdp_addr);
+        g_propagate_error (error, inner_error);
+        return FALSE;
+    }
+
+    if (out_pdp_type)
+        *out_pdp_type = pdp_type;
+    if (out_pdp_addr)
+        *out_pdp_addr = pdp_addr;
+    if (out_cid)
+        *out_cid = cid;
+    return TRUE;
+}
+
+/*
+ * +CGEV: NW PDN ACT <cid>
+ * +CGEV: ME PDN ACT <cid>[,<reason>[,<cid_other>]]
+ * +CGEV: NW PDN DEACT <cid>
+ * +CGEV: ME PDN DEACT <cid>
+ *
+ * NOTE: the special case of a "ME PDN ACT" notification with the additional
+ * <reason> and <cid_other> fields is telling us that <cid> was NOT connected
+ * but <cid_other> was connected instead, which may happen when trying to
+ * connect a IPv4v6 context but the modem ended up connecting a IPv4-only or
+ * IPv6-only context instead. We are right now ignoring this, and assuming the
+ * <cid> that we requested is the one reported as connected.
+ */
+gboolean
+mm_3gpp_parse_cgev_indication_primary (const gchar  *str,
+                                       MM3gppCgev    type,
+                                       guint        *out_cid,
+                                       GError      **error)
+{
+    GRegex     *r;
+    GMatchInfo *match_info = NULL;
+    GError     *inner_error = NULL;
+    guint       cid = 0;
+
+    g_assert ((type == MM_3GPP_CGEV_NW_ACT_PRIMARY)   ||
+              (type == MM_3GPP_CGEV_ME_ACT_PRIMARY)   ||
+              (type == MM_3GPP_CGEV_NW_DEACT_PRIMARY) ||
+              (type == MM_3GPP_CGEV_ME_DEACT_PRIMARY));
+
+    r = g_regex_new ("(?:"
+                     "NW PDN ACT|ME PDN ACT|"
+                     "NW PDN DEACT|ME PDN DEACT|"
+                     ")\\s*([0-9]+)", 0, 0, NULL);
+
+    str = mm_strip_tag (str, "+CGEV:");
+    g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
+    if (inner_error)
+        goto out;
+
+    if (!g_match_info_matches (match_info)) {
+        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response");
+        goto out;
+    }
+
+    if (out_cid && !mm_get_uint_from_match_info (match_info, 1, &cid)) {
+        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing CID");
+        goto out;
+    }
+
+out:
+    if (match_info)
+        g_match_info_free (match_info);
+    g_regex_unref (r);
+
+    if (inner_error) {
+        g_propagate_error (error, inner_error);
+        return FALSE;
+    }
+
+    if (out_cid)
+        *out_cid = cid;
+    return TRUE;
+}
+
+/*
+ * +CGEV: NW ACT <p_cid>, <cid>, <event_type>
+ * +CGEV: ME ACT <p_cid>, <cid>, <event_type>
+ * +CGEV: NW DEACT <p_cid>, <cid>, <event_type>
+ * +CGEV: ME DEACT <p_cid>, <cid>, <event_type>
+ */
+gboolean
+mm_3gpp_parse_cgev_indication_secondary (const gchar  *str,
+                                         MM3gppCgev    type,
+                                         guint        *out_p_cid,
+                                         guint        *out_cid,
+                                         guint        *out_event_type,
+                                         GError      **error)
+{
+    GRegex     *r;
+    GMatchInfo *match_info = NULL;
+    GError     *inner_error = NULL;
+    guint       p_cid = 0;
+    guint       cid = 0;
+    guint       event_type = 0;
+
+    g_assert (type == MM_3GPP_CGEV_NW_ACT_SECONDARY   ||
+              type == MM_3GPP_CGEV_ME_ACT_SECONDARY   ||
+              type == MM_3GPP_CGEV_NW_DEACT_SECONDARY ||
+              type == MM_3GPP_CGEV_ME_DEACT_SECONDARY);
+
+    r = g_regex_new ("(?:"
+                     "NW ACT|ME ACT|"
+                     "NW DEACT|ME DEACT"
+                     ")\\s*([0-9]+),\\s*([0-9]+),\\s*([0-9]+)", 0, 0, NULL);
+
+    str = mm_strip_tag (str, "+CGEV:");
+    g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
+    if (inner_error)
+        goto out;
+
+    if (!g_match_info_matches (match_info)) {
+        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response");
+        goto out;
+    }
+
+    if (out_p_cid && !mm_get_uint_from_match_info (match_info, 1, &p_cid)) {
+        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing primary CID");
+        goto out;
+    }
+
+    if (out_cid && !mm_get_uint_from_match_info (match_info, 2, &cid)) {
+        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing secondary CID");
+        goto out;
+    }
+
+    if (out_event_type && !mm_get_uint_from_match_info (match_info, 3, &event_type)) {
+        inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing event type");
+        goto out;
+    }
+
+out:
+    if (match_info)
+        g_match_info_free (match_info);
+    g_regex_unref (r);
+
+    if (inner_error) {
+        g_propagate_error (error, inner_error);
+        return FALSE;
+    }
+
+    if (out_p_cid)
+        *out_p_cid = p_cid;
+    if (out_cid)
+        *out_cid = cid;
+    if (out_event_type)
+        *out_event_type = event_type;
+    return TRUE;
+}
+
 /*************************************************************************/
 
 void
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index 71d8893ea8707b6c391e18ec73d964adf7008f44..237046ad7f358aaa3f507739309aa5771d652f98 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -125,6 +125,7 @@ MMFlowControl mm_flow_control_from_string (const gchar  *str,
 GPtrArray *mm_3gpp_creg_regex_get     (gboolean solicited);
 void       mm_3gpp_creg_regex_destroy (GPtrArray *array);
 GRegex    *mm_3gpp_ciev_regex_get (void);
+GRegex    *mm_3gpp_cgev_regex_get (void);
 GRegex    *mm_3gpp_cusd_regex_get (void);
 GRegex    *mm_3gpp_cmti_regex_get (void);
 GRegex    *mm_3gpp_cds_regex_get (void);
@@ -272,6 +273,47 @@ gint         mm_3gpp_cind_response_get_max       (MM3gppCindResponse *r);
 GByteArray *mm_3gpp_parse_cind_read_response (const gchar *reply,
                                               GError **error);
 
+/* +CGEV indication parser */
+typedef enum {
+    MM_3GPP_CGEV_UNKNOWN,
+    MM_3GPP_CGEV_NW_DETACH,
+    MM_3GPP_CGEV_ME_DETACH,
+    MM_3GPP_CGEV_NW_CLASS,
+    MM_3GPP_CGEV_ME_CLASS,
+    MM_3GPP_CGEV_NW_ACT_PRIMARY,
+    MM_3GPP_CGEV_ME_ACT_PRIMARY,
+    MM_3GPP_CGEV_NW_ACT_SECONDARY,
+    MM_3GPP_CGEV_ME_ACT_SECONDARY,
+    MM_3GPP_CGEV_NW_DEACT_PRIMARY,
+    MM_3GPP_CGEV_ME_DEACT_PRIMARY,
+    MM_3GPP_CGEV_NW_DEACT_SECONDARY,
+    MM_3GPP_CGEV_ME_DEACT_SECONDARY,
+    MM_3GPP_CGEV_NW_DEACT_PDP,
+    MM_3GPP_CGEV_ME_DEACT_PDP,
+    MM_3GPP_CGEV_NW_MODIFY,
+    MM_3GPP_CGEV_ME_MODIFY,
+    MM_3GPP_CGEV_REJECT,
+    MM_3GPP_CGEV_NW_REACT,
+} MM3gppCgev;
+
+MM3gppCgev mm_3gpp_parse_cgev_indication_action    (const gchar *str);
+gboolean   mm_3gpp_parse_cgev_indication_pdp       (const gchar  *str,
+                                                    MM3gppCgev    type,
+                                                    gchar       **out_pdp_type,
+                                                    gchar       **out_pdp_addr,
+                                                    guint        *out_cid,
+                                                    GError      **error);
+gboolean   mm_3gpp_parse_cgev_indication_primary   (const gchar  *str,
+                                                    MM3gppCgev    type,
+                                                    guint        *out_cid,
+                                                    GError      **error);
+gboolean   mm_3gpp_parse_cgev_indication_secondary (const gchar  *str,
+                                                    MM3gppCgev    type,
+                                                    guint        *out_p_cid,
+                                                    guint        *out_cid,
+                                                    guint        *out_event_type,
+                                                    GError      **error);
+
 /* AT+CMGL=4 (list sms parts) response parser */
 typedef struct {
     gint index;
diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c
index 598e535058a96c7f865fdc2395b0d3f467e84190..d5b09abdcafdb7b6a78517aa037a8ed3595fe271 100644
--- a/src/tests/test-modem-helpers.c
+++ b/src/tests/test-modem-helpers.c
@@ -2045,6 +2045,161 @@ test_cind_response_moto_v3m (void *f, gpointer d)
     test_cind_results ("Motorola V3m", reply, &expected[0], G_N_ELEMENTS (expected));
 }
 
+/*****************************************************************************/
+/* Test +CGEV indication parsing */
+
+typedef struct {
+    const gchar *str;
+    MM3gppCgev   expected_type;
+    const gchar *expected_pdp_type;
+    const gchar *expected_pdp_addr;
+    guint        expected_cid;
+    guint        expected_parent_cid;
+    guint        expected_event_type;
+} CgevIndicationTest;
+
+static const CgevIndicationTest cgev_indication_tests[] = {
+    { "+CGEV: REJECT IP, 123.123.123.123",      MM_3GPP_CGEV_REJECT,             "IP", "123.123.123.123", 0, 0, 0 },
+    { "REJECT IP, 123.123.123.123",             MM_3GPP_CGEV_REJECT,             "IP", "123.123.123.123", 0, 0, 0 },
+
+    { "+CGEV: NW REACT IP, 123.123.123.123",    MM_3GPP_CGEV_NW_REACT,           "IP", "123.123.123.123", 0, 0, 0 },
+    { "NW REACT IP, 123.123.123.123",           MM_3GPP_CGEV_NW_REACT,           "IP", "123.123.123.123", 0, 0, 0 },
+    { "+CGEV: NW REACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_REACT,           "IP", "123.123.123.123", 1, 0, 0 },
+
+    { "NW DEACT IP, 123.123.123.123, 1",        MM_3GPP_CGEV_NW_DEACT_PDP,       "IP", "123.123.123.123", 1, 0, 0 },
+    { "+CGEV: NW DEACT IP, 123.123.123.123",    MM_3GPP_CGEV_NW_DEACT_PDP,       "IP", "123.123.123.123", 0, 0, 0 },
+    { "NW DEACT IP, 123.123.123.123",           MM_3GPP_CGEV_NW_DEACT_PDP,       "IP", "123.123.123.123", 0, 0, 0 },
+    { "+CGEV: NW DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_DEACT_PDP,       "IP", "123.123.123.123", 1, 0, 0 },
+    { "NW DEACT IP, 123.123.123.123, 1",        MM_3GPP_CGEV_NW_DEACT_PDP,       "IP", "123.123.123.123", 1, 0, 0 },
+
+    { "ME DEACT IP, 123.123.123.123, 1",        MM_3GPP_CGEV_ME_DEACT_PDP,       "IP", "123.123.123.123", 1, 0, 0 },
+    { "+CGEV: ME DEACT IP, 123.123.123.123",    MM_3GPP_CGEV_ME_DEACT_PDP,       "IP", "123.123.123.123", 0, 0, 0 },
+    { "ME DEACT IP, 123.123.123.123",           MM_3GPP_CGEV_ME_DEACT_PDP,       "IP", "123.123.123.123", 0, 0, 0 },
+    { "+CGEV: ME DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_ME_DEACT_PDP,       "IP", "123.123.123.123", 1, 0, 0 },
+    { "ME DEACT IP, 123.123.123.123, 1",        MM_3GPP_CGEV_ME_DEACT_PDP,       "IP", "123.123.123.123", 1, 0, 0 },
+
+    { "ME PDN ACT 2",                           MM_3GPP_CGEV_ME_ACT_PRIMARY,     NULL, NULL,              2, 0, 0 },
+    { "+CGEV: ME PDN ACT 2",                    MM_3GPP_CGEV_ME_ACT_PRIMARY,     NULL, NULL,              2, 0, 0 },
+    /* with ,<reason>[,<cid_other>]] */
+    { "ME PDN ACT 2, 3",                        MM_3GPP_CGEV_ME_ACT_PRIMARY,     NULL, NULL,              2, 0, 0 },
+    { "+CGEV: ME PDN ACT 2, 3",                 MM_3GPP_CGEV_ME_ACT_PRIMARY,     NULL, NULL,              2, 0, 0 },
+    { "ME PDN ACT 2, 3, 4",                     MM_3GPP_CGEV_ME_ACT_PRIMARY,     NULL, NULL,              2, 0, 0 },
+    { "+CGEV: ME PDN ACT 2, 3, 4",              MM_3GPP_CGEV_ME_ACT_PRIMARY,     NULL, NULL,              2, 0, 0 },
+
+    { "ME PDN DEACT 2",                         MM_3GPP_CGEV_ME_DEACT_PRIMARY,   NULL, NULL,              2, 0, 0 },
+    { "+CGEV: ME PDN DEACT 2",                  MM_3GPP_CGEV_ME_DEACT_PRIMARY,   NULL, NULL,              2, 0, 0 },
+
+    { "ME ACT 3, 2, 1",                         MM_3GPP_CGEV_ME_ACT_SECONDARY,   NULL, NULL,              2, 3, 1 },
+    { "+CGEV: ME ACT 3, 2, 1",                  MM_3GPP_CGEV_ME_ACT_SECONDARY,   NULL, NULL,              2, 3, 1 },
+
+    { "ME DEACT 3, 2, 1",                       MM_3GPP_CGEV_ME_DEACT_SECONDARY, NULL, NULL,              2, 3, 1 },
+    { "+CGEV: ME DEACT 3, 2, 1",                MM_3GPP_CGEV_ME_DEACT_SECONDARY, NULL, NULL,              2, 3, 1 },
+
+    { "NW PDN ACT 2",                           MM_3GPP_CGEV_NW_ACT_PRIMARY,     NULL, NULL,              2, 0, 0 },
+    { "+CGEV: NW PDN ACT 2",                    MM_3GPP_CGEV_NW_ACT_PRIMARY,     NULL, NULL,              2, 0, 0 },
+
+    { "NW PDN DEACT 2",                         MM_3GPP_CGEV_NW_DEACT_PRIMARY,   NULL, NULL,              2, 0, 0 },
+    { "+CGEV: NW PDN DEACT 2",                  MM_3GPP_CGEV_NW_DEACT_PRIMARY,   NULL, NULL,              2, 0, 0 },
+
+    { "NW ACT 3, 2, 1",                         MM_3GPP_CGEV_NW_ACT_SECONDARY,   NULL, NULL,              2, 3, 1 },
+    { "+CGEV: NW ACT 3, 2, 1",                  MM_3GPP_CGEV_NW_ACT_SECONDARY,   NULL, NULL,              2, 3, 1 },
+
+    { "NW DEACT 3, 2, 1",                       MM_3GPP_CGEV_NW_DEACT_SECONDARY, NULL, NULL,              2, 3, 1 },
+    { "+CGEV: NW DEACT 3, 2, 1",                MM_3GPP_CGEV_NW_DEACT_SECONDARY, NULL, NULL,              2, 3, 1 },
+
+    { "+CGEV: NW DETACH",                       MM_3GPP_CGEV_NW_DETACH,          NULL, NULL,              0, 0, 0 },
+    { "NW DETACH",                              MM_3GPP_CGEV_NW_DETACH,          NULL, NULL,              0, 0, 0 },
+    { "+CGEV: ME DETACH",                       MM_3GPP_CGEV_ME_DETACH,          NULL, NULL,              0, 0, 0 },
+    { "ME DETACH",                              MM_3GPP_CGEV_ME_DETACH,          NULL, NULL,              0, 0, 0 },
+
+    { "+CGEV: NW CLASS A",                      MM_3GPP_CGEV_NW_CLASS,           NULL, NULL,              0, 0, 0 },
+    { "NW CLASS A",                             MM_3GPP_CGEV_NW_CLASS,           NULL, NULL,              0, 0, 0 },
+    { "+CGEV: ME CLASS A",                      MM_3GPP_CGEV_ME_CLASS,           NULL, NULL,              0, 0, 0 },
+    { "ME CLASS A",                             MM_3GPP_CGEV_ME_CLASS,           NULL, NULL,              0, 0, 0 },
+};
+
+static void
+test_cgev_indication (const CgevIndicationTest *t)
+{
+    guint     i;
+    GError   *error = NULL;
+    gboolean  ret;
+
+    for (i = 0; i < G_N_ELEMENTS (cgev_indication_tests); i++) {
+        const CgevIndicationTest *test = &cgev_indication_tests[i];
+        MM3gppCgev                type;
+
+        type = mm_3gpp_parse_cgev_indication_action (test->str);
+        g_assert_cmpuint (type, ==, test->expected_type);
+
+        g_print ("[%u] type: %u\n", i, type);
+
+        switch (type) {
+        case MM_3GPP_CGEV_NW_DETACH:
+        case MM_3GPP_CGEV_ME_DETACH:
+            break;
+        case MM_3GPP_CGEV_NW_ACT_PRIMARY:
+        case MM_3GPP_CGEV_ME_ACT_PRIMARY:
+        case MM_3GPP_CGEV_NW_DEACT_PRIMARY:
+        case MM_3GPP_CGEV_ME_DEACT_PRIMARY: {
+            guint cid;
+
+            g_print ("[%u] parsing as primary\n", i);
+            ret = mm_3gpp_parse_cgev_indication_primary (test->str, type, &cid, &error);
+            g_assert_no_error (error);
+            g_assert (ret);
+            g_assert_cmpuint (cid, ==, test->expected_cid);
+            break;
+        }
+        case MM_3GPP_CGEV_NW_ACT_SECONDARY:
+        case MM_3GPP_CGEV_ME_ACT_SECONDARY:
+        case MM_3GPP_CGEV_NW_DEACT_SECONDARY:
+        case MM_3GPP_CGEV_ME_DEACT_SECONDARY: {
+            guint p_cid;
+            guint cid;
+            guint event_type;
+
+            g_print ("[%u] parsing as secondary\n", i);
+            ret = mm_3gpp_parse_cgev_indication_secondary (test->str, type, &p_cid, &cid, &event_type, &error);
+            g_assert_no_error (error);
+            g_assert (ret);
+            g_assert_cmpuint (cid, ==, test->expected_cid);
+            g_assert_cmpuint (p_cid, ==, test->expected_parent_cid);
+            g_assert_cmpuint (event_type, ==, test->expected_event_type);
+            break;
+        }
+        case MM_3GPP_CGEV_NW_DEACT_PDP:
+        case MM_3GPP_CGEV_ME_DEACT_PDP:
+        case MM_3GPP_CGEV_REJECT:
+        case MM_3GPP_CGEV_NW_REACT: {
+            gchar *pdp_type;
+            gchar *pdp_addr;
+            guint  cid;
+
+            g_print ("[%u] parsing as pdp\n", i);
+            ret = mm_3gpp_parse_cgev_indication_pdp (test->str, type, &pdp_type, &pdp_addr, &cid, &error);
+            g_assert_no_error (error);
+            g_assert (ret);
+            g_assert_cmpstr (pdp_type, ==, test->expected_pdp_type);
+            g_assert_cmpstr (pdp_addr, ==, test->expected_pdp_addr);
+            g_assert_cmpuint (cid, ==, test->expected_cid);
+
+            g_free (pdp_type);
+            g_free (pdp_addr);
+            break;
+        }
+        case MM_3GPP_CGEV_NW_CLASS:
+        case MM_3GPP_CGEV_ME_CLASS:
+        case MM_3GPP_CGEV_NW_MODIFY:
+        case MM_3GPP_CGEV_ME_MODIFY:
+            /* ignore */
+            break;
+        default:
+            break;
+        }
+    }
+}
+
 /*****************************************************************************/
 /* Test ICCID parsing */
 
@@ -3999,6 +4154,8 @@ int main (int argc, char **argv)
     g_test_suite_add (suite, TESTCASE (test_cind_response_linktop_lw273, NULL));
     g_test_suite_add (suite, TESTCASE (test_cind_response_moto_v3m, NULL));
 
+    g_test_suite_add (suite, TESTCASE (test_cgev_indication, NULL));
+
     g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_swap_19_digit, NULL));
     g_test_suite_add (suite, TESTCASE (test_iccid_parse_unquoted_swap_20_digit, NULL));
     g_test_suite_add (suite, TESTCASE (test_iccid_parse_unquoted_unswapped_19_digit, NULL));