Fix several interrelated issues dealing with the holding bridge technology.
[asterisk/asterisk.git] / main / bridge_channel.c
index 1164e4f..f6d909c 100644 (file)
@@ -108,7 +108,10 @@ void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel,
        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)) {
@@ -260,16 +263,7 @@ 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)
 {
        struct ast_bridge_features *features = bridge_channel->features;
        struct ast_bridge_hook *hook;
@@ -283,7 +277,7 @@ static void bridge_channel_handle_hangup(struct ast_bridge_channel *bridge_chann
                if (hook->type != AST_BRIDGE_HOOK_TYPE_HANGUP) {
                        continue;
                }
-               remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
+               remove_me = hook->callback(bridge_channel, hook->hook_pvt);
                if (remove_me) {
                        ast_debug(1, "Hangup hook %p is being removed from %p(%s)\n",
                                hook, bridge_channel, ast_channel_name(bridge_channel->chan));
@@ -328,6 +322,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 +562,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 +594,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);
        }
        if (moh_class) {
                ast_bridge_channel_write_unhold(bridge_channel);
@@ -683,6 +761,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 +774,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 +797,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 +814,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 +823,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 +852,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 +902,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 +914,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,16 +931,16 @@ 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);
                }
 
                ast_debug(1, "Executing hook %p on %p(%s)\n",
                        hook, bridge_channel, ast_channel_name(bridge_channel->chan));
-               interval = hook->generic.callback(bridge_channel->bridge, bridge_channel,
-                       hook->generic.hook_pvt);
+               interval = hook->generic.callback(bridge_channel, hook->generic.hook_pvt);
 
                ast_heap_wrlock(interval_hooks);
                if (ast_heap_peek(interval_hooks, hook->timer.heap_index) != hook
@@ -965,13 +988,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,
@@ -979,16 +1005,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);
 
@@ -1002,14 +1029,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",
@@ -1022,39 +1081,16 @@ 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;
 
-               remove_me = hook->generic.callback(bridge_channel->bridge, bridge_channel,
-                       hook->generic.hook_pvt);
+               /* 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",
                                hook, bridge_channel, ast_channel_name(bridge_channel->chan));
@@ -1069,14 +1105,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);
                }
        } 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;
@@ -1117,7 +1157,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);
@@ -1154,7 +1197,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);
@@ -1164,15 +1210,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);
 }
 
-/*! \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)
 {
@@ -1182,7 +1234,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);
                return;
        }
 
@@ -1199,7 +1251,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);
 }
 
 /*!
@@ -1215,13 +1267,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);
@@ -1249,11 +1294,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);
@@ -1313,7 +1354,12 @@ static void bridge_channel_dissolve_check(struct ast_bridge_channel *bridge_chan
        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);
+       }
 }
 
 void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel)
@@ -1350,6 +1396,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);
@@ -1405,6 +1454,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;
        }
@@ -1418,7 +1470,7 @@ 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);
                bridge_channel_internal_pull(swap);
@@ -1450,27 +1502,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;
@@ -1578,22 +1623,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;
@@ -1617,14 +1675,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);
                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);
                        ast_frfree(frame);
                        return;
 /* BUGBUG This is where incoming HOLD/UNHOLD memory should register.  Write UNHOLD into bridge when this channel is pulled. */
@@ -1633,12 +1691,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;
@@ -1650,8 +1707,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);
 }
@@ -1710,7 +1765,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));
@@ -1782,7 +1837,7 @@ static void bridge_channel_event_join_leave(struct ast_bridge_channel *bridge_ch
                ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
                do {
                        if (hook->type == type) {
-                               hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
+                               hook->callback(bridge_channel, hook->hook_pvt);
                                ao2_unlink(features->other_hooks, hook);
                        }
                        ao2_ref(hook, -1);
@@ -1794,8 +1849,9 @@ static void bridge_channel_event_join_leave(struct ast_bridge_channel *bridge_ch
 }
 
 /*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */
-void bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
+int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
 {
+       int res = 0;
        ast_format_copy(&bridge_channel->read_format, ast_channel_readformat(bridge_channel->chan));
        ast_format_copy(&bridge_channel->write_format, ast_channel_writeformat(bridge_channel->chan));
 
@@ -1804,28 +1860,37 @@ void 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);
+               res = -1;
        }
        bridge_reconfigured(bridge_channel->bridge, 1);
 
@@ -1881,6 +1946,8 @@ void bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
        ast_channel_unlock(bridge_channel->chan);
 
        ast_bridge_channel_restore_formats(bridge_channel);
+
+       return res;
 }
 
 int bridge_channel_internal_queue_blind_transfer(struct ast_channel *transferee,