diff --git a/plugins/huawei/mm-call-huawei.c b/plugins/huawei/mm-call-huawei.c
index ee8c3f79a25da4c27dfb8ec899fbd91e0a8b1889..f1c4b13310e90467db3d761710081693b6946d92 100644
--- a/plugins/huawei/mm-call-huawei.c
+++ b/plugins/huawei/mm-call-huawei.c
@@ -139,5 +139,7 @@ mm_call_huawei_class_init (MMCallHuaweiClass *klass)
 {
     MMBaseCallClass *base_call_class = MM_BASE_CALL_CLASS (klass);
 
-    base_call_class->start = call_start;
+    base_call_class->start                      = call_start;
+    base_call_class->setup_unsolicited_events   = NULL;
+    base_call_class->cleanup_unsolicited_events = NULL;
 }
diff --git a/src/mm-base-call.c b/src/mm-base-call.c
index dfbdb702191588815a2c0fed9ec83ace68de0a49..949e7ccb222f042f849c64f3811cea38357a44b7 100644
--- a/src/mm-base-call.c
+++ b/src/mm-base-call.c
@@ -59,8 +59,82 @@ struct _MMBaseCallPrivate {
     gboolean supports_ringing_to_active;
 
     guint incoming_timeout;
+    GRegex *in_call_events;
 };
 
+/*****************************************************************************/
+/* In-call unsolicited events
+ * Once a call is started, we may need to detect special URCs to trigger call
+ * state changes, e.g. "NO CARRIER" when the remote party hangs up. */
+
+static void
+in_call_event_received (MMPortSerialAt *port,
+                        GMatchInfo     *info,
+                        MMBaseCall     *self)
+{
+    gchar *str;
+
+    str = g_match_info_fetch (info, 1);
+    if (!str)
+        return;
+
+    if (!strcmp (str, "NO DIALTONE"))
+        mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_ERROR);
+    else if (!strcmp (str, "NO CARRIER") || !strcmp (str, "BUSY") || !strcmp (str, "NO ANSWER"))
+        mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_REFUSED_OR_BUSY);
+
+    g_free (str);
+}
+
+static gboolean
+common_setup_cleanup_unsolicited_events (MMBaseCall  *self,
+                                         gboolean     enable,
+                                         GError     **error)
+{
+    MMBaseModem    *modem = NULL;
+    MMPortSerialAt *ports[2];
+    gint            i;
+
+    if (G_UNLIKELY (!self->priv->in_call_events))
+        self->priv->in_call_events = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n$",
+                                                  G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
+    g_object_get (self,
+                  MM_BASE_CALL_MODEM, &modem,
+                  NULL);
+
+    ports[0] = mm_base_modem_peek_port_primary   (modem);
+    ports[1] = mm_base_modem_peek_port_secondary (modem);
+
+    for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+        if (!ports[i])
+            continue;
+
+        mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+                                                       self->priv->in_call_events,
+                                                       enable ? (MMPortSerialAtUnsolicitedMsgFn) in_call_event_received : NULL,
+                                                       enable ? self : NULL,
+                                                       NULL);
+    }
+
+    g_object_unref (modem);
+    return TRUE;
+}
+
+static gboolean
+setup_unsolicited_events (MMBaseCall  *self,
+                          GError     **error)
+{
+    return common_setup_cleanup_unsolicited_events (self, TRUE, error);
+}
+
+static gboolean
+cleanup_unsolicited_events (MMBaseCall  *self,
+                            GError     **error)
+{
+    return common_setup_cleanup_unsolicited_events (self, FALSE, error);
+}
+
 /*****************************************************************************/
 /* Incoming calls are reported via RING URCs. If the caller stops the call
  * attempt before it has been answered, the only thing we would see is that the
@@ -991,6 +1065,8 @@ finalize (GObject *object)
 {
     MMBaseCall *self = MM_BASE_CALL (object);
 
+    if (self->priv->in_call_events)
+        g_regex_unref (self->priv->in_call_events);
     g_free (self->priv->path);
 
     G_OBJECT_CLASS (mm_base_call_parent_class)->finalize (object);
@@ -1031,14 +1107,17 @@ mm_base_call_class_init (MMBaseCallClass *klass)
     object_class->finalize = finalize;
     object_class->dispose = dispose;
 
-    klass->start            = call_start;
-    klass->start_finish     = call_start_finish;
-    klass->accept           = call_accept;
-    klass->accept_finish    = call_accept_finish;
-    klass->hangup           = call_hangup;
-    klass->hangup_finish    = call_hangup_finish;
-    klass->send_dtmf        = call_send_dtmf;
-    klass->send_dtmf_finish = call_send_dtmf_finish;
+    klass->start                      = call_start;
+    klass->start_finish               = call_start_finish;
+    klass->accept                     = call_accept;
+    klass->accept_finish              = call_accept_finish;
+    klass->hangup                     = call_hangup;
+    klass->hangup_finish              = call_hangup_finish;
+    klass->send_dtmf                  = call_send_dtmf;
+    klass->send_dtmf_finish           = call_send_dtmf_finish;
+    klass->setup_unsolicited_events   = setup_unsolicited_events;
+    klass->cleanup_unsolicited_events = cleanup_unsolicited_events;
+
 
     properties[PROP_CONNECTION] =
         g_param_spec_object (MM_BASE_CALL_CONNECTION,