Bridge API: Set a cause code on a channel when it is ejected from a bridge.
[asterisk/asterisk.git] / main / bridge_channel.c
index 013475c..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,17 +887,13 @@ struct bridge_park {
  */
 static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload)
 {
-       RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
-               ast_parking_get_bridge_features(),
-               ao2_cleanup);
-
-       if (!parking_provider) {
+       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 (parking_provider->parking_park_bridge_channel(bridge_channel, payload->parkee_uuid,
+       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)) {
                ast_log(AST_LOG_WARNING, "Error occurred while parking %s\n",
@@ -811,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
  *
@@ -904,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);
@@ -921,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);
                }
@@ -977,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,
@@ -1091,7 +1140,7 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel, co
                 * 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. */
@@ -1099,7 +1148,10 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel, co
        }
 }
 
-/*! \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;
@@ -1140,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);
@@ -1177,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);
@@ -1187,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)
 {
@@ -1205,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;
        }
 
@@ -1222,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);
 }
 
 /*!
@@ -1265,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);
@@ -1311,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;
        }
 
@@ -1322,7 +1382,7 @@ 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;
@@ -1331,9 +1391,14 @@ static void bridge_channel_dissolve_check(struct ast_bridge_channel *bridge_chan
        }
 
        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);
+               /*
+                * 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));
        }
 }
 
@@ -1447,7 +1512,7 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
 
        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);
        }
 
@@ -1503,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:
@@ -1650,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. */
@@ -1864,7 +1924,8 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
        }
 
        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);