Bridge API: Set a cause code on a channel when it is ejected from a bridge.
[asterisk/asterisk.git] / main / bridge_channel.c
index f887ef5..385be6d 100644 (file)
@@ -53,6 +53,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/musiconhold.h"
 #include "asterisk/features_config.h"
 #include "asterisk/parking.h"
+#include "asterisk/causes.h"
 
 /*!
  * \brief Used to queue an action frame onto a bridge channel and write an action frame into a bridge.
@@ -101,14 +102,10 @@ int ast_bridge_channel_notify_talking(struct ast_bridge_channel *bridge_channel,
        return ast_bridge_channel_queue_frame(bridge_channel, &action);
 }
 
-void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state)
-{
-       ast_bridge_channel_lock(bridge_channel);
-       ast_bridge_channel_leave_bridge_nolock(bridge_channel, new_state);
-       ast_bridge_channel_unlock(bridge_channel);
-}
-
-/*! \internal \brief Poke the bridge_channel thread */
+/*!
+ * \internal
+ * \brief Poke the bridge_channel thread
+ */
 static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel)
 {
        if (!pthread_equal(pthread_self(), bridge_channel->thread)) {
@@ -119,9 +116,33 @@ static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel)
        }
 }
 
-void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state)
+/*!
+ * \internal
+ * \brief Set actual cause on channel.
+ * \since 12.0.0
+ *
+ * \param chan Channel to set cause.
+ * \param cause Cause to set on channel.
+ *   If cause <= 0 then use cause on channel if cause still <= 0 use AST_CAUSE_NORMAL_CLEARING.
+ *
+ * \return Actual cause set on channel.
+ */
+static int channel_set_cause(struct ast_channel *chan, int cause)
+{
+       ast_channel_lock(chan);
+       if (cause <= 0) {
+               cause = ast_channel_hangupcause(chan);
+               if (cause <= 0) {
+                       cause = AST_CAUSE_NORMAL_CLEARING;
+               }
+       }
+       ast_channel_hangupcause_set(chan, cause);
+       ast_channel_unlock(chan);
+       return cause;
+}
+
+void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause)
 {
-/* BUGBUG need cause code for the bridge_channel leaving the bridge. */
        if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
                return;
        }
@@ -130,12 +151,21 @@ void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_ch
                bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state,
                new_state);
 
+       channel_set_cause(bridge_channel->chan, cause);
+
        /* Change the state on the bridge channel */
        bridge_channel->state = new_state;
 
        bridge_channel_poke(bridge_channel);
 }
 
+void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause)
+{
+       ast_bridge_channel_lock(bridge_channel);
+       ast_bridge_channel_leave_bridge_nolock(bridge_channel, new_state, cause);
+       ast_bridge_channel_unlock(bridge_channel);
+}
+
 struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel)
 {
        struct ast_bridge *bridge = bridge_channel->bridge;
@@ -260,21 +290,19 @@ void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_ch
        }
 }
 
-/*!
-* \internal
-* \brief Handle bridge hangup event.
-* \since 12.0.0
-*
-* \param bridge_channel Which channel is hanging up.
-*
-* \return Nothing
-*/
-static void bridge_channel_handle_hangup(struct ast_bridge_channel *bridge_channel)
+void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel, int cause)
 {
        struct ast_bridge_features *features = bridge_channel->features;
        struct ast_bridge_hook *hook;
        struct ao2_iterator iter;
 
+       ast_bridge_channel_lock(bridge_channel);
+       if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
+               channel_set_cause(bridge_channel->chan, cause);
+               cause = 0;
+       }
+       ast_bridge_channel_unlock(bridge_channel);
+
        /* Run any hangup hooks. */
        iter = ao2_iterator_init(features->other_hooks, 0);
        for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
@@ -293,7 +321,7 @@ static void bridge_channel_handle_hangup(struct ast_bridge_channel *bridge_chann
        ao2_iterator_destroy(&iter);
 
        /* Default hangup action. */
-       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
+       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, cause);
 }
 
 /*!
@@ -311,7 +339,8 @@ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel,
 {
        ast_bridge_channel_lock_bridge(bridge_channel);
 /*
- * BUGBUG need to implement a deferred write queue for when there is no peer channel in the bridge (yet or it was kicked).
+ * XXX need to implement a deferred write queue for when there
+ * is no peer channel in the bridge (yet or it was kicked).
  *
  * The tech decides if a frame needs to be pushed back for deferral.
  * simple_bridge/native_bridge are likely the only techs that will do this.
@@ -328,6 +357,87 @@ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel,
 
 /*!
  * \internal
+ * \brief Suspend a channel from a bridge.
+ *
+ * \param bridge_channel Channel to suspend.
+ *
+ * \note This function assumes bridge_channel->bridge is locked.
+ *
+ * \return Nothing
+ */
+void bridge_channel_internal_suspend_nolock(struct ast_bridge_channel *bridge_channel)
+{
+       bridge_channel->suspended = 1;
+       if (bridge_channel->in_bridge) {
+               --bridge_channel->bridge->num_active;
+       }
+
+       /* Get technology bridge threads off of the channel. */
+       if (bridge_channel->bridge->technology->suspend) {
+               bridge_channel->bridge->technology->suspend(bridge_channel->bridge, bridge_channel);
+       }
+}
+
+/*!
+ * \internal
+ * \brief Suspend a channel from a bridge.
+ *
+ * \param bridge_channel Channel to suspend.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel)
+{
+       ast_bridge_channel_lock_bridge(bridge_channel);
+       bridge_channel_internal_suspend_nolock(bridge_channel);
+       ast_bridge_unlock(bridge_channel->bridge);
+}
+
+/*!
+ * \internal
+ * \brief Unsuspend a channel from a bridge.
+ *
+ * \param bridge_channel Channel to unsuspend.
+ *
+ * \note This function assumes bridge_channel->bridge is locked.
+ *
+ * \return Nothing
+ */
+void bridge_channel_internal_unsuspend_nolock(struct ast_bridge_channel *bridge_channel)
+{
+       bridge_channel->suspended = 0;
+       if (bridge_channel->in_bridge) {
+               ++bridge_channel->bridge->num_active;
+       }
+
+       /* Wake technology bridge threads to take care of channel again. */
+       if (bridge_channel->bridge->technology->unsuspend) {
+               bridge_channel->bridge->technology->unsuspend(bridge_channel->bridge, bridge_channel);
+       }
+
+       /* Wake suspended channel. */
+       ast_bridge_channel_lock(bridge_channel);
+       ast_cond_signal(&bridge_channel->cond);
+       ast_bridge_channel_unlock(bridge_channel);
+}
+
+/*!
+ * \internal
+ * \brief Unsuspend a channel from a bridge.
+ *
+ * \param bridge_channel Channel to unsuspend.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel)
+{
+       ast_bridge_channel_lock_bridge(bridge_channel);
+       bridge_channel_internal_unsuspend_nolock(bridge_channel);
+       ast_bridge_unlock(bridge_channel->bridge);
+}
+
+/*!
+ * \internal
  * \brief Queue an action frame onto the bridge channel with data.
  * \since 12.0.0
  *
@@ -487,7 +597,10 @@ int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel)
        return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, NULL, 0);
 }
 
-/*! \internal \brief Helper function to kick off a PBX app on a bridge_channel */
+/*!
+ * \internal
+ * \brief Helper function to kick off a PBX app on a bridge_channel
+ */
 static int run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args)
 {
        int res = 0;
@@ -516,7 +629,7 @@ void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const
        }
        if (run_app_helper(bridge_channel->chan, app_name, S_OR(app_args, ""))) {
                /* Break the bridge if the app returns non-zero. */
-               bridge_channel_handle_hangup(bridge_channel);
+               ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING);
        }
        if (moh_class) {
                ast_bridge_channel_write_unhold(bridge_channel);
@@ -683,6 +796,8 @@ struct bridge_custom_callback {
        ast_bridge_custom_callback_fn callback;
        /*! Size of the payload if it exists.  A number otherwise. */
        size_t payload_size;
+       /*! Option flags determining how callback is called. */
+       unsigned int flags;
        /*! Nonzero if the payload exists. */
        char payload_exists;
        /*! Payload to give to callback. */
@@ -694,14 +809,22 @@ struct bridge_custom_callback {
  * \brief Handle the do custom callback bridge action.
  * \since 12.0.0
  *
- * \param bridge_channel Which channel to run the application on.
- * \param data Action frame data to run the application.
+ * \param bridge_channel Which channel to call the callback on.
+ * \param data Action frame data to call the callback.
  *
  * \return Nothing
  */
 static void bridge_channel_do_callback(struct ast_bridge_channel *bridge_channel, struct bridge_custom_callback *data)
 {
+       if (ast_test_flag(data, AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA)) {
+               bridge_channel_suspend(bridge_channel);
+               ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+       }
        data->callback(bridge_channel, data->payload_exists ? data->payload : NULL, data->payload_size);
+       if (ast_test_flag(data, AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA)) {
+               ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+               bridge_channel_unsuspend(bridge_channel);
+       }
 }
 
 /*!
@@ -709,7 +832,9 @@ static void bridge_channel_do_callback(struct ast_bridge_channel *bridge_channel
  * \brief Marshal a custom callback function to be called on a bridge_channel
  */
 static int payload_helper_cb(ast_bridge_channel_post_action_data post_it,
-       struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
+       struct ast_bridge_channel *bridge_channel,
+       enum ast_bridge_channel_custom_callback_option flags,
+       ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
 {
        struct bridge_custom_callback *cb_data;
        size_t len_data = sizeof(*cb_data) + (payload ? payload_size : 0);
@@ -724,6 +849,7 @@ static int payload_helper_cb(ast_bridge_channel_post_action_data post_it,
        cb_data = alloca(len_data);
        cb_data->callback = callback;
        cb_data->payload_size = payload_size;
+       cb_data->flags = flags;
        cb_data->payload_exists = payload && payload_size;
        if (cb_data->payload_exists) {
                memcpy(cb_data->payload, payload, payload_size);/* Safe */
@@ -732,16 +858,20 @@ static int payload_helper_cb(ast_bridge_channel_post_action_data post_it,
        return post_it(bridge_channel, BRIDGE_CHANNEL_ACTION_CALLBACK, cb_data, len_data);
 }
 
-int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
+int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel,
+       enum ast_bridge_channel_custom_callback_option flags,
+       ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
 {
        return payload_helper_cb(bridge_channel_write_action_data,
-               bridge_channel, callback, payload, payload_size);
+               bridge_channel, flags, callback, payload, payload_size);
 }
 
-int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
+int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel,
+       enum ast_bridge_channel_custom_callback_option flags,
+       ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
 {
        return payload_helper_cb(bridge_channel_queue_action_data,
-               bridge_channel, callback, payload, payload_size);
+               bridge_channel, flags, callback, payload, payload_size);
 }
 
 struct bridge_park {
@@ -757,9 +887,18 @@ struct bridge_park {
  */
 static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload)
 {
-       ast_bridge_channel_park(bridge_channel, payload->parkee_uuid,
+       if (!ast_parking_provider_registered()) {
+               ast_log(AST_LOG_WARNING, "Unable to park %s: No parking provider loaded!\n",
+                       ast_channel_name(bridge_channel->chan));
+               return;
+       }
+
+       if (ast_parking_park_bridge_channel(bridge_channel, payload->parkee_uuid,
                &payload->parkee_uuid[payload->parker_uuid_offset],
-               payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL);
+               payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL)) {
+               ast_log(AST_LOG_WARNING, "Error occurred while parking %s\n",
+                       ast_channel_name(bridge_channel->chan));
+       }
 }
 
 /*!
@@ -798,87 +937,6 @@ int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, con
 
 /*!
  * \internal
- * \brief Suspend a channel from a bridge.
- *
- * \param bridge_channel Channel to suspend.
- *
- * \note This function assumes bridge_channel->bridge is locked.
- *
- * \return Nothing
- */
-void bridge_channel_internal_suspend_nolock(struct ast_bridge_channel *bridge_channel)
-{
-       bridge_channel->suspended = 1;
-       if (bridge_channel->in_bridge) {
-               --bridge_channel->bridge->num_active;
-       }
-
-       /* Get technology bridge threads off of the channel. */
-       if (bridge_channel->bridge->technology->suspend) {
-               bridge_channel->bridge->technology->suspend(bridge_channel->bridge, bridge_channel);
-       }
-}
-
-/*!
- * \internal
- * \brief Suspend a channel from a bridge.
- *
- * \param bridge_channel Channel to suspend.
- *
- * \return Nothing
- */
-static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel)
-{
-       ast_bridge_channel_lock_bridge(bridge_channel);
-       bridge_channel_internal_suspend_nolock(bridge_channel);
-       ast_bridge_unlock(bridge_channel->bridge);
-}
-
-/*!
- * \internal
- * \brief Unsuspend a channel from a bridge.
- *
- * \param bridge_channel Channel to unsuspend.
- *
- * \note This function assumes bridge_channel->bridge is locked.
- *
- * \return Nothing
- */
-void bridge_channel_internal_unsuspend_nolock(struct ast_bridge_channel *bridge_channel)
-{
-       bridge_channel->suspended = 0;
-       if (bridge_channel->in_bridge) {
-               ++bridge_channel->bridge->num_active;
-       }
-
-       /* Wake technology bridge threads to take care of channel again. */
-       if (bridge_channel->bridge->technology->unsuspend) {
-               bridge_channel->bridge->technology->unsuspend(bridge_channel->bridge, bridge_channel);
-       }
-
-       /* Wake suspended channel. */
-       ast_bridge_channel_lock(bridge_channel);
-       ast_cond_signal(&bridge_channel->cond);
-       ast_bridge_channel_unlock(bridge_channel);
-}
-
-/*!
- * \internal
- * \brief Unsuspend a channel from a bridge.
- *
- * \param bridge_channel Channel to unsuspend.
- *
- * \return Nothing
- */
-static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel)
-{
-       ast_bridge_channel_lock_bridge(bridge_channel);
-       bridge_channel_internal_unsuspend_nolock(bridge_channel);
-       ast_bridge_unlock(bridge_channel->bridge);
-}
-
-/*!
- * \internal
  * \brief Handle bridge channel interval expiration.
  * \since 12.0.0
  *
@@ -891,7 +949,7 @@ static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_cha
        struct ast_heap *interval_hooks;
        struct ast_bridge_hook_timer *hook;
        struct timeval start;
-       int hook_run = 0;
+       int chan_suspended = 0;
 
        interval_hooks = bridge_channel->features->interval_hooks;
        ast_heap_wrlock(interval_hooks);
@@ -908,8 +966,9 @@ static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_cha
                ao2_ref(hook, +1);
                ast_heap_unlock(interval_hooks);
 
-               if (!hook_run) {
-                       hook_run = 1;
+               if (!chan_suspended
+                       && ast_test_flag(&hook->timer, AST_BRIDGE_HOOK_TIMER_OPTION_MEDIA)) {
+                       chan_suspended = 1;
                        bridge_channel_suspend(bridge_channel);
                        ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
                }
@@ -964,13 +1023,16 @@ static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_cha
        }
        ast_heap_unlock(interval_hooks);
 
-       if (hook_run) {
+       if (chan_suspended) {
                ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
                bridge_channel_unsuspend(bridge_channel);
        }
 }
 
-/*! \internal \brief Write a DTMF stream out to a channel */
+/*!
+ * \internal
+ * \brief Write a DTMF stream out to a channel
+ */
 static int bridge_channel_write_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf)
 {
        return bridge_channel_write_action_data(bridge_channel,
@@ -978,16 +1040,17 @@ static int bridge_channel_write_dtmf_stream(struct ast_bridge_channel *bridge_ch
 }
 
 /*!
- * \internal \brief Internal function that executes a feature on a bridge channel
+ * \internal
+ * \brief Internal function that executes a feature on a bridge channel
  * \note Neither the bridge nor the bridge_channel locks should be held when entering
  * this function.
  */
-static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
+static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel, const char *starting_dtmf)
 {
        struct ast_bridge_features *features = bridge_channel->features;
        struct ast_bridge_hook_dtmf *hook = NULL;
-       char dtmf[MAXIMUM_DTMF_FEATURE_STRING] = "";
-       size_t dtmf_len = 0;
+       char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
+       size_t dtmf_len;
        unsigned int digit_timeout;
        RAII_VAR(struct ast_features_general_config *, gen_cfg, NULL, ao2_cleanup);
 
@@ -1001,14 +1064,46 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
        digit_timeout = gen_cfg->featuredigittimeout;
        ast_channel_unlock(bridge_channel->chan);
 
-       /* The channel is now under our control and we don't really want any begin frames to do our DTMF matching so disable 'em at the core level */
-       ast_set_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_END_DTMF_ONLY);
+       if (ast_strlen_zero(starting_dtmf)) {
+               dtmf[0] = '\0';
+               dtmf_len = 0;
+       } else {
+               ast_copy_string(dtmf, starting_dtmf, sizeof(dtmf));
+               dtmf_len = strlen(dtmf);
+       }
 
-       /* Wait for DTMF on the channel and put it into a buffer. If the buffer matches any feature hook execute the hook. */
-       do {
+       /*
+        * Check if any feature DTMF hooks match or could match and
+        * try to collect more DTMF digits.
+        */
+       for (;;) {
                int res;
 
-               /* If the above timed out simply exit */
+               if (dtmf_len) {
+                       ast_debug(1, "DTMF feature string on %p(%s) is now '%s'\n",
+                               bridge_channel, ast_channel_name(bridge_channel->chan), dtmf);
+
+                       /* See if a DTMF feature hook matches or can match */
+                       hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_PARTIAL_KEY);
+                       if (!hook) {
+                               ast_debug(1, "No DTMF feature hooks on %p(%s) match '%s'\n",
+                                       bridge_channel, ast_channel_name(bridge_channel->chan), dtmf);
+                               break;
+                       }
+                       if (strlen(hook->dtmf.code) == dtmf_len) {
+                               ast_debug(1, "DTMF feature hook %p matched DTMF string '%s' on %p(%s)\n",
+                                       hook, dtmf, bridge_channel, ast_channel_name(bridge_channel->chan));
+                               break;
+                       }
+                       ao2_ref(hook, -1);
+                       hook = NULL;
+
+                       if (ARRAY_LEN(dtmf) - 1 <= dtmf_len) {
+                               /* We have reached the maximum length of a DTMF feature string. */
+                               break;
+                       }
+               }
+
                res = ast_waitfordigit(bridge_channel->chan, digit_timeout);
                if (!res) {
                        ast_debug(1, "DTMF feature string collection on %p(%s) timed out\n",
@@ -1021,37 +1116,15 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
                        break;
                }
 
-/* BUGBUG need to record the duration of DTMF digits so when the string is played back, they are reproduced. */
-               /* Add the above DTMF into the DTMF string so we can do our matching */
-               dtmf[dtmf_len++] = res;
-               ast_debug(1, "DTMF feature string on %p(%s) is now '%s'\n",
-                       bridge_channel, ast_channel_name(bridge_channel->chan), dtmf);
-
-               /* See if a DTMF feature hook matches or can match */
-               hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_PARTIAL_KEY);
-               if (!hook) {
-                       ast_debug(1, "No DTMF feature hooks on %p(%s) match '%s'\n",
-                               bridge_channel, ast_channel_name(bridge_channel->chan), dtmf);
-                       break;
-               }
-               if (strlen(hook->dtmf.code) == dtmf_len) {
-                       ast_debug(1, "DTMF feature hook %p matched DTMF string '%s' on %p(%s)\n",
-                               hook, dtmf, bridge_channel, ast_channel_name(bridge_channel->chan));
-                       break;
-               }
-               ao2_ref(hook, -1);
-               hook = NULL;
-
-               /* Stop if we have reached the maximum length of a DTMF feature string. */
-       } while (dtmf_len < ARRAY_LEN(dtmf) - 1);
-
-       /* Since we are done bringing DTMF in return to using both begin and end frames */
-       ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_END_DTMF_ONLY);
+               /* Add the new DTMF into the DTMF string so we can do our matching */
+               dtmf[dtmf_len] = res;
+               dtmf[++dtmf_len] = '\0';
+       }
 
-       /* If a hook was actually matched execute it on this channel, otherwise stream up the DTMF to the other channels */
        if (hook) {
                int remove_me;
 
+               /* Execute the matched hook on this channel. */
                remove_me = hook->generic.callback(bridge_channel, hook->generic.hook_pvt);
                if (remove_me) {
                        ast_debug(1, "DTMF hook %p is being removed from %p(%s)\n",
@@ -1067,14 +1140,18 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
                 * here if the hook did not already change the state.
                 */
                if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) {
-                       bridge_channel_handle_hangup(bridge_channel);
+                       ast_bridge_channel_kick(bridge_channel, 0);
                }
        } else if (features->dtmf_passthrough) {
+               /* Stream any collected DTMF to the other channels. */
                bridge_channel_write_dtmf_stream(bridge_channel, dtmf);
        }
 }
 
-/*! \internal \brief Indicate that a bridge_channel is talking */
+/*!
+ * \internal
+ * \brief Indicate that a bridge_channel is talking
+ */
 static void bridge_channel_talking(struct ast_bridge_channel *bridge_channel, int talking)
 {
        struct ast_bridge_features *features = bridge_channel->features;
@@ -1115,7 +1192,10 @@ struct blind_transfer_data {
        char context[AST_MAX_CONTEXT];
 };
 
-/*! \internal \brief Execute after bridge actions on a channel when it leaves a bridge */
+/*!
+ * \internal
+ * \brief Execute after bridge actions on a channel when it leaves a bridge
+ */
 static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *data)
 {
        RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup);
@@ -1152,7 +1232,10 @@ static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *da
        ast_party_connected_line_free(&connected_target);
 }
 
-/*! \internal \brief Execute logic to cleanup when after bridge fails */
+/*!
+ * \internal
+ * \brief Execute logic to cleanup when after bridge fails
+ */
 static void after_bridge_move_channel_fail(enum ast_bridge_after_cb_reason reason, void *data)
 {
        RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup);
@@ -1162,15 +1245,21 @@ static void after_bridge_move_channel_fail(enum ast_bridge_after_cb_reason reaso
        ast_softhangup(chan_target, AST_SOFTHANGUP_DEV);
 }
 
-/*! \internal \brief Perform a blind transfer on a channel in a bridge */
+/*!
+ * \internal
+ * \brief Perform a blind transfer on a channel in a bridge
+ */
 static void bridge_channel_blind_transfer(struct ast_bridge_channel *bridge_channel,
                struct blind_transfer_data *blind_data)
 {
        ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1);
-       bridge_channel_handle_hangup(bridge_channel);
+       ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING);
 }
 
-/*! \internal \brief Perform an attended transfer on a channel in a bridge */
+/*!
+ * \internal
+ * \brief Perform an attended transfer on a channel in a bridge
+ */
 static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_channel,
                const char *target_chan_name)
 {
@@ -1180,7 +1269,7 @@ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_c
        chan_target = ast_channel_get_by_name(target_chan_name);
        if (!chan_target) {
                /* Dang, it disappeared somehow */
-               bridge_channel_handle_hangup(bridge_channel);
+               ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING);
                return;
        }
 
@@ -1197,7 +1286,7 @@ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_c
                /* Release the ref we tried to pass to ast_bridge_set_after_callback(). */
                ast_channel_unref(chan_target);
        }
-       bridge_channel_handle_hangup(bridge_channel);
+       ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING);
 }
 
 /*!
@@ -1213,13 +1302,6 @@ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_c
 static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_channel, struct ast_frame *action)
 {
        switch (action->subclass.integer) {
-       case BRIDGE_CHANNEL_ACTION_FEATURE:
-               bridge_channel_suspend(bridge_channel);
-               ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-               bridge_channel_feature(bridge_channel);
-               ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-               bridge_channel_unsuspend(bridge_channel);
-               break;
        case BRIDGE_CHANNEL_ACTION_DTMF_STREAM:
                bridge_channel_suspend(bridge_channel);
                ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
@@ -1247,11 +1329,7 @@ static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_chann
                bridge_channel_unsuspend(bridge_channel);
                break;
        case BRIDGE_CHANNEL_ACTION_CALLBACK:
-               bridge_channel_suspend(bridge_channel);
-               ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
                bridge_channel_do_callback(bridge_channel, action->data.ptr);
-               ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-               bridge_channel_unsuspend(bridge_channel);
                break;
        case BRIDGE_CHANNEL_ACTION_PARK:
                bridge_channel_suspend(bridge_channel);
@@ -1293,7 +1371,7 @@ static void bridge_channel_dissolve_check(struct ast_bridge_channel *bridge_chan
        if (!bridge->num_channels
                && ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY)) {
                /* Last channel leaving the bridge turns off the lights. */
-               bridge_dissolve(bridge);
+               bridge_dissolve(bridge, ast_channel_hangupcause(bridge_channel->chan));
                return;
        }
 
@@ -1304,14 +1382,24 @@ static void bridge_channel_dissolve_check(struct ast_bridge_channel *bridge_chan
                        || (bridge_channel->features->usable
                                && ast_test_flag(&bridge_channel->features->feature_flags,
                                        AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP))) {
-                       bridge_dissolve(bridge);
+                       bridge_dissolve(bridge, ast_channel_hangupcause(bridge_channel->chan));
                        return;
                }
                break;
        default:
                break;
        }
-/* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */
+
+       if (bridge->num_lonely && bridge->num_lonely == bridge->num_channels) {
+               /*
+                * This will start a chain reaction where each channel leaving
+                * enters this function and causes the next to leave as long as
+                * there aren't non-lonely channels in the bridge.
+                */
+               ast_bridge_channel_leave_bridge(AST_LIST_FIRST(&bridge->channels),
+                       BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE,
+                       ast_channel_hangupcause(bridge_channel->chan));
+       }
 }
 
 void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel)
@@ -1348,6 +1436,9 @@ void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel)
        if (!bridge_channel->suspended) {
                --bridge->num_active;
        }
+       if (ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_LONELY)) {
+               --bridge->num_lonely;
+       }
        --bridge->num_channels;
        AST_LIST_REMOVE(&bridge->channels, bridge_channel, entry);
        bridge->v_table->pull(bridge, bridge_channel);
@@ -1403,6 +1494,9 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
        bridge_channel->just_joined = 1;
        AST_LIST_INSERT_TAIL(&bridge->channels, bridge_channel, entry);
        ++bridge->num_channels;
+       if (ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_LONELY)) {
+               ++bridge->num_lonely;
+       }
        if (!bridge_channel->suspended) {
                ++bridge->num_active;
        }
@@ -1416,9 +1510,9 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
                bridge->v_table->name,
                bridge->uniqueid);
 
-       ast_bridge_publish_enter(bridge, bridge_channel->chan);
+       ast_bridge_publish_enter(bridge, bridge_channel->chan, swap ? swap->chan : NULL);
        if (swap) {
-               ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+               ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0);
                bridge_channel_internal_pull(swap);
        }
 
@@ -1448,27 +1542,20 @@ static void bridge_channel_handle_control(struct ast_bridge_channel *bridge_chan
        struct ast_channel *chan;
        struct ast_option_header *aoh;
        int is_caller;
-       int intercept_failed;
 
        chan = bridge_channel->chan;
        switch (fr->subclass.integer) {
        case AST_CONTROL_REDIRECTING:
                is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING);
-               bridge_channel_suspend(bridge_channel);
-               intercept_failed = ast_channel_redirecting_sub(NULL, chan, fr, 1)
-                       && ast_channel_redirecting_macro(NULL, chan, fr, is_caller, 1);
-               bridge_channel_unsuspend(bridge_channel);
-               if (intercept_failed) {
+               if (ast_channel_redirecting_sub(NULL, chan, fr, 1) &&
+                       ast_channel_redirecting_macro(NULL, chan, fr, is_caller, 1)) {
                        ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
                }
                break;
        case AST_CONTROL_CONNECTED_LINE:
                is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING);
-               bridge_channel_suspend(bridge_channel);
-               intercept_failed = ast_channel_connected_line_sub(NULL, chan, fr, 1)
-                       && ast_channel_connected_line_macro(NULL, chan, fr, is_caller, 1);
-               bridge_channel_unsuspend(bridge_channel);
-               if (intercept_failed) {
+               if (ast_channel_connected_line_sub(NULL, chan, fr, 1) &&
+                       ast_channel_connected_line_macro(NULL, chan, fr, is_caller, 1)) {
                        ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
                }
                break;
@@ -1481,11 +1568,6 @@ static void bridge_channel_handle_control(struct ast_bridge_channel *bridge_chan
  * When the receiving channel is pulled from the bridge it needs to generate its own UNHOLD.
  * Something similar needs to be done for DTMF begin/end.
  */
-       case AST_CONTROL_VIDUPDATE:
-       case AST_CONTROL_SRCUPDATE:
-       case AST_CONTROL_SRCCHANGE:
-       case AST_CONTROL_T38_PARAMETERS:
-/* BUGBUG may have to do something with a jitter buffer for these. */
                ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
                break;
        case AST_CONTROL_OPTION:
@@ -1576,22 +1658,35 @@ static struct ast_frame *bridge_handle_dtmf(struct ast_bridge_channel *bridge_ch
        struct ast_bridge_hook_dtmf *hook;
        char dtmf[2];
 
-/* BUGBUG the feature hook matching needs to be done here.  Any matching feature hook needs to be queued onto the bridge_channel.  Also the feature hook digit timeout needs to be handled. */
-/* BUGBUG the AMI atxfer action just sends DTMF end events to initiate DTMF atxfer and dial the extension.  Another reason the DTMF hook matching needs rework. */
-       /* See if this DTMF matches the beginnings of any feature hooks, if so we switch to the feature state to either execute the feature or collect more DTMF */
+       /* See if this DTMF matches the beginning of any feature hooks. */
        dtmf[0] = frame->subclass.integer;
        dtmf[1] = '\0';
        hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_PARTIAL_KEY);
        if (hook) {
-               struct ast_frame action = {
-                       .frametype = AST_FRAME_BRIDGE_ACTION,
-                       .subclass.integer = BRIDGE_CHANNEL_ACTION_FEATURE,
-               };
+               enum ast_frame_type frametype = frame->frametype;
 
                ast_frfree(frame);
                frame = NULL;
-               ast_bridge_channel_queue_frame(bridge_channel, &action);
+
                ao2_ref(hook, -1);
+
+               /* Collect any more needed DTMF to execute a hook. */
+               bridge_channel_suspend(bridge_channel);
+               ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+               switch (frametype) {
+               case AST_FRAME_DTMF_BEGIN:
+                       bridge_channel_feature(bridge_channel, NULL);
+                       break;
+               case AST_FRAME_DTMF_END:
+                       bridge_channel_feature(bridge_channel, dtmf);
+                       break;
+               default:
+                       /* Unexpected frame type. */
+                       ast_assert(0);
+                       break;
+               }
+               ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+               bridge_channel_unsuspend(bridge_channel);
        }
 
        return frame;
@@ -1615,14 +1710,14 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel)
        }
 
        if (!frame) {
-               bridge_channel_handle_hangup(bridge_channel);
+               ast_bridge_channel_kick(bridge_channel, 0);
                return;
        }
        switch (frame->frametype) {
        case AST_FRAME_CONTROL:
                switch (frame->subclass.integer) {
                case AST_CONTROL_HANGUP:
-                       bridge_channel_handle_hangup(bridge_channel);
+                       ast_bridge_channel_kick(bridge_channel, 0);
                        ast_frfree(frame);
                        return;
 /* BUGBUG This is where incoming HOLD/UNHOLD memory should register.  Write UNHOLD into bridge when this channel is pulled. */
@@ -1631,12 +1726,11 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel)
                }
                break;
        case AST_FRAME_DTMF_BEGIN:
+       case AST_FRAME_DTMF_END:
                frame = bridge_handle_dtmf(bridge_channel, frame);
                if (!frame) {
                        return;
                }
-               /* Fall through */
-       case AST_FRAME_DTMF_END:
                if (!bridge_channel->features->dtmf_passthrough) {
                        ast_frfree(frame);
                        return;
@@ -1648,8 +1742,6 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel)
        }
 
        /* Simply write the frame out to the bridge technology. */
-/* BUGBUG The tech is where AST_CONTROL_ANSWER hook should go. (early bridge) */
-/* BUGBUG The tech is where incoming BUSY/CONGESTION hangup should happen? (early bridge) */
        bridge_channel_write_frame(bridge_channel, frame);
        ast_frfree(frame);
 }
@@ -1708,7 +1800,7 @@ static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel)
        ast_bridge_channel_lock(bridge_channel);
        if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
        } else if (bridge_channel->suspended) {
-/* BUGBUG the external party use of suspended will go away as will these references because this is the bridge channel thread */
+/* XXX ASTERISK-21271 the external party use of suspended will go away as will these references because this is the bridge channel thread */
                ast_debug(1, "Bridge %s: %p(%s) is going into a signal wait\n",
                        bridge_channel->bridge->uniqueid, bridge_channel,
                        ast_channel_name(bridge_channel->chan));
@@ -1803,28 +1895,37 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
                bridge_channel, ast_channel_name(bridge_channel->chan));
 
        /*
-        * Get "in the bridge" before pushing the channel for any
-        * masquerades on the channel to happen before bridging.
+        * Directly locking the bridge is safe here because nobody else
+        * knows about this bridge_channel yet.
+        */
+       ast_bridge_lock(bridge_channel->bridge);
+
+       /* Make sure we're still good to be put into a bridge
         */
        ast_channel_lock(bridge_channel->chan);
+       if (ast_channel_internal_bridge(bridge_channel->chan)
+               || ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_ZOMBIE)) {
+               ast_channel_unlock(bridge_channel->chan);
+               ast_bridge_unlock(bridge_channel->bridge);
+               ast_debug(1, "Bridge %s: %p(%s) failed to join Bridge\n",
+                       bridge_channel->bridge->uniqueid,
+                       bridge_channel,
+                       ast_channel_name(bridge_channel->chan));
+               return -1;
+       }
        ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge);
        ast_channel_unlock(bridge_channel->chan);
 
        /* Add the jitterbuffer if the channel requires it */
        ast_jb_enable_for_channel(bridge_channel->chan);
 
-       /*
-        * Directly locking the bridge is safe here because nobody else
-        * knows about this bridge_channel yet.
-        */
-       ast_bridge_lock(bridge_channel->bridge);
-
        if (!bridge_channel->bridge->callid) {
                bridge_channel->bridge->callid = ast_read_threadstorage_callid();
        }
 
        if (bridge_channel_internal_push(bridge_channel)) {
-               ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+               ast_bridge_channel_leave_bridge(bridge_channel,
+                       BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, bridge_channel->bridge->cause);
                res = -1;
        }
        bridge_reconfigured(bridge_channel->bridge, 1);