diff --git a/apps/app_agent_pool.c b/apps/app_agent_pool.c
index 956a97e3f566e9fc7fa7326b7163211db99fad0c..a9f256e3b16730c7de906a049d1dd1607899fff2 100644
--- a/apps/app_agent_pool.c
+++ b/apps/app_agent_pool.c
@@ -43,6 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/bridging.h"
 #include "asterisk/bridging_internal.h"
 #include "asterisk/bridging_basic.h"
+#include "asterisk/bridging_after.h"
 #include "asterisk/config_options.h"
 #include "asterisk/features_config.h"
 #include "asterisk/astobj2.h"
@@ -1055,7 +1056,7 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru
 
 	if (!caller_bridge) {
 		/* Reset agent. */
-		ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
 		return;
 	}
 	res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
@@ -1063,7 +1064,7 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru
 	if (res) {
 		/* Reset agent. */
 		ast_bridge_destroy(caller_bridge);
-		ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
 		return;
 	}
 	ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
@@ -1159,13 +1160,13 @@ static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bri
 
 	if (deferred_logoff) {
 		ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
-		ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
 	} else if (probation_timedout) {
 		ast_debug(1, "Agent %s: Login complete.\n", agent->username);
 		agent_devstate_changed(agent->username);
 	} else if (ack_timedout) {
 		ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
-		ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
 	} else if (wrapup_timedout) {
 		ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
 		agent_devstate_changed(agent->username);
@@ -1175,7 +1176,7 @@ static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bri
 }
 
 static void agent_after_bridge_cb(struct ast_channel *chan, void *data);
-static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason, void *data);
+static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data);
 
 /*!
  * \internal
@@ -1252,7 +1253,7 @@ static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_cha
 	}
 
 	if (swap) {
-		res = ast_after_bridge_callback_set(chan, agent_after_bridge_cb,
+		res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb,
 			agent_after_bridge_cb_failed, chan);
 		if (res) {
 			ast_channel_remove_bridge_role(chan, "holding_participant");
@@ -1270,7 +1271,7 @@ static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_cha
 		 * agent will have some slightly different behavior in corner
 		 * cases.
 		 */
-		ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
 		return 0;
 	}
 
@@ -1620,7 +1621,7 @@ static void agent_after_bridge_cb(struct ast_channel *chan, void *data)
 	ao2_ref(agent, -1);
 }
 
-static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason, void *data)
+static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
 {
 	struct ast_channel *chan = data;
 	struct agent_pvt *agent;
@@ -1631,7 +1632,7 @@ static void agent_after_bridge_cb_failed(enum ast_after_bridge_cb_reason reason,
 	}
 	ast_log(LOG_WARNING, "Agent %s: Forced logout.  Lost control of %s because: %s\n",
 		agent->username, ast_channel_name(chan),
-		ast_after_bridge_cb_reason_string(reason));
+		ast_bridge_after_cb_reason_string(reason));
 	agent_lock(agent);
 	agent_logout(agent);
 	ao2_ref(agent, -1);
@@ -1704,7 +1705,7 @@ static void caller_abort_agent(struct agent_pvt *agent)
 	}
 
 	/* Kick the agent out of the holding bridge to reset it. */
-	ast_bridge_channel_leave_bridge_nolock(logged, AST_BRIDGE_CHANNEL_STATE_END);
+	ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END);
 	ast_bridge_channel_unlock(logged);
 }
 
@@ -1714,7 +1715,7 @@ static int caller_safety_timeout(struct ast_bridge *bridge, struct ast_bridge_ch
 
 	if (agent->state == AGENT_STATE_CALL_PRESENT) {
 		ast_verb(3, "Agent '%s' did not respond.  Safety timeout.\n", agent->username);
-		ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
 		caller_abort_agent(agent);
 	}
 
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 0a43197b440ae5aab67d9bb13199e7fdaa1e94ec..6d7c1abb50b637c192698012fe1ad05b2111c601 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -67,6 +67,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/dial.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/bridging.h"
+#include "asterisk/bridging_after.h"
 #include "asterisk/features_config.h"
 
 /*** DOCUMENTATION
@@ -201,7 +202,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<argument name="exten" required="false" />
 					<argument name="priority" required="true" />
 					<para>If the call is answered, transfer the calling party to
-					the specified <replaceable>priority</replaceable> and the called party to the specified 
+					the specified <replaceable>priority</replaceable> and the called party to the specified
 					<replaceable>priority</replaceable> plus one.</para>
 					<note>
 						<para>You cannot use any additional action post answer options in conjunction with this option.</para>
@@ -290,7 +291,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<argument name="arg" multiple="true">
 						<para>Macro arguments</para>
 					</argument>
-					<para>Execute the specified <replaceable>macro</replaceable> for the <emphasis>called</emphasis> channel 
+					<para>Execute the specified <replaceable>macro</replaceable> for the <emphasis>called</emphasis> channel
 					before connecting to the calling channel. Arguments can be specified to the Macro
 					using <literal>^</literal> as a delimiter. The macro can set the variable
 					<variable>MACRO_RESULT</variable> to specify the following actions after the macro is
@@ -332,7 +333,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 						<para>With <replaceable>delete</replaceable> set to <literal>1</literal>, the introduction will
 						always be deleted.</para>
 					</argument>
-					<para>This option is a modifier for the call screening/privacy mode. (See the 
+					<para>This option is a modifier for the call screening/privacy mode. (See the
 					<literal>p</literal> and <literal>P</literal> options.) It specifies
 					that no introductions are to be saved in the <directory>priv-callerintros</directory>
 					directory.</para>
@@ -353,7 +354,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 					<argument name="mode">
 						<para>With <replaceable>mode</replaceable> either not specified or set to <literal>1</literal>,
 						the originator hanging up will cause the phone to ring back immediately.</para>
-						<para>With <replaceable>mode</replaceable> set to <literal>2</literal>, when the operator 
+						<para>With <replaceable>mode</replaceable> set to <literal>2</literal>, when the operator
 						flashes the trunk, it will ring their phone back.</para>
 					</argument>
 					<para>Enables <emphasis>operator services</emphasis> mode.  This option only
@@ -1071,7 +1072,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
 			/* If we are calling a single channel, and not providing ringback or music, */
 			/* then, make them compatible for in-band tone purpose */
 			if (ast_channel_make_compatible(outgoing->chan, in) < 0) {
-				/* If these channels can not be made compatible, 
+				/* If these channels can not be made compatible,
 				 * there is no point in continuing.  The bridge
 				 * will just fail if it gets that far.
 				 */
@@ -1765,7 +1766,7 @@ static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
 
 		/*! \page DialPrivacy Dial Privacy scripts
 		 * \par priv-callee-options script:
-		 * \li Dial 1 if you wish this caller to reach you directly in the future, 
+		 * \li Dial 1 if you wish this caller to reach you directly in the future,
 		 * 	and immediately connect to their incoming call.
 		 * \li Dial 2 if you wish to send this caller to voicemail now and forevermore.
 		 * \li Dial 3 to send this caller to the torture menus, now and forevermore.
@@ -1891,7 +1892,7 @@ static int setup_privacy_args(struct privacy_args *pa,
 	} else if (ast_test_flag64(opts, OPT_SCREEN_NOCALLERID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) {
 		ast_verb(3, "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val);
 	}
-	
+
 	if (pa->privdb_val == AST_PRIVACY_DENY) {
 		ast_verb(3, "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n");
 		ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
@@ -2021,14 +2022,14 @@ static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_ch
 		ast_channel_lock(chan);
 		context = ast_strdupa(ast_channel_context(chan));
 		ast_channel_unlock(chan);
-		ast_after_bridge_set_h(peer, context);
+		ast_bridge_set_after_h(peer, context);
 	} else if (ast_test_flag64(opts, OPT_CALLEE_GO_ON)) {
 		ast_channel_lock(chan);
 		context = ast_strdupa(ast_channel_context(chan));
 		extension = ast_strdupa(ast_channel_exten(chan));
 		priority = ast_channel_priority(chan);
 		ast_channel_unlock(chan);
-		ast_after_bridge_set_go_on(peer, context, extension, priority,
+		ast_bridge_set_after_go_on(peer, context, extension, priority,
 			opt_args[OPT_ARG_CALLEE_GO_ON]);
 	}
 }
@@ -2444,7 +2445,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 			/* We are on the only destination. */
 			ast_rtp_instance_early_bridge_make_compatible(tc, chan);
 		}
-		
+
 		/* Inherit specially named variables from parent channel */
 		ast_channel_inherit_variables(chan, tc);
 		ast_channel_datastore_inherit(chan, tc);
@@ -2698,7 +2699,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 		/* If appropriate, log that we have a destination channel and set the answer time */
 		if (ast_channel_name(peer))
 			pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", ast_channel_name(peer));
-		
+
 		ast_channel_lock(peer);
 		number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER");
 		if (ast_strlen_zero(number)) {
@@ -2967,7 +2968,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 				ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer));
 			}
 			setup_peer_after_bridge_goto(chan, peer, &opts, opt_args);
-			if (ast_after_bridge_goto_setup(peer)
+			if (ast_bridge_setup_after_goto(peer)
 				|| ast_pbx_start(peer)) {
 				ast_autoservice_chan_hangup_peer(chan, peer);
 			}
@@ -2997,7 +2998,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 			config.end_bridge_callback = end_bridge_callback;
 			config.end_bridge_callback_data = chan;
 			config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
-			
+
 			if (moh) {
 				moh = 0;
 				ast_moh_stop(chan);
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 8c5291a0df4284b4063044561f8944b5541e5477..6b89a7046363967576f94b670596e55d2e51cb2a 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -109,6 +109,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/stasis_channels.h"
 #include "asterisk/stasis_message_router.h"
 #include "asterisk/bridging.h"
+#include "asterisk/bridging_after.h"
 
 /* Define, to debug reference counts on queues, without debugging reference counts on queue members */
 /* #define REF_DEBUG_ONLY_QUEUES */
@@ -5293,7 +5294,7 @@ static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_ch
 		extension = ast_strdupa(ast_channel_exten(chan));
 		priority = ast_channel_priority(chan);
 		ast_channel_unlock(chan);
-		ast_after_bridge_set_go_on(peer, context, extension, priority,
+		ast_bridge_set_after_go_on(peer, context, extension, priority,
 			opt_args[OPT_ARG_CALLEE_GO_ON]);
 	}
 }
diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c
index 1252240ac9c9cd92d7f778b9baa38aef7c6989f3..00c88710cdde5c2810e8315421f819c6220719fb 100644
--- a/bridges/bridge_builtin_features.c
+++ b/bridges/bridge_builtin_features.c
@@ -483,7 +483,7 @@ static int feature_hangup(struct ast_bridge *bridge, struct ast_bridge_channel *
 	 * bridge_channel to force the channel out of the bridge and the
 	 * core takes care of the rest.
 	 */
-	ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
 	return 0;
 }
 
diff --git a/bridges/bridge_builtin_interval_features.c b/bridges/bridge_builtin_interval_features.c
index 7e5291ac8e2c4f780bbeec9b91ae4bce9f482349..5a02991d4efaac4e6da532bb07f6aaa8b47a2fac 100644
--- a/bridges/bridge_builtin_interval_features.c
+++ b/bridges/bridge_builtin_interval_features.c
@@ -58,7 +58,7 @@ static int bridge_features_duration_callback(struct ast_bridge *bridge, struct a
 		ast_stream_and_wait(bridge_channel->chan, limits->duration_sound, AST_DIGIT_NONE);
 	}
 
-	ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
 
 	ast_test_suite_event_notify("BRIDGE_TIMELIMIT", "Channel1: %s", ast_channel_name(bridge_channel->chan));
 	return -1;
@@ -114,7 +114,7 @@ static int bridge_features_connect_callback(struct ast_bridge *bridge, struct as
 {
 	struct ast_bridge_features_limits *limits = hook_pvt;
 
-	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+	if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
 		return -1;
 	}
 
@@ -126,7 +126,7 @@ static int bridge_features_warning_callback(struct ast_bridge *bridge, struct as
 {
 	struct ast_bridge_features_limits *limits = hook_pvt;
 
-	if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+	if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
 		/* If we aren't in the wait state, something more important than this warning is happening and we should skip it. */
 		limits_interval_playback(bridge, bridge_channel, limits, limits->warning_sound);
 	}
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index dcf11ec6a16ea0dfa5c0279f53a6469386b154ab..503c0b9a58c6fd941bfc832fbd26a9bf2f83ac53 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -576,7 +576,7 @@ static void softmix_bridge_write_voice(struct ast_bridge *bridge, struct ast_bri
 	ast_mutex_unlock(&sc->lock);
 
 	if (update_talking != -1) {
-		ast_bridge_notify_talking(bridge_channel, update_talking);
+		ast_bridge_channel_notify_talking(bridge_channel, update_talking);
 	}
 }
 
diff --git a/funcs/func_channel.c b/funcs/func_channel.c
index 4d098c3470c1c1fcd2e60b2546eb05646872854b..f4aa76322b9ccdc175ba9f3667f7f177845f7176 100644
--- a/funcs/func_channel.c
+++ b/funcs/func_channel.c
@@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/stringfields.h"
 #include "asterisk/global_datastores.h"
 #include "asterisk/bridging_basic.h"
+#include "asterisk/bridging_after.h"
 
 /*** DOCUMENTATION
 	<function name="CHANNELS" language="en_US">
@@ -531,7 +532,7 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
 
 		locked_copy_string(chan, buf,  ast_print_namedgroups(&tmp_str, ast_channel_named_pickupgroups(chan)), len);
 	} else if (!strcasecmp(data, "after_bridge_goto")) {
-		ast_after_bridge_goto_read(chan, buf, len);
+		ast_bridge_read_after_goto(chan, buf, len);
 	} else if (!strcasecmp(data, "amaflags")) {
 		ast_channel_lock(chan);
 		snprintf(buf, len, "%d", ast_channel_amaflags(chan));
@@ -575,9 +576,9 @@ static int func_channel_write_real(struct ast_channel *chan, const char *functio
 		locked_string_field_set(chan, userfield, value);
 	else if (!strcasecmp(data, "after_bridge_goto")) {
 		if (ast_strlen_zero(value)) {
-			ast_after_bridge_goto_discard(chan);
+			ast_bridge_discard_after_goto(chan);
 		} else {
-			ast_after_bridge_set_go_on(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), value);
+			ast_bridge_set_after_go_on(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), value);
 		}
 	} else if (!strcasecmp(data, "amaflags")) {
 		ast_channel_lock(chan);
diff --git a/include/asterisk/bridging.h b/include/asterisk/bridging.h
index bc16ae0bc1ce996e5e6ec210772a3aa793a902f2..7e22dba648d9c6f5178077b82efc427b4194de55 100644
--- a/include/asterisk/bridging.h
+++ b/include/asterisk/bridging.h
@@ -447,7 +447,7 @@ void ast_bridge_notify_masquerade(struct ast_channel *chan);
  * If channel specific features are enabled a pointer to the features structure
  * can be specified in the features parameter.
  */
-enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge,
+enum bridge_channel_state ast_bridge_join(struct ast_bridge *bridge,
 	struct ast_channel *chan,
 	struct ast_channel *swap,
 	struct ast_bridge_features *features,
@@ -884,202 +884,6 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
  */
 enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee,
 		struct ast_channel *to_transfer_target);
-/*!
- * \brief Set channel to goto specific location after the bridge.
- * \since 12.0.0
- *
- * \param chan Channel to setup after bridge goto location.
- * \param context Context to goto after bridge.
- * \param exten Exten to goto after bridge.
- * \param priority Priority to goto after bridge.
- *
- * \note chan is locked by this function.
- *
- * \details Add a channel datastore to setup the goto location
- * when the channel leaves the bridge and run a PBX from there.
- *
- * \return Nothing
- */
-void ast_after_bridge_set_goto(struct ast_channel *chan, const char *context, const char *exten, int priority);
-
-/*!
- * \brief Set channel to run the h exten after the bridge.
- * \since 12.0.0
- *
- * \param chan Channel to setup after bridge goto location.
- * \param context Context to goto after bridge.
- *
- * \note chan is locked by this function.
- *
- * \details Add a channel datastore to setup the goto location
- * when the channel leaves the bridge and run a PBX from there.
- *
- * \return Nothing
- */
-void ast_after_bridge_set_h(struct ast_channel *chan, const char *context);
-
-/*!
- * \brief Set channel to go on in the dialplan after the bridge.
- * \since 12.0.0
- *
- * \param chan Channel to setup after bridge goto location.
- * \param context Current context of the caller channel.
- * \param exten Current exten of the caller channel.
- * \param priority Current priority of the caller channel
- * \param parseable_goto User specified goto string from dialplan.
- *
- * \note chan is locked by this function.
- *
- * \details Add a channel datastore to setup the goto location
- * when the channel leaves the bridge and run a PBX from there.
- *
- * If parseable_goto then use the given context/exten/priority
- *   as the relative position for the parseable_goto.
- * Else goto the given context/exten/priority+1.
- *
- * \return Nothing
- */
-void ast_after_bridge_set_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto);
-
-/*!
- * \brief Setup any after bridge goto location to begin execution.
- * \since 12.0.0
- *
- * \param chan Channel to setup after bridge goto location.
- *
- * \note chan is locked by this function.
- *
- * \details Pull off any after bridge goto location datastore and
- * setup for dialplan execution there.
- *
- * \retval 0 on success.  The goto location is set for a PBX to run it.
- * \retval non-zero on error or no goto location.
- *
- * \note If the after bridge goto is set to run an h exten it is
- * run here immediately.
- */
-int ast_after_bridge_goto_setup(struct ast_channel *chan);
-
-/*!
- * \brief Run a PBX on any after bridge goto location.
- * \since 12.0.0
- *
- * \param chan Channel to execute after bridge goto location.
- *
- * \note chan is locked by this function.
- *
- * \details Pull off any after bridge goto location datastore
- * and run a PBX at that location.
- *
- * \note On return, the chan pointer is no longer valid because
- * the channel has hung up.
- *
- * \return Nothing
- */
-void ast_after_bridge_goto_run(struct ast_channel *chan);
-
-/*!
- * \brief Discard channel after bridge goto location.
- * \since 12.0.0
- *
- * \param chan Channel to discard after bridge goto location.
- *
- * \note chan is locked by this function.
- *
- * \return Nothing
- */
-void ast_after_bridge_goto_discard(struct ast_channel *chan);
-
-/*!
- * \brief Read after bridge goto if it exists
- * \since 12.0.0
- *
- * \param chan Channel to read the after bridge goto parseable goto string from
- * \param buffer Buffer to write the after bridge goto data to
- * \param buf_size size of the buffer being written to
- */
-void ast_after_bridge_goto_read(struct ast_channel *chan, char *buffer, size_t buf_size);
-
-/*! Reason the the after bridge callback will not be called. */
-enum ast_after_bridge_cb_reason {
-	/*! The datastore is being destroyed.  Likely due to hangup. (Enum value must be zero.) */
-	AST_AFTER_BRIDGE_CB_REASON_DESTROY,
-	/*! Something else replaced the callback with another. */
-	AST_AFTER_BRIDGE_CB_REASON_REPLACED,
-	/*! The callback was removed because of a masquerade. (fixup) */
-	AST_AFTER_BRIDGE_CB_REASON_MASQUERADE,
-	/*! The channel was departed from the bridge. */
-	AST_AFTER_BRIDGE_CB_REASON_DEPART,
-	/*! Was explicitly removed by external code. */
-	AST_AFTER_BRIDGE_CB_REASON_REMOVED,
-};
-
-/*!
- * \brief After bridge callback failed.
- * \since 12.0.0
- *
- * \param reason Reason callback is failing.
- * \param data Extra data what setup the callback wanted to pass.
- *
- * \note Called when the channel leaves the bridging system or
- * is destroyed.
- *
- * \return Nothing
- */
-typedef void (*ast_after_bridge_cb_failed)(enum ast_after_bridge_cb_reason reason, void *data);
-
-/*!
- * \brief After bridge callback function.
- * \since 12.0.0
- *
- * \param chan Channel just leaving bridging system.
- * \param data Extra data what setup the callback wanted to pass.
- *
- * \return Nothing
- */
-typedef void (*ast_after_bridge_cb)(struct ast_channel *chan, void *data);
-
-/*!
- * \brief Discard channel after bridge callback.
- * \since 12.0.0
- *
- * \param chan Channel to discard after bridge callback.
- * \param reason Why are we doing this.
- *
- * \note chan is locked by this function.
- *
- * \return Nothing
- */
-void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason);
-
-/*!
- * \brief Setup an after bridge callback for when the channel leaves the bridging system.
- * \since 12.0.0
- *
- * \param chan Channel to setup an after bridge callback on.
- * \param callback Function to call when the channel leaves the bridging system.
- * \param failed Function to call when it will not be calling the callback.
- * \param data Extra data to pass with the callback.
- *
- * \note chan is locked by this function.
- *
- * \note failed is called when the channel leaves the bridging
- * system or is destroyed.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data);
-
-/*!
- * \brief Get a string representation of an after bridge callback reason
- * \since 12.0.0
- *
- * \param reason The reason to interpret to a string
- * \retval NULL Unrecognized reason
- * \retval non-NULL String representation of reason
- */
-const char *ast_after_bridge_cb_reason_string(enum ast_after_bridge_cb_reason reason);
 
 /*!
  * \brief Get a container of all channels in the bridge
diff --git a/include/asterisk/bridging_after.h b/include/asterisk/bridging_after.h
new file mode 100644
index 0000000000000000000000000000000000000000..53f30b9ad25baa97c3d389a5bb5894a446e47831
--- /dev/null
+++ b/include/asterisk/bridging_after.h
@@ -0,0 +1,244 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013 Digium, Inc.
+ *
+ * Richard Mudgett <rmudgett@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief After Bridge Execution API
+ *
+ * \author Richard Mudgett <rmudgett@digium.com>
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
+ */
+
+#ifndef _ASTERISK_BRIDGING_AFTER_H
+#define _ASTERISK_BRIDGING_AFTER_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! Reason the the after bridge callback will not be called. */
+enum ast_bridge_after_cb_reason {
+	/*! The datastore is being destroyed.  Likely due to hangup. (Enum value must be zero.) */
+	AST_BRIDGE_AFTER_CB_REASON_DESTROY,
+	/*! Something else replaced the callback with another. */
+	AST_BRIDGE_AFTER_CB_REASON_REPLACED,
+	/*! The callback was removed because of a masquerade. (fixup) */
+	AST_BRIDGE_AFTER_CB_REASON_MASQUERADE,
+	/*! The channel was departed from the bridge. */
+	AST_BRIDGE_AFTER_CB_REASON_DEPART,
+	/*! Was explicitly removed by external code. */
+	AST_BRIDGE_AFTER_CB_REASON_REMOVED,
+};
+
+/*!
+ * \brief Set channel to goto specific location after the bridge.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ * \param context Context to goto after bridge.
+ * \param exten Exten to goto after bridge.
+ * \param priority Priority to goto after bridge.
+ *
+ * \note chan is locked by this function.
+ *
+ * \details Add a channel datastore to setup the goto location
+ * when the channel leaves the bridge and run a PBX from there.
+ *
+ * \return Nothing
+ */
+void ast_bridge_set_after_goto(struct ast_channel *chan, const char *context, const char *exten, int priority);
+
+/*!
+ * \brief Set channel to run the h exten after the bridge.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ * \param context Context to goto after bridge.
+ *
+ * \note chan is locked by this function.
+ *
+ * \details Add a channel datastore to setup the goto location
+ * when the channel leaves the bridge and run a PBX from there.
+ *
+ * \return Nothing
+ */
+void ast_bridge_set_after_h(struct ast_channel *chan, const char *context);
+
+/*!
+ * \brief Set channel to go on in the dialplan after the bridge.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ * \param context Current context of the caller channel.
+ * \param exten Current exten of the caller channel.
+ * \param priority Current priority of the caller channel
+ * \param parseable_goto User specified goto string from dialplan.
+ *
+ * \note chan is locked by this function.
+ *
+ * \details Add a channel datastore to setup the goto location
+ * when the channel leaves the bridge and run a PBX from there.
+ *
+ * If parseable_goto then use the given context/exten/priority
+ *   as the relative position for the parseable_goto.
+ * Else goto the given context/exten/priority+1.
+ *
+ * \return Nothing
+ */
+void ast_bridge_set_after_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto);
+
+/*!
+ * \brief Setup any after bridge goto location to begin execution.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ *
+ * \note chan is locked by this function.
+ *
+ * \details Pull off any after bridge goto location datastore and
+ * setup for dialplan execution there.
+ *
+ * \retval 0 on success.  The goto location is set for a PBX to run it.
+ * \retval non-zero on error or no goto location.
+ *
+ * \note If the after bridge goto is set to run an h exten it is
+ * run here immediately.
+ */
+int ast_bridge_setup_after_goto(struct ast_channel *chan);
+
+/*!
+ * \brief Run any after bridge callback.
+ * \since 12.0.0
+ *
+ * \param chan Channel to run after bridge callback.
+ *
+ * \return Nothing
+ */
+void ast_bridge_run_after_callback(struct ast_channel *chan);
+
+/*!
+ * \brief Run discarding any after bridge callbacks.
+ * \since 12.0.0
+ *
+ * \param chan Channel to run after bridge callback.
+ *
+ * \return Nothing
+ */
+void ast_bridge_discard_after_callback(struct ast_channel *chan, enum ast_bridge_after_cb_reason reason);
+
+/*!
+ * \brief Run a PBX on any after bridge goto location.
+ * \since 12.0.0
+ *
+ * \param chan Channel to execute after bridge goto location.
+ *
+ * \note chan is locked by this function.
+ *
+ * \details Pull off any after bridge goto location datastore
+ * and run a PBX at that location.
+ *
+ * \note On return, the chan pointer is no longer valid because
+ * the channel has hung up.
+ *
+ * \return Nothing
+ */
+void ast_bridge_run_after_goto(struct ast_channel *chan);
+
+/*!
+ * \brief Discard channel after bridge goto location.
+ * \since 12.0.0
+ *
+ * \param chan Channel to discard after bridge goto location.
+ *
+ * \note chan is locked by this function.
+ *
+ * \return Nothing
+ */
+void ast_bridge_discard_after_goto(struct ast_channel *chan);
+
+/*!
+ * \brief Read after bridge goto if it exists
+ * \since 12.0.0
+ *
+ * \param chan Channel to read the after bridge goto parseable goto string from
+ * \param buffer Buffer to write the after bridge goto data to
+ * \param buf_size size of the buffer being written to
+ */
+void ast_bridge_read_after_goto(struct ast_channel *chan, char *buffer, size_t buf_size);
+
+/*!
+ * \brief After bridge callback failed.
+ * \since 12.0.0
+ *
+ * \param reason Reason callback is failing.
+ * \param data Extra data what setup the callback wanted to pass.
+ *
+ * \note Called when the channel leaves the bridging system or
+ * is destroyed.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_bridge_after_cb_failed)(enum ast_bridge_after_cb_reason reason, void *data);
+
+/*!
+ * \brief After bridge callback function.
+ * \since 12.0.0
+ *
+ * \param chan Channel just leaving bridging system.
+ * \param data Extra data what setup the callback wanted to pass.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_bridge_after_cb)(struct ast_channel *chan, void *data);
+
+/*!
+ * \brief Setup an after bridge callback for when the channel leaves the bridging system.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup an after bridge callback on.
+ * \param callback Function to call when the channel leaves the bridging system.
+ * \param failed Function to call when it will not be calling the callback.
+ * \param data Extra data to pass with the callback.
+ *
+ * \note chan is locked by this function.
+ *
+ * \note failed is called when the channel leaves the bridging
+ * system or is destroyed.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_set_after_callback(struct ast_channel *chan, ast_bridge_after_cb callback, ast_bridge_after_cb_failed failed, void *data);
+
+/*!
+ * \brief Get a string representation of an after bridge callback reason
+ * \since 12.0.0
+ *
+ * \param reason The reason to interpret to a string
+ * \retval NULL Unrecognized reason
+ * \retval non-NULL String representation of reason
+ */
+const char *ast_bridge_after_cb_reason_string(enum ast_bridge_after_cb_reason reason);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif	/* _ASTERISK_BRIDGING_H */
diff --git a/include/asterisk/bridging_channel.h b/include/asterisk/bridging_channel.h
index cdbb2ec5d75583b9561c91bb35ce94e4334dee3c..f58fba7dcaa6cf63ca860cc98062fae611bccb4b 100644
--- a/include/asterisk/bridging_channel.h
+++ b/include/asterisk/bridging_channel.h
@@ -20,29 +20,29 @@
 
 /*!
  * \file
- * \brief Bridging Channel API
+ * \page AstBridgeChannel Bridging Channel API
  *
  * An API that act on a channel in a bridge. Note that while the
  * \ref ast_bridge_channel is owned by a channel, it should only be used
  * by members of the bridging system. The only places where this API should
  * be used is:
- *  - The \ref AstBridging API itself
- *  - Bridge mixing technologies
- *  - Bridge sub-classes
+ *  \arg \ref AstBridging API itself
+ *  \arg Bridge mixing technologies
+ *  \arg Bridge sub-classes
  *
  * In general, anywhere else it is unsafe to use this API. Care should be
  * taken when using this API to ensure that the locking order remains
  * correct. The locking order must be:
- *  - The \ref ast_bridge
- *  - The \ref ast_bridge_channel
- *  - The \ref ast_channel
+ *  \arg The \ref \c ast_bridge
+ *  \arg The \ref \c ast_bridge_channel
+ *  \arg The \ref \c ast_channel
  *
  * \author Joshua Colp <jcolp@digium.com>
  * \author Richard Mudgett <rmudgett@digium.com>
  * \author Matt Jordan <mjordan@digium.com>
  *
  * See Also:
- * \ref bridging.h
+ * \arg \ref AstBridging
  * \arg \ref AstCREDITS
  */
 
@@ -56,22 +56,22 @@ extern "C" {
 #include "asterisk/bridging_technology.h"
 
 /*! \brief State information about a bridged channel */
-enum ast_bridge_channel_state {
+enum bridge_channel_state {
 	/*! Waiting for a signal (Channel in the bridge) */
-	AST_BRIDGE_CHANNEL_STATE_WAIT = 0,
+	BRIDGE_CHANNEL_STATE_WAIT = 0,
 	/*! Bridged channel was forced out and should be hung up (Bridge may dissolve.) */
-	AST_BRIDGE_CHANNEL_STATE_END,
+	BRIDGE_CHANNEL_STATE_END,
 	/*! Bridged channel was forced out. Don't dissolve the bridge regardless */
-	AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE,
+	BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE,
 };
 
-enum ast_bridge_channel_thread_state {
+enum bridge_channel_thread_state {
 	/*! Bridge channel thread is idle/waiting. */
-	AST_BRIDGE_CHANNEL_THREAD_IDLE,
+	BRIDGE_CHANNEL_THREAD_IDLE,
 	/*! Bridge channel thread is writing a normal/simple frame. */
-	AST_BRIDGE_CHANNEL_THREAD_SIMPLE,
+	BRIDGE_CHANNEL_THREAD_SIMPLE,
 	/*! Bridge channel thread is processing a frame. */
-	AST_BRIDGE_CHANNEL_THREAD_FRAME,
+	BRIDGE_CHANNEL_THREAD_FRAME,
 };
 
 struct ast_bridge;
@@ -85,7 +85,7 @@ struct ast_bridge_channel {
 	/*! Condition, used if we want to wake up a thread waiting on the bridged channel */
 	ast_cond_t cond;
 	/*! Current bridged channel state */
-	enum ast_bridge_channel_state state;
+	enum bridge_channel_state state;
 	/*! Asterisk channel participating in the bridge */
 	struct ast_channel *chan;
 	/*! Asterisk channel we are swapping with (if swapping) */
@@ -154,7 +154,7 @@ struct ast_bridge_channel {
 	 *
 	 * \note Needs to be atomically settable.
 	 */
-	enum ast_bridge_channel_thread_state activity;
+	enum bridge_channel_thread_state activity;
 };
 
 /*!
@@ -216,6 +216,24 @@ static inline void _ast_bridge_channel_unlock(struct ast_bridge_channel *bridge_
  */
 void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel);
 
+/*!
+ * \brief Lets the bridging indicate when a bridge channel has stopped or started talking.
+ *
+ * \note All DSP functionality on the bridge has been pushed down to the lowest possible
+ * layer, which in this case is the specific bridging technology being used. Since it
+ * is necessary for the knowledge of which channels are talking to make its way up to the
+ * application, this function has been created to allow the bridging technology to communicate
+ * that information with the bridging core.
+ *
+ * \param bridge_channel The bridge channel that has either started or stopped talking.
+ * \param started_talking set to 1 when this indicates the channel has started talking set to 0
+ * when this indicates the channel has stopped talking.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking);
+
 /*!
  * \brief Set bridge channel state to leave bridge (if not leaving already).
  *
@@ -225,14 +243,14 @@ void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel);
  * Example usage:
  *
  * \code
- * ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+ * ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
  * \endcode
  *
  * This places the channel pointed to by bridge_channel into the
- * state AST_BRIDGE_CHANNEL_STATE_END if it was
- * AST_BRIDGE_CHANNEL_STATE_WAIT before.
+ * state BRIDGE_CHANNEL_STATE_END if it was
+ * BRIDGE_CHANNEL_STATE_WAIT before.
  */
-void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state);
+void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state);
 
 /*!
  * \brief Set bridge channel state to leave bridge (if not leaving already).
@@ -243,14 +261,82 @@ void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel,
  * Example usage:
  *
  * \code
- * ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+ * ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
  * \endcode
  *
  * This places the channel pointed to by bridge_channel into the
- * state AST_BRIDGE_CHANNEL_STATE_END if it was
- * AST_BRIDGE_CHANNEL_STATE_WAIT before.
+ * state BRIDGE_CHANNEL_STATE_END if it was
+ * BRIDGE_CHANNEL_STATE_WAIT before.
+ */
+void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state);
+
+/*!
+ * \brief Get the peer bridge channel of a two party bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel What to get the peer of.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \note This is an internal bridge function.
+ *
+ * \retval peer on success.
+ * \retval NULL no peer channel.
+ */
+struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel);
+
+/*!
+ * \brief Restore the formats of a bridge channel's channel to how they were before bridge_channel_internal_join
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to restore
+ */
+void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel);
+
+/*!
+ * \brief Adjust the bridge_channel's bridge merge inhibit request count.
+ * \since 12.0.0
+ *
+ * \param bridge_channel What to operate on.
+ * \param request Inhibit request increment.
+ *     (Positive to add requests.  Negative to remove requests.)
+ *
+ * \note This API call is meant for internal bridging operations.
+ *
+ * \retval bridge adjusted merge inhibit with reference count.
+ */
+struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request);
+
+/*!
+ * \internal
+ * \brief Update the linkedids for all channels in a bridge
+ * \since 12.0.0
+ *
+ * \param bridge_channel The channel joining the bridge
+ * \param swap The channel being swapped out of the bridge. May be NULL.
+ *
+ * \note The bridge must be locked prior to calling this function. This should be called
+ * during a \ref bridge_channel_internal_push operation, typically by a sub-class of a bridge
+ */
+void ast_bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap);
+
+/*!
+ * \internal
+ * \brief Update the accountcodes for a channel entering a bridge
+ * \since 12.0.0
+ *
+ * This function updates the accountcode and peeraccount on channels in two-party
+ * bridges. In multi-party bridges, peeraccount is not set - it doesn't make much sense -
+ * however accountcode propagation will still occur if the channel joining has an
+ * accountcode.
+ *
+ * \param bridge_channel The channel joining the bridge
+ * \param swap The channel being swapped out of the bridge. May be NULL.
+ *
+ * \note The bridge must be locked prior to calling this function. This should be called
+ * during a \ref bridge_channel_internal_push operation, typically by a sub-class of a bridge
  */
-void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state);
+void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap);
 
 /*!
  * \brief Write a frame to the specified bridge_channel.
@@ -497,79 +583,6 @@ int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel,
 int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid,
 	const char *parker_uuid, const char *app_data);
 
-/*!
- * \brief Restore the formats of a bridge channel's channel to how they were before bridge_channel_join
- * \since 12.0.0
- *
- * \param bridge_channel Channel to restore
- */
-void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel);
-
-/*!
- * \brief Get the peer bridge channel of a two party bridge.
- * \since 12.0.0
- *
- * \param bridge_channel What to get the peer of.
- *
- * \note On entry, bridge_channel->bridge is already locked.
- *
- * \note This is an internal bridge function.
- *
- * \retval peer on success.
- * \retval NULL no peer channel.
- */
-struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel);
-
-struct blind_transfer_data {
-	char exten[AST_MAX_EXTENSION];
-	char context[AST_MAX_CONTEXT];
-};
-
-/*!
- * \brief Adjust the bridge_channel's bridge merge inhibit request count.
- * \since 12.0.0
- *
- * \param bridge_channel What to operate on.
- * \param request Inhibit request increment.
- *     (Positive to add requests.  Negative to remove requests.)
- *
- * \note This API call is meant for internal bridging operations.
- *
- * \retval bridge adjusted merge inhibit with reference count.
- */
-struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request);
-
-/*!
- * \internal
- * \brief Update the linkedids for all channels in a bridge
- * \since 12.0.0
- *
- * \param bridge_channel The channel joining the bridge
- * \param swap The channel being swapped out of the bridge. May be NULL.
- *
- * \note The bridge must be locked prior to calling this function. This should be called
- * during a \ref bridge_channel_push operation, typically by a sub-class of a bridge
- */
-void ast_bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap);
-
-/*!
- * \internal
- * \brief Update the accountcodes for a channel entering a bridge
- * \since 12.0.0
- *
- * This function updates the accountcode and peeraccount on channels in two-party
- * bridges. In multi-party bridges, peeraccount is not set - it doesn't make much sense -
- * however accountcode propagation will still occur if the channel joining has an
- * accountcode.
- *
- * \param bridge_channel The channel joining the bridge
- * \param swap The channel being swapped out of the bridge. May be NULL.
- *
- * \note The bridge must be locked prior to calling this function. This should be called
- * during a \ref bridge_channel_push operation, typically by a sub-class of a bridge
- */
-void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap);
-
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
diff --git a/include/asterisk/bridging_channel_internal.h b/include/asterisk/bridging_channel_internal.h
index cbfa20aa6b3448d1e1e3b2a18abbf8ffab78d281..6c3add13b058b5a7f32c4879ae6c9a4e0628a04a 100644
--- a/include/asterisk/bridging_channel_internal.h
+++ b/include/asterisk/bridging_channel_internal.h
@@ -16,6 +16,9 @@
  * at the top of the source tree.
  */
 
+#ifndef _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H
+#define _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H
+
 /*!
  * \file
  * \brief Private Bridging Channel API
@@ -23,16 +26,14 @@
  * \author Matt Jordan <mjordan@digium.com>
  *
  * A private API to manipulate channels in a bridge. These can be called on a channel in
- * a bridge by the bridging API, but should not be called by external consumers of the
- * Bridging API.
+ * a bridge by \ref bridging.c. These functions should not be called elsewhere, including
+ * by other members of the Bridging API.
  *
  * See Also:
  * \arg \ref AstCREDITS
+ * \arg \ref Ast
  */
 
-#ifndef _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H
-#define _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H
-
 /*!
  * \internal
  * \brief Actions that can be taken on a channel in a bridge
@@ -71,6 +72,18 @@ enum bridge_channel_action_type {
 	BRIDGE_CHANNEL_ACTION_DEFERRED_DISSOLVING,
 };
 
+/*!
+ * \internal
+ * \brief Allocate a new ao2 ref counted bridge_channel
+ * \since 12.0.0
+ *
+ * \param bridge The bridge to make the bridge_channel for
+ *
+ * \retval NULL on error
+ * \retval ao2 ref counted object on success
+ */
+struct ast_bridge_channel *bridge_channel_internal_alloc(struct ast_bridge *bridge);
+
 /*!
  * \internal
  * \brief Push the bridge channel into its specified bridge.
@@ -83,7 +96,7 @@ enum bridge_channel_action_type {
  * \retval 0 on success.
  * \retval -1 on failure.  The channel did not get pushed.
  */
-int bridge_channel_push(struct ast_bridge_channel *bridge_channel);
+int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel);
 
 /*!
  * \internal
@@ -96,7 +109,7 @@ int bridge_channel_push(struct ast_bridge_channel *bridge_channel);
  *
  * \return Nothing
  */
-void bridge_channel_pull(struct ast_bridge_channel *bridge_channel);
+void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel);
 
 /*!
  * \internal
@@ -108,7 +121,7 @@ void bridge_channel_pull(struct ast_bridge_channel *bridge_channel);
  * it is in the bridge. It will return when the channel has been instructed to
  * leave the bridge.
  */
-void bridge_channel_join(struct ast_bridge_channel *bridge_channel);
+void bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel);
 
 /*!
  * \internal
@@ -118,7 +131,7 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel);
  * \param bridge_channel The channel in the bridge
  * \note This function assumes that \ref bridge_channel is already locked
  */
-void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel);
+void bridge_channel_internal_suspend_nolock(struct ast_bridge_channel *bridge_channel);
 
 /*!
  * \internal
@@ -127,7 +140,7 @@ void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel);
  * \param bridge_channel The channel in the bridge
  * \note This function assumes that \ref bridge_channel is already locked
  */
-void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel);
+void bridge_channel_internal_unsuspend_nolock(struct ast_bridge_channel *bridge_channel);
 
 /*!
  * \internal
@@ -146,11 +159,35 @@ void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel);
  * \retval 0 on success.
  * \retval -1 on error.
  */
-int bridge_channel_queue_blind_transfer(struct ast_channel *transferee,
+int bridge_channel_internal_queue_blind_transfer(struct ast_channel *transferee,
 		const char *exten, const char *context,
 		transfer_channel_cb new_channel_cb, void *user_data);
 
-int bridge_channel_queue_attended_transfer(struct ast_channel *transferee,
+/*!
+ * \internal
+ * \brief Queue an attended transfer action on a transferee bridge channel
+ *
+ * This is only relevant for when an attended transfer is performed on a two-party
+ * bridge. The transferee's bridge channel will have an attended transfer bridge
+ * action queued onto it.
+ *
+ * \param transferee The channel to have the action queued on
+ * \param unbridged_chan The unbridged channel who is the target of the attended
+ * transfer
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int bridge_channel_internal_queue_attended_transfer(struct ast_channel *transferee,
 		struct ast_channel *unbridged_chan);
 
+/*!
+ * \internal
+ * \brief Return whether or not the bridge_channel would allow optimization
+ *
+ * \retval 0 if optimization is not allowed
+ * \retval non-zero if optimization is allowed
+ */
+int bridge_channel_internal_allows_optimization(struct ast_bridge_channel *bridge_channel);
+
 #endif /* _ASTERISK_PRIVATE_BRIDGING_H */
diff --git a/include/asterisk/bridging_features.h b/include/asterisk/bridging_features.h
index dacb6c698fce4caaf6b2a1b6d240f43c47c13cda..05fdf2587be0b8d268e52331af56b1062b8f67dd 100644
--- a/include/asterisk/bridging_features.h
+++ b/include/asterisk/bridging_features.h
@@ -16,7 +16,8 @@
  * at the top of the source tree.
  */
 
-/*! \file
+/*!
+ * \file
  * \brief Channel Bridging API
  * \author Joshua Colp <jcolp@digium.com>
  */
@@ -75,7 +76,7 @@ enum ast_bridge_builtin_feature {
 	 * how it was imparted.
 	 *
 	 * \note Joined channels exit the bridge with
-	 * AST_BRIDGE_CHANNEL_STATE_END_WITH_DISSOLVE.
+	 * BRIDGE_CHANNEL_STATE_END_WITH_DISSOLVE.
 	 */
 	AST_BRIDGE_BUILTIN_HANGUP,
 	/*!
diff --git a/include/asterisk/bridging_internal.h b/include/asterisk/bridging_internal.h
index cafa693f594b3615b819aae70f0b982c4f340059..18ef56e96462a06f51e018405246412e6bf85790 100644
--- a/include/asterisk/bridging_internal.h
+++ b/include/asterisk/bridging_internal.h
@@ -180,8 +180,8 @@ void bridge_merge_inhibit_nolock(struct ast_bridge *bridge, int request);
  * \param colp_update Whether to perform COLP updates.
  *
  * \details
- * After a series of bridge_channel_push and
- * bridge_channel_pull calls, you need to call this function
+ * After a series of bridge_channel_internal_push and
+ * bridge_channel_internal_pull calls, you need to call this function
  * to cause the bridge to complete restructuring for the change
  * in the channel makeup of the bridge.
  *
diff --git a/include/asterisk/bridging_technology.h b/include/asterisk/bridging_technology.h
index e037b7490ed0594cc3a78e1040345619d444e6b7..534eb541747943875a8ed2ac42bb404d8c6e70be 100644
--- a/include/asterisk/bridging_technology.h
+++ b/include/asterisk/bridging_technology.h
@@ -202,24 +202,6 @@ int __ast_bridge_technology_register(struct ast_bridge_technology *technology, s
  */
 int ast_bridge_technology_unregister(struct ast_bridge_technology *technology);
 
-/*!
- * \brief Lets the bridging indicate when a bridge channel has stopped or started talking.
- *
- * \note All DSP functionality on the bridge has been pushed down to the lowest possible
- * layer, which in this case is the specific bridging technology being used. Since it
- * is necessary for the knowledge of which channels are talking to make its way up to the
- * application, this function has been created to allow the bridging technology to communicate
- * that information with the bridging core.
- *
- * \param bridge_channel The bridge channel that has either started or stopped talking.
- * \param started_talking set to 1 when this indicates the channel has started talking set to 0
- * when this indicates the channel has stopped talking.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking);
-
 /*!
  * \brief Suspend a bridge technology from consideration
  *
diff --git a/main/bridging.c b/main/bridging.c
index dda0732365748bf5e31add109be2f280f699133c..1e1e6c763d22eb0c48fb95864738c81fb3201012 100644
--- a/main/bridging.c
+++ b/main/bridging.c
@@ -38,9 +38,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/lock.h"
 #include "asterisk/linkedlists.h"
 #include "asterisk/bridging.h"
+#include "asterisk/bridging_internal.h"
+#include "asterisk/bridging_channel_internal.h"
 #include "asterisk/bridging_basic.h"
 #include "asterisk/bridging_technology.h"
 #include "asterisk/bridging_channel.h"
+#include "asterisk/bridging_after.h"
 #include "asterisk/stasis_bridging.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/app.h"
@@ -61,8 +64,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/parking.h"
 #include "asterisk/core_local.h"
 #include "asterisk/core_unreal.h"
-#include "asterisk/bridging_internal.h"
-#include "asterisk/bridging_channel_internal.h"
 
 /*! All bridges container. */
 static struct ao2_container *bridges;
@@ -268,7 +269,7 @@ void bridge_dissolve(struct ast_bridge *bridge)
 
 /* BUGBUG need a cause code on the bridge for the later ejected channels. */
 	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-		ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 	}
 
 	/* Must defer dissolving bridge because it is already locked. */
@@ -1343,24 +1344,6 @@ static void set_bridge_peer_vars(struct ast_bridge *bridge)
 	}
 }
 
-/*!
- * \internal
- * \brief Notify the bridge that it has been reconfigured.
- * \since 12.0.0
- *
- * \param bridge Reconfigured bridge.
- * \param colp_update Whether to perform COLP updates.
- *
- * \details
- * After a series of bridge_channel_push and
- * bridge_channel_pull calls, you need to call this function
- * to cause the bridge to complete restructuring for the change
- * in the channel makeup of the bridge.
- *
- * \note On entry, the bridge is already locked.
- *
- * \return Nothing
- */
 void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update)
 {
 	if (!bridge->reconfigured) {
@@ -1387,726 +1370,17 @@ void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update)
 	}
 }
 
-/*!
- * \internal
- * \brief Close a pipe.
- * \since 12.0.0
- *
- * \param my_pipe What to close.
- *
- * \return Nothing
- */
-static void pipe_close(int *my_pipe)
-{
-	if (my_pipe[0] > -1) {
-		close(my_pipe[0]);
-		my_pipe[0] = -1;
-	}
-	if (my_pipe[1] > -1) {
-		close(my_pipe[1]);
-		my_pipe[1] = -1;
-	}
-}
-
-/*!
- * \internal
- * \brief Initialize a pipe as non-blocking.
- * \since 12.0.0
- *
- * \param my_pipe What to initialize.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-static int pipe_init_nonblock(int *my_pipe)
-{
-	int flags;
-
-	my_pipe[0] = -1;
-	my_pipe[1] = -1;
-	if (pipe(my_pipe)) {
-		ast_log(LOG_WARNING, "Can't create pipe! Try increasing max file descriptors with ulimit -n\n");
-		return -1;
-	}
-	flags = fcntl(my_pipe[0], F_GETFL);
-	if (fcntl(my_pipe[0], F_SETFL, flags | O_NONBLOCK) < 0) {
-		ast_log(LOG_WARNING, "Unable to set read pipe nonblocking! (%d: %s)\n",
-			errno, strerror(errno));
-		return -1;
-	}
-	flags = fcntl(my_pipe[1], F_GETFL);
-	if (fcntl(my_pipe[1], F_SETFL, flags | O_NONBLOCK) < 0) {
-		ast_log(LOG_WARNING, "Unable to set write pipe nonblocking! (%d: %s)\n",
-			errno, strerror(errno));
-		return -1;
-	}
-	return 0;
-}
-
-/* Destroy elements of the bridge channel structure and the bridge channel structure itself */
-static void bridge_channel_destroy(void *obj)
-{
-	struct ast_bridge_channel *bridge_channel = obj;
-	struct ast_frame *fr;
-
-	if (bridge_channel->callid) {
-		bridge_channel->callid = ast_callid_unref(bridge_channel->callid);
-	}
-
-	if (bridge_channel->bridge) {
-		ao2_ref(bridge_channel->bridge, -1);
-		bridge_channel->bridge = NULL;
-	}
-
-	/* Flush any unhandled wr_queue frames. */
-	while ((fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list))) {
-		ast_frfree(fr);
-	}
-	pipe_close(bridge_channel->alert_pipe);
-
-	ast_cond_destroy(&bridge_channel->cond);
-}
-
-static struct ast_bridge_channel *bridge_channel_alloc(struct ast_bridge *bridge)
+struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan)
 {
 	struct ast_bridge_channel *bridge_channel;
 
-	bridge_channel = ao2_alloc(sizeof(struct ast_bridge_channel), bridge_channel_destroy);
-	if (!bridge_channel) {
-		return NULL;
-	}
-	ast_cond_init(&bridge_channel->cond, NULL);
-	if (pipe_init_nonblock(bridge_channel->alert_pipe)) {
-		ao2_ref(bridge_channel, -1);
-		return NULL;
-	}
-	if (bridge) {
-		bridge_channel->bridge = bridge;
-		ao2_ref(bridge_channel->bridge, +1);
-	}
-
-	return bridge_channel;
-}
-
-struct after_bridge_cb_node {
-	/*! Next list node. */
-	AST_LIST_ENTRY(after_bridge_cb_node) list;
-	/*! Desired callback function. */
-	ast_after_bridge_cb callback;
-	/*! After bridge callback will not be called and destroy any resources data may contain. */
-	ast_after_bridge_cb_failed failed;
-	/*! Extra data to pass to the callback. */
-	void *data;
-	/*! Reason the after bridge callback failed. */
-	enum ast_after_bridge_cb_reason reason;
-};
-
-struct after_bridge_cb_ds {
-	/*! After bridge callbacks container. */
-	AST_LIST_HEAD(, after_bridge_cb_node) callbacks;
-};
-
-/*!
- * \internal
- * \brief Indicate after bridge callback failed.
- * \since 12.0.0
- *
- * \param node After bridge callback node.
- *
- * \return Nothing
- */
-static void after_bridge_cb_failed(struct after_bridge_cb_node *node)
-{
-	if (node->failed) {
-		node->failed(node->reason, node->data);
-		node->failed = NULL;
-	}
-}
-
-/*!
- * \internal
- * \brief Run discarding any after bridge callbacks.
- * \since 12.0.0
- *
- * \param after_bridge After bridge callback container process.
- * \param reason Why are we doing this.
- *
- * \return Nothing
- */
-static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_after_bridge_cb_reason reason)
-{
-	struct after_bridge_cb_node *node;
-
-	for (;;) {
-		AST_LIST_LOCK(&after_bridge->callbacks);
-		node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
-		AST_LIST_UNLOCK(&after_bridge->callbacks);
-		if (!node) {
-			break;
-		}
-		if (!node->reason) {
-			node->reason = reason;
-		}
-		after_bridge_cb_failed(node);
-		ast_free(node);
-	}
-}
-
-/*!
- * \internal
- * \brief Destroy the after bridge callback datastore.
- * \since 12.0.0
- *
- * \param data After bridge callback data to destroy.
- *
- * \return Nothing
- */
-static void after_bridge_cb_destroy(void *data)
-{
-	struct after_bridge_cb_ds *after_bridge = data;
-
-	after_bridge_cb_run_discard(after_bridge, AST_AFTER_BRIDGE_CB_REASON_DESTROY);
-
-	AST_LIST_HEAD_DESTROY(&after_bridge->callbacks);
-	ast_free(after_bridge);
-}
-
-/*!
- * \internal
- * \brief Fixup the after bridge callback datastore.
- * \since 12.0.0
- *
- * \param data After bridge callback data to fixup.
- * \param old_chan The datastore is moving from this channel.
- * \param new_chan The datastore is moving to this channel.
- *
- * \return Nothing
- */
-static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
-{
-	ast_after_bridge_callback_discard(new_chan, AST_AFTER_BRIDGE_CB_REASON_MASQUERADE);
-}
-
-static const struct ast_datastore_info after_bridge_cb_info = {
-	.type = "after-bridge-cb",
-	.destroy = after_bridge_cb_destroy,
-	.chan_fixup = after_bridge_cb_fixup,
-};
-
-/*!
- * \internal
- * \brief Setup/create an after bridge callback datastore container.
- * \since 12.0.0
- *
- * \param chan Channel to setup/create the after bridge callback container on.
- *
- * \retval after_bridge datastore container on success.
- * \retval NULL on error.
- */
-static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan)
-{
-	struct ast_datastore *datastore;
-	struct after_bridge_cb_ds *after_bridge;
-	SCOPED_CHANNELLOCK(lock, chan);
-
-	datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
-	if (datastore) {
-		return datastore->data;
-	}
-
-	/* Create a new datastore. */
-	datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
-	if (!datastore) {
-		return NULL;
-	}
-	after_bridge = ast_calloc(1, sizeof(*after_bridge));
-	if (!after_bridge) {
-		ast_datastore_free(datastore);
-		return NULL;
-	}
-	AST_LIST_HEAD_INIT(&after_bridge->callbacks);
-	datastore->data = after_bridge;
-	ast_channel_datastore_add(chan, datastore);
-
-	return datastore->data;
-}
-
-/*!
- * \internal
- * \brief Find an after bridge callback datastore container.
- * \since 12.0.0
- *
- * \param chan Channel to find the after bridge callback container on.
- *
- * \retval after_bridge datastore container on success.
- * \retval NULL on error.
- */
-static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan)
-{
-	struct ast_datastore *datastore;
-	SCOPED_CHANNELLOCK(lock, chan);
-
-	datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
-	if (!datastore) {
-		return NULL;
-	}
-	return datastore->data;
-}
-
-/*!
- * \internal
- * \brief Run any after bridge callback.
- * \since 12.0.0
- *
- * \param chan Channel to run after bridge callback.
- *
- * \return Nothing
- */
-static void after_bridge_callback_run(struct ast_channel *chan)
-{
-	struct after_bridge_cb_ds *after_bridge;
-	struct after_bridge_cb_node *node;
-
-	after_bridge = after_bridge_cb_find(chan);
-	if (!after_bridge) {
-		return;
-	}
-
-	for (;;) {
-		AST_LIST_LOCK(&after_bridge->callbacks);
-		node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
-		AST_LIST_UNLOCK(&after_bridge->callbacks);
-		if (!node) {
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		if (bridge_channel->chan == chan) {
 			break;
 		}
-		if (node->reason) {
-			after_bridge_cb_failed(node);
-		} else {
-			node->failed = NULL;
-			node->callback(chan, node->data);
-		}
-		ast_free(node);
 	}
-}
 
-/*!
- * \internal
- * \brief Run discarding any after bridge callbacks.
- * \since 12.0.0
- *
- * \param chan Channel to run after bridge callback.
- *
- * \return Nothing
- */
-static void after_bridge_callback_run_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
-{
-	struct after_bridge_cb_ds *after_bridge;
-
-	after_bridge = after_bridge_cb_find(chan);
-	if (!after_bridge) {
-		return;
-	}
-
-	after_bridge_cb_run_discard(after_bridge, reason);
-}
-
-void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
-{
-	struct after_bridge_cb_ds *after_bridge;
-	struct after_bridge_cb_node *node;
-
-	after_bridge = after_bridge_cb_find(chan);
-	if (!after_bridge) {
-		return;
-	}
-
-	AST_LIST_LOCK(&after_bridge->callbacks);
-	node = AST_LIST_LAST(&after_bridge->callbacks);
-	if (node && !node->reason) {
-		node->reason = reason;
-	}
-	AST_LIST_UNLOCK(&after_bridge->callbacks);
-}
-
-int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data)
-{
-	struct after_bridge_cb_ds *after_bridge;
-	struct after_bridge_cb_node *new_node;
-	struct after_bridge_cb_node *last_node;
-
-	/* Sanity checks. */
-	ast_assert(chan != NULL);
-	if (!chan || !callback) {
-		return -1;
-	}
-
-	after_bridge = after_bridge_cb_setup(chan);
-	if (!after_bridge) {
-		return -1;
-	}
-
-	/* Create a new callback node. */
-	new_node = ast_calloc(1, sizeof(*new_node));
-	if (!new_node) {
-		return -1;
-	}
-	new_node->callback = callback;
-	new_node->failed = failed;
-	new_node->data = data;
-
-	/* Put it in the container disabling any previously active one. */
-	AST_LIST_LOCK(&after_bridge->callbacks);
-	last_node = AST_LIST_LAST(&after_bridge->callbacks);
-	if (last_node && !last_node->reason) {
-		last_node->reason = AST_AFTER_BRIDGE_CB_REASON_REPLACED;
-	}
-	AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list);
-	AST_LIST_UNLOCK(&after_bridge->callbacks);
-	return 0;
-}
-
-const char *reason_strings[] = {
-	[AST_AFTER_BRIDGE_CB_REASON_DESTROY] = "Channel destroyed (hungup)",
-	[AST_AFTER_BRIDGE_CB_REASON_REPLACED] = "Callback was replaced",
-	[AST_AFTER_BRIDGE_CB_REASON_MASQUERADE] = "Channel masqueraded",
-	[AST_AFTER_BRIDGE_CB_REASON_DEPART] = "Channel was departed from bridge",
-	[AST_AFTER_BRIDGE_CB_REASON_REMOVED] = "Callback was removed",
-};
-
-const char *ast_after_bridge_cb_reason_string(enum ast_after_bridge_cb_reason reason)
-{
-	if (reason < AST_AFTER_BRIDGE_CB_REASON_DESTROY
-		|| AST_AFTER_BRIDGE_CB_REASON_REMOVED < reason
-		|| !reason_strings[reason]) {
-		return "Unknown";
-	}
-
-	return reason_strings[reason];
-}
-
-struct after_bridge_goto_ds {
-	/*! Goto string that can be parsed by ast_parseable_goto(). */
-	const char *parseable_goto;
-	/*! Specific goto context or default context for parseable_goto. */
-	const char *context;
-	/*! Specific goto exten or default exten for parseable_goto. */
-	const char *exten;
-	/*! Specific goto priority or default priority for parseable_goto. */
-	int priority;
-	/*! TRUE if the peer should run the h exten. */
-	unsigned int run_h_exten:1;
-	/*! Specific goto location */
-	unsigned int specific:1;
-};
-
-/*!
- * \internal
- * \brief Destroy the after bridge goto datastore.
- * \since 12.0.0
- *
- * \param data After bridge goto data to destroy.
- *
- * \return Nothing
- */
-static void after_bridge_goto_destroy(void *data)
-{
-	struct after_bridge_goto_ds *after_bridge = data;
-
-	ast_free((char *) after_bridge->parseable_goto);
-	ast_free((char *) after_bridge->context);
-	ast_free((char *) after_bridge->exten);
-}
-
-/*!
- * \internal
- * \brief Fixup the after bridge goto datastore.
- * \since 12.0.0
- *
- * \param data After bridge goto data to fixup.
- * \param old_chan The datastore is moving from this channel.
- * \param new_chan The datastore is moving to this channel.
- *
- * \return Nothing
- */
-static void after_bridge_goto_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
-{
-	/* There can be only one.  Discard any already on the new channel. */
-	ast_after_bridge_goto_discard(new_chan);
-}
-
-static const struct ast_datastore_info after_bridge_goto_info = {
-	.type = "after-bridge-goto",
-	.destroy = after_bridge_goto_destroy,
-	.chan_fixup = after_bridge_goto_fixup,
-};
-
-/*!
- * \internal
- * \brief Remove channel goto location after the bridge and return it.
- * \since 12.0.0
- *
- * \param chan Channel to remove after bridge goto location.
- *
- * \retval datastore on success.
- * \retval NULL on error or not found.
- */
-static struct ast_datastore *after_bridge_goto_remove(struct ast_channel *chan)
-{
-	struct ast_datastore *datastore;
-
-	ast_channel_lock(chan);
-	datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL);
-	if (datastore && ast_channel_datastore_remove(chan, datastore)) {
-		datastore = NULL;
-	}
-	ast_channel_unlock(chan);
-
-	return datastore;
-}
-
-void ast_after_bridge_goto_discard(struct ast_channel *chan)
-{
-	struct ast_datastore *datastore;
-
-	datastore = after_bridge_goto_remove(chan);
-	if (datastore) {
-		ast_datastore_free(datastore);
-	}
-}
-
-void ast_after_bridge_goto_read(struct ast_channel *chan, char *buffer, size_t buf_size)
-{
-	struct ast_datastore *datastore;
-	struct after_bridge_goto_ds *after_bridge;
-	char *current_pos = buffer;
-	size_t remaining_size = buf_size;
-
-	SCOPED_CHANNELLOCK(lock, chan);
-
-	datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL);
-	if (!datastore) {
-		buffer[0] = '\0';
-		return;
-	}
-
-	after_bridge = datastore->data;
-
-	if (after_bridge->parseable_goto) {
-		snprintf(buffer, buf_size, "%s", after_bridge->parseable_goto);
-		return;
-	}
-
-	if (!ast_strlen_zero(after_bridge->context)) {
-		snprintf(current_pos, remaining_size, "%s,", after_bridge->context);
-		remaining_size = remaining_size - strlen(current_pos);
-		current_pos += strlen(current_pos);
-	}
-
-	if (after_bridge->run_h_exten) {
-		snprintf(current_pos, remaining_size, "h,");
-		remaining_size = remaining_size - strlen(current_pos);
-		current_pos += strlen(current_pos);
-	} else if (!ast_strlen_zero(after_bridge->exten)) {
-		snprintf(current_pos, remaining_size, "%s,", after_bridge->exten);
-		remaining_size = remaining_size - strlen(current_pos);
-		current_pos += strlen(current_pos);
-	}
-
-	snprintf(current_pos, remaining_size, "%d", after_bridge->priority);
-}
-
-int ast_after_bridge_goto_setup(struct ast_channel *chan)
-{
-	struct ast_datastore *datastore;
-	struct after_bridge_goto_ds *after_bridge;
-	int goto_failed = -1;
-
-	/* Determine if we are going to setup a dialplan location and where. */
-	if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
-		/* An async goto has already setup a location. */
-		ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_ASYNCGOTO);
-		if (!ast_check_hangup(chan)) {
-			goto_failed = 0;
-		}
-		return goto_failed;
-	}
-
-	/* Get after bridge goto datastore. */
-	datastore = after_bridge_goto_remove(chan);
-	if (!datastore) {
-		return goto_failed;
-	}
-
-	after_bridge = datastore->data;
-	if (after_bridge->run_h_exten) {
-		if (ast_exists_extension(chan, after_bridge->context, "h", 1,
-			S_COR(ast_channel_caller(chan)->id.number.valid,
-				ast_channel_caller(chan)->id.number.str, NULL))) {
-			ast_debug(1, "Running after bridge goto h exten %s,h,1\n",
-				ast_channel_context(chan));
-			ast_pbx_h_exten_run(chan, after_bridge->context);
-		}
-	} else if (!ast_check_hangup(chan)) {
-		if (after_bridge->specific) {
-			goto_failed = ast_explicit_goto(chan, after_bridge->context,
-				after_bridge->exten, after_bridge->priority);
-		} else if (!ast_strlen_zero(after_bridge->parseable_goto)) {
-			char *context;
-			char *exten;
-			int priority;
-
-			/* Option F(x) for Bridge(), Dial(), and Queue() */
-
-			/* Save current dialplan location in case of failure. */
-			context = ast_strdupa(ast_channel_context(chan));
-			exten = ast_strdupa(ast_channel_exten(chan));
-			priority = ast_channel_priority(chan);
-
-			/* Set current dialplan position to default dialplan position */
-			ast_explicit_goto(chan, after_bridge->context, after_bridge->exten,
-				after_bridge->priority);
-
-			/* Then perform the goto */
-			goto_failed = ast_parseable_goto(chan, after_bridge->parseable_goto);
-			if (goto_failed) {
-				/* Restore original dialplan location. */
-				ast_channel_context_set(chan, context);
-				ast_channel_exten_set(chan, exten);
-				ast_channel_priority_set(chan, priority);
-			}
-		} else {
-			/* Option F() for Bridge(), Dial(), and Queue() */
-			goto_failed = ast_goto_if_exists(chan, after_bridge->context,
-				after_bridge->exten, after_bridge->priority + 1);
-		}
-		if (!goto_failed) {
-			if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
-				ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
-			}
-
-			ast_debug(1, "Setup after bridge goto location to %s,%s,%d.\n",
-				ast_channel_context(chan),
-				ast_channel_exten(chan),
-				ast_channel_priority(chan));
-		}
-	}
-
-	/* Discard after bridge goto datastore. */
-	ast_datastore_free(datastore);
-
-	return goto_failed;
-}
-
-void ast_after_bridge_goto_run(struct ast_channel *chan)
-{
-	int goto_failed;
-
-	goto_failed = ast_after_bridge_goto_setup(chan);
-	if (goto_failed || ast_pbx_run(chan)) {
-		ast_hangup(chan);
-	}
-}
-
-/*!
- * \internal
- * \brief Set after bridge goto location of channel.
- * \since 12.0.0
- *
- * \param chan Channel to setup after bridge goto location.
- * \param run_h_exten TRUE if the h exten should be run.
- * \param specific TRUE if the context/exten/priority is exactly specified.
- * \param context Context to goto after bridge.
- * \param exten Exten to goto after bridge. (Could be NULL if run_h_exten)
- * \param priority Priority to goto after bridge.
- * \param parseable_goto User specified goto string. (Could be NULL)
- *
- * \details Add a channel datastore to setup the goto location
- * when the channel leaves the bridge and run a PBX from there.
- *
- * If run_h_exten then execute the h exten found in the given context.
- * Else if specific then goto the given context/exten/priority.
- * Else if parseable_goto then use the given context/exten/priority
- *   as the relative position for the parseable_goto.
- * Else goto the given context/exten/priority+1.
- *
- * \return Nothing
- */
-static void __after_bridge_set_goto(struct ast_channel *chan, int run_h_exten, int specific, const char *context, const char *exten, int priority, const char *parseable_goto)
-{
-	struct ast_datastore *datastore;
-	struct after_bridge_goto_ds *after_bridge;
-
-	/* Sanity checks. */
-	ast_assert(chan != NULL);
-	if (!chan) {
-		return;
-	}
-	if (run_h_exten) {
-		ast_assert(run_h_exten && context);
-		if (!context) {
-			return;
-		}
-	} else {
-		ast_assert(context && exten && 0 < priority);
-		if (!context || !exten || priority < 1) {
-			return;
-		}
-	}
-
-	/* Create a new datastore. */
-	datastore = ast_datastore_alloc(&after_bridge_goto_info, NULL);
-	if (!datastore) {
-		return;
-	}
-	after_bridge = ast_calloc(1, sizeof(*after_bridge));
-	if (!after_bridge) {
-		ast_datastore_free(datastore);
-		return;
-	}
-
-	/* Initialize it. */
-	after_bridge->parseable_goto = ast_strdup(parseable_goto);
-	after_bridge->context = ast_strdup(context);
-	after_bridge->exten = ast_strdup(exten);
-	after_bridge->priority = priority;
-	after_bridge->run_h_exten = run_h_exten ? 1 : 0;
-	after_bridge->specific = specific ? 1 : 0;
-	datastore->data = after_bridge;
-	if ((parseable_goto && !after_bridge->parseable_goto)
-		|| (context && !after_bridge->context)
-		|| (exten && !after_bridge->exten)) {
-		ast_datastore_free(datastore);
-		return;
-	}
-
-	/* Put it on the channel replacing any existing one. */
-	ast_channel_lock(chan);
-	ast_after_bridge_goto_discard(chan);
-	ast_channel_datastore_add(chan, datastore);
-	ast_channel_unlock(chan);
-}
-
-void ast_after_bridge_set_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
-{
-	__after_bridge_set_goto(chan, 0, 1, context, exten, priority, NULL);
-}
-
-void ast_after_bridge_set_h(struct ast_channel *chan, const char *context)
-{
-	__after_bridge_set_goto(chan, 1, 0, context, NULL, 1, NULL);
-}
-
-void ast_after_bridge_set_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto)
-{
-	char *p_goto;
-
-	if (!ast_strlen_zero(parseable_goto)) {
-		p_goto = ast_strdupa(parseable_goto);
-		ast_replace_subargument_delimiter(p_goto);
-	} else {
-		p_goto = NULL;
-	}
-	__after_bridge_set_goto(chan, 0, 0, context, exten, priority, p_goto);
+	return bridge_channel;
 }
 
 void ast_bridge_notify_masquerade(struct ast_channel *chan)
@@ -2146,7 +1420,7 @@ void ast_bridge_notify_masquerade(struct ast_channel *chan)
  * Need to update the features parameter doxygen when this
  * change is made to be like ast_bridge_impart().
  */
-enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge,
+enum bridge_channel_state ast_bridge_join(struct ast_bridge *bridge,
 	struct ast_channel *chan,
 	struct ast_channel *swap,
 	struct ast_bridge_features *features,
@@ -2154,21 +1428,21 @@ enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge,
 	int pass_reference)
 {
 	struct ast_bridge_channel *bridge_channel;
-	enum ast_bridge_channel_state state;
+	enum bridge_channel_state state;
 
-	bridge_channel = bridge_channel_alloc(bridge);
+	bridge_channel = bridge_channel_internal_alloc(bridge);
 	if (pass_reference) {
 		ao2_ref(bridge, -1);
 	}
 	if (!bridge_channel) {
-		state = AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE;
+		state = BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE;
 		goto join_exit;
 	}
 /* BUGBUG features cannot be NULL when passed in. When it is changed to allocated we can do like ast_bridge_impart() and allocate one. */
 	ast_assert(features != NULL);
 	if (!features) {
 		ao2_ref(bridge_channel, -1);
-		state = AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE;
+		state = BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE;
 		goto join_exit;
 	}
 	if (tech_args) {
@@ -2184,7 +1458,7 @@ enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge,
 	bridge_channel->swap = swap;
 	bridge_channel->features = features;
 
-	bridge_channel_join(bridge_channel);
+	bridge_channel_internal_join(bridge_channel);
 	state = bridge_channel->state;
 
 	/* Cleanup all the data in the bridge channel after it leaves the bridge. */
@@ -2199,9 +1473,9 @@ enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge,
 
 join_exit:;
 /* BUGBUG this is going to cause problems for DTMF atxfer attended bridge between B & C.  Maybe an ast_bridge_join_internal() that does not do the after bridge goto for this case. */
-	after_bridge_callback_run(chan);
+	ast_bridge_run_after_callback(chan);
 	if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)
-		&& !ast_after_bridge_goto_setup(chan)) {
+		&& !ast_bridge_setup_after_goto(chan)) {
 		/* Claim the after bridge goto is an async goto destination. */
 		ast_channel_lock(chan);
 		ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
@@ -2219,15 +1493,15 @@ static void *bridge_channel_depart_thread(void *data)
 		ast_callid_threadassoc_add(bridge_channel->callid);
 	}
 
-	bridge_channel_join(bridge_channel);
+	bridge_channel_internal_join(bridge_channel);
 
 	/* cleanup */
 	bridge_channel->swap = NULL;
 	ast_bridge_features_destroy(bridge_channel->features);
 	bridge_channel->features = NULL;
 
-	after_bridge_callback_run_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART);
-	ast_after_bridge_goto_discard(bridge_channel->chan);
+	ast_bridge_discard_after_callback(bridge_channel->chan, AST_BRIDGE_AFTER_CB_REASON_DEPART);
+	ast_bridge_discard_after_goto(bridge_channel->chan);
 
 	return NULL;
 }
@@ -2242,7 +1516,7 @@ static void *bridge_channel_ind_thread(void *data)
 		ast_callid_threadassoc_add(bridge_channel->callid);
 	}
 
-	bridge_channel_join(bridge_channel);
+	bridge_channel_internal_join(bridge_channel);
 	chan = bridge_channel->chan;
 
 	/* cleanup */
@@ -2256,8 +1530,8 @@ static void *bridge_channel_ind_thread(void *data)
 
 	ao2_ref(bridge_channel, -1);
 
-	after_bridge_callback_run(chan);
-	ast_after_bridge_goto_run(chan);
+	ast_bridge_run_after_callback(chan);
+	ast_bridge_run_after_goto(chan);
 	return NULL;
 }
 
@@ -2275,7 +1549,7 @@ int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struc
 	}
 
 	/* Try to allocate a structure for the bridge channel */
-	bridge_channel = bridge_channel_alloc(bridge);
+	bridge_channel = bridge_channel_internal_alloc(bridge);
 	if (!bridge_channel) {
 		ast_bridge_features_destroy(features);
 		return -1;
@@ -2350,7 +1624,7 @@ int ast_bridge_depart(struct ast_channel *chan)
 	 * channel thread.
 	 */
 
-	ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 
 	/* Wait for the depart thread to die */
 	ast_debug(1, "Waiting for %p(%s) bridge thread to die.\n",
@@ -2378,7 +1652,7 @@ int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan)
 		return -1;
 	}
 
-	ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 
 	ast_bridge_unlock(bridge);
 
@@ -2425,10 +1699,10 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg
 	 * Move channels from src_bridge over to dst_bridge.
 	 *
 	 * We must use AST_LIST_TRAVERSE_SAFE_BEGIN() because
-	 * bridge_channel_pull() alters the list we are traversing.
+	 * bridge_channel_internal_pull() alters the list we are traversing.
 	 */
 	AST_LIST_TRAVERSE_SAFE_BEGIN(&src_bridge->channels, bridge_channel, entry) {
-		if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
 			/*
 			 * The channel is already leaving let it leave normally because
 			 * pulling it may delete hooks that should run for this channel.
@@ -2443,13 +1717,13 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg
 		if (kick_me) {
 			for (idx = 0; idx < num_kick; ++idx) {
 				if (bridge_channel == kick_me[idx]) {
-					ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+					ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 					break;
 				}
 			}
 		}
-		bridge_channel_pull(bridge_channel);
-		if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		bridge_channel_internal_pull(bridge_channel);
+		if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
 			/*
 			 * The channel died as a result of being pulled or it was
 			 * kicked.  Leave it pointing to the original bridge.
@@ -2460,8 +1734,8 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg
 		/* Point to new bridge.*/
 		bridge_channel_change_bridge(bridge_channel, dst_bridge);
 
-		if (bridge_channel_push(bridge_channel)) {
-			ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+		if (bridge_channel_internal_push(bridge_channel)) {
+			ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 		}
 	}
 	AST_LIST_TRAVERSE_SAFE_END;
@@ -2474,9 +1748,9 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg
 		for (idx = 0; idx < num_kick; ++idx) {
 			bridge_channel = kick_me[idx];
 			ast_bridge_channel_lock(bridge_channel);
-			if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
-				ast_bridge_channel_leave_bridge_nolock(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
-				bridge_channel_pull(bridge_channel);
+			if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
+				ast_bridge_channel_leave_bridge_nolock(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+				bridge_channel_internal_pull(bridge_channel);
 			}
 			ast_bridge_channel_unlock(bridge_channel);
 		}
@@ -2689,8 +1963,8 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri
 	orig_bridge = bridge_channel->bridge;
 	was_in_bridge = bridge_channel->in_bridge;
 
-	bridge_channel_pull(bridge_channel);
-	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+	bridge_channel_internal_pull(bridge_channel);
+	if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
 		/*
 		 * The channel died as a result of being pulled.  Leave it
 		 * pointing to the original bridge.
@@ -2703,17 +1977,17 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri
 	ao2_ref(orig_bridge, +1);/* Keep a ref in case the push fails. */
 	bridge_channel_change_bridge(bridge_channel, dst_bridge);
 
-	if (bridge_channel_push(bridge_channel)) {
+	if (bridge_channel_internal_push(bridge_channel)) {
 		/* Try to put the channel back into the original bridge. */
 		if (attempt_recovery && was_in_bridge) {
 			/* Point back to original bridge. */
 			bridge_channel_change_bridge(bridge_channel, orig_bridge);
 
-			if (bridge_channel_push(bridge_channel)) {
-				ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+			if (bridge_channel_internal_push(bridge_channel)) {
+				ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 			}
 		} else {
-			ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+			ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 		}
 		res = -1;
 	}
@@ -2767,7 +2041,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge *
 			ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid);
 		return -1;
 	}
-	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+	if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
 		ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, channel leaving bridge.\n",
 			ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid);
 		return -1;
@@ -2789,7 +2063,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge *
 				ast_channel_name(swap));
 			return -1;
 		}
-		if (bridge_channel_swap->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		if (bridge_channel_swap->state != BRIDGE_CHANNEL_STATE_WAIT) {
 			ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, swap channel %s leaving bridge.\n",
 				ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid,
 				ast_channel_name(swap));
@@ -2894,12 +2168,6 @@ static int bridge_allows_optimization(struct ast_bridge *bridge)
 		|| ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY));
 }
 
-static int bridge_channel_allows_optimization(struct ast_bridge_channel *bridge_channel)
-{
-	return bridge_channel->in_bridge
-		&& AST_LIST_EMPTY(&bridge_channel->wr_queue);
-}
-
 /*!
  * \internal
  * \brief Lock the unreal channel stack for chan and prequalify it.
@@ -2929,13 +2197,13 @@ static struct ast_bridge *optimize_lock_chan_stack(struct ast_channel *chan)
 		return NULL;
 	}
 	bridge = bridge_channel->bridge;
-	if (bridge_channel->activity != AST_BRIDGE_CHANNEL_THREAD_SIMPLE
-		|| bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT
+	if (bridge_channel->activity != BRIDGE_CHANNEL_THREAD_SIMPLE
+		|| bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT
 		|| ast_bridge_trylock(bridge)) {
 		ast_bridge_channel_unlock(bridge_channel);
 		return NULL;
 	}
-	if (!bridge_channel_allows_optimization(bridge_channel) ||
+	if (!bridge_channel_internal_allows_optimization(bridge_channel) ||
 			!bridge_allows_optimization(bridge)) {
 		ast_bridge_unlock(bridge);
 		ast_bridge_channel_unlock(bridge_channel);
@@ -2977,15 +2245,15 @@ static struct ast_bridge *optimize_lock_peer_stack(struct ast_channel *peer)
 		return NULL;
 	}
 	bridge = bridge_channel->bridge;
-	if (bridge_channel->activity != AST_BRIDGE_CHANNEL_THREAD_IDLE
-		|| bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT
+	if (bridge_channel->activity != BRIDGE_CHANNEL_THREAD_IDLE
+		|| bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT
 		|| ast_bridge_trylock(bridge)) {
 		ast_bridge_channel_unlock(bridge_channel);
 		ast_channel_unlock(peer);
 		return NULL;
 	}
 	if (!bridge_allows_optimization(bridge) ||
-			!bridge_channel_allows_optimization(bridge_channel)) {
+			!bridge_channel_internal_allows_optimization(bridge_channel)) {
 		ast_bridge_unlock(bridge);
 		ast_bridge_channel_unlock(bridge_channel);
 		ast_channel_unlock(peer);
@@ -3099,7 +2367,7 @@ static int try_swap_optimize_out(struct ast_bridge *chan_bridge,
 	}
 
 	other = ast_bridge_channel_peer(src_bridge_channel);
-	if (other && other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+	if (other && other->state == BRIDGE_CHANNEL_STATE_WAIT) {
 		ast_verb(3, "Move-swap optimizing %s <-- %s.\n",
 			ast_channel_name(dst_bridge_channel->chan),
 			ast_channel_name(other->chan));
@@ -3111,7 +2379,7 @@ static int try_swap_optimize_out(struct ast_bridge *chan_bridge,
 		}
 		other->swap = dst_bridge_channel->chan;
 		if (!bridge_do_move(dst_bridge, other, 1, 1)) {
-			ast_bridge_channel_leave_bridge(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+			ast_bridge_channel_leave_bridge(src_bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 			res = -1;
 			if (pvt && pvt->callbacks && pvt->callbacks->optimization_finished) {
 				pvt->callbacks->optimization_finished(pvt);
@@ -3343,7 +2611,7 @@ int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan)
 		return -1;
 	}
 
-	bridge_channel_suspend_nolock(bridge_channel);
+	bridge_channel_internal_suspend_nolock(bridge_channel);
 
 	ast_bridge_unlock(bridge);
 
@@ -3362,7 +2630,7 @@ int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan)
 		return -1;
 	}
 
-	bridge_channel_unsuspend_nolock(bridge_channel);
+	bridge_channel_internal_unsuspend_nolock(bridge_channel);
 
 	ast_bridge_unlock(bridge);
 
@@ -4686,7 +3954,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
 		goto publish;
 	}
 
-	if (bridge_channel_queue_blind_transfer(transferee, exten, context,
+	if (bridge_channel_internal_queue_blind_transfer(transferee, exten, context,
 				new_channel_cb, user_data)) {
 		transfer_result = AST_BRIDGE_TRANSFER_FAIL;
 		goto publish;
@@ -4722,7 +3990,7 @@ static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge
 
 	bridged_to_source = ast_bridge_channel_peer(source_bridge_channel);
 	if (bridged_to_source
-		&& bridged_to_source->state == AST_BRIDGE_CHANNEL_STATE_WAIT
+		&& bridged_to_source->state == BRIDGE_CHANNEL_STATE_WAIT
 		&& !ast_test_flag(&bridged_to_source->features->feature_flags,
 			AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE)) {
 		bridged_to_source->swap = swap_channel;
@@ -4730,7 +3998,7 @@ static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge
 			return AST_BRIDGE_TRANSFER_FAIL;
 		}
 		/* Must kick the source channel out of its bridge. */
-		ast_bridge_channel_leave_bridge(source_bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+		ast_bridge_channel_leave_bridge(source_bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 		return AST_BRIDGE_TRANSFER_SUCCESS;
 	} else {
 		return AST_BRIDGE_TRANSFER_INVALID;
@@ -4953,7 +4221,7 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
 	}
 
 	app = ast_strdupa(ast_channel_appl(chan_unbridged));
-	if (bridge_channel_queue_attended_transfer(transferee, chan_unbridged)) {
+	if (bridge_channel_internal_queue_attended_transfer(transferee, chan_unbridged)) {
 		res = AST_BRIDGE_TRANSFER_FAIL;
 		goto end;
 	}
diff --git a/main/bridging_after.c b/main/bridging_after.c
new file mode 100644
index 0000000000000000000000000000000000000000..8ab4bc842232739b5e76a3b33dc2750dc8b3085c
--- /dev/null
+++ b/main/bridging_after.c
@@ -0,0 +1,640 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007 - 2009, Digium, Inc.
+ *
+ * Richard Mudgett <rmudgett@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief After Bridge Execution API
+ *
+ * \author Richard Mudgett <rmudgett@digium.com>
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/bridging_after.h"
+
+struct after_bridge_cb_node {
+	/*! Next list node. */
+	AST_LIST_ENTRY(after_bridge_cb_node) list;
+	/*! Desired callback function. */
+	ast_bridge_after_cb callback;
+	/*! After bridge callback will not be called and destroy any resources data may contain. */
+	ast_bridge_after_cb_failed failed;
+	/*! Extra data to pass to the callback. */
+	void *data;
+	/*! Reason the after bridge callback failed. */
+	enum ast_bridge_after_cb_reason reason;
+};
+
+struct after_bridge_cb_ds {
+	/*! After bridge callbacks container. */
+	AST_LIST_HEAD(, after_bridge_cb_node) callbacks;
+};
+
+/*!
+ * \internal
+ * \brief Indicate after bridge callback failed.
+ * \since 12.0.0
+ *
+ * \param node After bridge callback node.
+ *
+ * \return Nothing
+ */
+static void after_bridge_cb_failed(struct after_bridge_cb_node *node)
+{
+	if (node->failed) {
+		node->failed(node->reason, node->data);
+		node->failed = NULL;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Run discarding any after bridge callbacks.
+ * \since 12.0.0
+ *
+ * \param after_bridge After bridge callback container process.
+ * \param reason Why are we doing this.
+ *
+ * \return Nothing
+ */
+static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_bridge_after_cb_reason reason)
+{
+	struct after_bridge_cb_node *node;
+
+	for (;;) {
+		AST_LIST_LOCK(&after_bridge->callbacks);
+		node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
+		AST_LIST_UNLOCK(&after_bridge->callbacks);
+		if (!node) {
+			break;
+		}
+		if (!node->reason) {
+			node->reason = reason;
+		}
+		after_bridge_cb_failed(node);
+		ast_free(node);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Destroy the after bridge callback datastore.
+ * \since 12.0.0
+ *
+ * \param data After bridge callback data to destroy.
+ *
+ * \return Nothing
+ */
+static void after_bridge_cb_destroy(void *data)
+{
+	struct after_bridge_cb_ds *after_bridge = data;
+
+	after_bridge_cb_run_discard(after_bridge, AST_BRIDGE_AFTER_CB_REASON_DESTROY);
+
+	AST_LIST_HEAD_DESTROY(&after_bridge->callbacks);
+	ast_free(after_bridge);
+}
+
+static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan);
+
+/*!
+ * \internal
+ * \brief Fixup the after bridge callback datastore.
+ * \since 12.0.0
+ *
+ * \param data After bridge callback data to fixup.
+ * \param old_chan The datastore is moving from this channel.
+ * \param new_chan The datastore is moving to this channel.
+ *
+ * \return Nothing
+ */
+static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+	struct after_bridge_cb_ds *after_bridge;
+	struct after_bridge_cb_node *node;
+
+	after_bridge = after_bridge_cb_find(new_chan);
+	if (!after_bridge) {
+		return;
+	}
+
+	AST_LIST_LOCK(&after_bridge->callbacks);
+	node = AST_LIST_LAST(&after_bridge->callbacks);
+	if (node && !node->reason) {
+		node->reason = AST_BRIDGE_AFTER_CB_REASON_MASQUERADE;
+	}
+	AST_LIST_UNLOCK(&after_bridge->callbacks);
+}
+
+static const struct ast_datastore_info after_bridge_cb_info = {
+	.type = "after-bridge-cb",
+	.destroy = after_bridge_cb_destroy,
+	.chan_fixup = after_bridge_cb_fixup,
+};
+
+/*!
+ * \internal
+ * \brief Find an after bridge callback datastore container.
+ * \since 12.0.0
+ *
+ * \param chan Channel to find the after bridge callback container on.
+ *
+ * \retval after_bridge datastore container on success.
+ * \retval NULL on error.
+ */
+static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+	SCOPED_CHANNELLOCK(lock, chan);
+
+	datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
+	if (!datastore) {
+		return NULL;
+	}
+	return datastore->data;
+}
+
+/*!
+ * \internal
+ * \brief Setup/create an after bridge callback datastore container.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup/create the after bridge callback container on.
+ *
+ * \retval after_bridge datastore container on success.
+ * \retval NULL on error.
+ */
+static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+	struct after_bridge_cb_ds *after_bridge;
+	SCOPED_CHANNELLOCK(lock, chan);
+
+	datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
+	if (datastore) {
+		return datastore->data;
+	}
+
+	/* Create a new datastore. */
+	datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
+	if (!datastore) {
+		return NULL;
+	}
+	after_bridge = ast_calloc(1, sizeof(*after_bridge));
+	if (!after_bridge) {
+		ast_datastore_free(datastore);
+		return NULL;
+	}
+	AST_LIST_HEAD_INIT(&after_bridge->callbacks);
+	datastore->data = after_bridge;
+	ast_channel_datastore_add(chan, datastore);
+
+	return datastore->data;
+}
+
+void ast_bridge_run_after_callback(struct ast_channel *chan)
+{
+	struct after_bridge_cb_ds *after_bridge;
+	struct after_bridge_cb_node *node;
+
+	after_bridge = after_bridge_cb_find(chan);
+	if (!after_bridge) {
+		return;
+	}
+
+	for (;;) {
+		AST_LIST_LOCK(&after_bridge->callbacks);
+		node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
+		AST_LIST_UNLOCK(&after_bridge->callbacks);
+		if (!node) {
+			break;
+		}
+		if (node->reason) {
+			after_bridge_cb_failed(node);
+		} else {
+			node->failed = NULL;
+			node->callback(chan, node->data);
+		}
+		ast_free(node);
+	}
+}
+
+void ast_bridge_discard_after_callback(struct ast_channel *chan, enum ast_bridge_after_cb_reason reason)
+{
+	struct after_bridge_cb_ds *after_bridge;
+
+	after_bridge = after_bridge_cb_find(chan);
+	if (!after_bridge) {
+		return;
+	}
+
+	after_bridge_cb_run_discard(after_bridge, reason);
+}
+
+int ast_bridge_set_after_callback(struct ast_channel *chan, ast_bridge_after_cb callback, ast_bridge_after_cb_failed failed, void *data)
+{
+	struct after_bridge_cb_ds *after_bridge;
+	struct after_bridge_cb_node *new_node;
+	struct after_bridge_cb_node *last_node;
+
+	/* Sanity checks. */
+	ast_assert(chan != NULL);
+	if (!chan || !callback) {
+		return -1;
+	}
+
+	after_bridge = after_bridge_cb_setup(chan);
+	if (!after_bridge) {
+		return -1;
+	}
+
+	/* Create a new callback node. */
+	new_node = ast_calloc(1, sizeof(*new_node));
+	if (!new_node) {
+		return -1;
+	}
+	new_node->callback = callback;
+	new_node->failed = failed;
+	new_node->data = data;
+
+	/* Put it in the container disabling any previously active one. */
+	AST_LIST_LOCK(&after_bridge->callbacks);
+	last_node = AST_LIST_LAST(&after_bridge->callbacks);
+	if (last_node && !last_node->reason) {
+		last_node->reason = AST_BRIDGE_AFTER_CB_REASON_REPLACED;
+	}
+	AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list);
+	AST_LIST_UNLOCK(&after_bridge->callbacks);
+	return 0;
+}
+
+const char *reason_strings[] = {
+	[AST_BRIDGE_AFTER_CB_REASON_DESTROY] = "Channel destroyed (hungup)",
+	[AST_BRIDGE_AFTER_CB_REASON_REPLACED] = "Callback was replaced",
+	[AST_BRIDGE_AFTER_CB_REASON_MASQUERADE] = "Channel masqueraded",
+	[AST_BRIDGE_AFTER_CB_REASON_DEPART] = "Channel was departed from bridge",
+	[AST_BRIDGE_AFTER_CB_REASON_REMOVED] = "Callback was removed",
+};
+
+const char *ast_bridge_after_cb_reason_string(enum ast_bridge_after_cb_reason reason)
+{
+	if (reason < AST_BRIDGE_AFTER_CB_REASON_DESTROY
+		|| AST_BRIDGE_AFTER_CB_REASON_REMOVED < reason
+		|| !reason_strings[reason]) {
+		return "Unknown";
+	}
+
+	return reason_strings[reason];
+}
+
+struct after_bridge_goto_ds {
+	/*! Goto string that can be parsed by ast_parseable_goto(). */
+	const char *parseable_goto;
+	/*! Specific goto context or default context for parseable_goto. */
+	const char *context;
+	/*! Specific goto exten or default exten for parseable_goto. */
+	const char *exten;
+	/*! Specific goto priority or default priority for parseable_goto. */
+	int priority;
+	/*! TRUE if the peer should run the h exten. */
+	unsigned int run_h_exten:1;
+	/*! Specific goto location */
+	unsigned int specific:1;
+};
+
+/*!
+ * \internal
+ * \brief Destroy the after bridge goto datastore.
+ * \since 12.0.0
+ *
+ * \param data After bridge goto data to destroy.
+ *
+ * \return Nothing
+ */
+static void after_bridge_goto_destroy(void *data)
+{
+	struct after_bridge_goto_ds *after_bridge = data;
+
+	ast_free((char *) after_bridge->parseable_goto);
+	ast_free((char *) after_bridge->context);
+	ast_free((char *) after_bridge->exten);
+}
+
+/*!
+ * \internal
+ * \brief Fixup the after bridge goto datastore.
+ * \since 12.0.0
+ *
+ * \param data After bridge goto data to fixup.
+ * \param old_chan The datastore is moving from this channel.
+ * \param new_chan The datastore is moving to this channel.
+ *
+ * \return Nothing
+ */
+static void after_bridge_goto_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+	/* There can be only one.  Discard any already on the new channel. */
+	ast_bridge_discard_after_goto(new_chan);
+}
+
+static const struct ast_datastore_info after_bridge_goto_info = {
+	.type = "after-bridge-goto",
+	.destroy = after_bridge_goto_destroy,
+	.chan_fixup = after_bridge_goto_fixup,
+};
+
+/*!
+ * \internal
+ * \brief Remove channel goto location after the bridge and return it.
+ * \since 12.0.0
+ *
+ * \param chan Channel to remove after bridge goto location.
+ *
+ * \retval datastore on success.
+ * \retval NULL on error or not found.
+ */
+static struct ast_datastore *after_bridge_goto_remove(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+
+	ast_channel_lock(chan);
+	datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL);
+	if (datastore && ast_channel_datastore_remove(chan, datastore)) {
+		datastore = NULL;
+	}
+	ast_channel_unlock(chan);
+
+	return datastore;
+}
+
+void ast_bridge_discard_after_goto(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+
+	datastore = after_bridge_goto_remove(chan);
+	if (datastore) {
+		ast_datastore_free(datastore);
+	}
+}
+
+void ast_bridge_read_after_goto(struct ast_channel *chan, char *buffer, size_t buf_size)
+{
+	struct ast_datastore *datastore;
+	struct after_bridge_goto_ds *after_bridge;
+	char *current_pos = buffer;
+	size_t remaining_size = buf_size;
+
+	SCOPED_CHANNELLOCK(lock, chan);
+
+	datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL);
+	if (!datastore) {
+		buffer[0] = '\0';
+		return;
+	}
+
+	after_bridge = datastore->data;
+
+	if (after_bridge->parseable_goto) {
+		snprintf(buffer, buf_size, "%s", after_bridge->parseable_goto);
+		return;
+	}
+
+	if (!ast_strlen_zero(after_bridge->context)) {
+		snprintf(current_pos, remaining_size, "%s,", after_bridge->context);
+		remaining_size = remaining_size - strlen(current_pos);
+		current_pos += strlen(current_pos);
+	}
+
+	if (after_bridge->run_h_exten) {
+		snprintf(current_pos, remaining_size, "h,");
+		remaining_size = remaining_size - strlen(current_pos);
+		current_pos += strlen(current_pos);
+	} else if (!ast_strlen_zero(after_bridge->exten)) {
+		snprintf(current_pos, remaining_size, "%s,", after_bridge->exten);
+		remaining_size = remaining_size - strlen(current_pos);
+		current_pos += strlen(current_pos);
+	}
+
+	snprintf(current_pos, remaining_size, "%d", after_bridge->priority);
+}
+
+int ast_bridge_setup_after_goto(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+	struct after_bridge_goto_ds *after_bridge;
+	int goto_failed = -1;
+
+	/* Determine if we are going to setup a dialplan location and where. */
+	if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
+		/* An async goto has already setup a location. */
+		ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_ASYNCGOTO);
+		if (!ast_check_hangup(chan)) {
+			goto_failed = 0;
+		}
+		return goto_failed;
+	}
+
+	/* Get after bridge goto datastore. */
+	datastore = after_bridge_goto_remove(chan);
+	if (!datastore) {
+		return goto_failed;
+	}
+
+	after_bridge = datastore->data;
+	if (after_bridge->run_h_exten) {
+		if (ast_exists_extension(chan, after_bridge->context, "h", 1,
+			S_COR(ast_channel_caller(chan)->id.number.valid,
+				ast_channel_caller(chan)->id.number.str, NULL))) {
+			ast_debug(1, "Running after bridge goto h exten %s,h,1\n",
+				ast_channel_context(chan));
+			ast_pbx_h_exten_run(chan, after_bridge->context);
+		}
+	} else if (!ast_check_hangup(chan)) {
+		if (after_bridge->specific) {
+			goto_failed = ast_explicit_goto(chan, after_bridge->context,
+				after_bridge->exten, after_bridge->priority);
+		} else if (!ast_strlen_zero(after_bridge->parseable_goto)) {
+			char *context;
+			char *exten;
+			int priority;
+
+			/* Option F(x) for Bridge(), Dial(), and Queue() */
+
+			/* Save current dialplan location in case of failure. */
+			context = ast_strdupa(ast_channel_context(chan));
+			exten = ast_strdupa(ast_channel_exten(chan));
+			priority = ast_channel_priority(chan);
+
+			/* Set current dialplan position to default dialplan position */
+			ast_explicit_goto(chan, after_bridge->context, after_bridge->exten,
+				after_bridge->priority);
+
+			/* Then perform the goto */
+			goto_failed = ast_parseable_goto(chan, after_bridge->parseable_goto);
+			if (goto_failed) {
+				/* Restore original dialplan location. */
+				ast_channel_context_set(chan, context);
+				ast_channel_exten_set(chan, exten);
+				ast_channel_priority_set(chan, priority);
+			}
+		} else {
+			/* Option F() for Bridge(), Dial(), and Queue() */
+			goto_failed = ast_goto_if_exists(chan, after_bridge->context,
+				after_bridge->exten, after_bridge->priority + 1);
+		}
+		if (!goto_failed) {
+			if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
+				ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
+			}
+
+			ast_debug(1, "Setup after bridge goto location to %s,%s,%d.\n",
+				ast_channel_context(chan),
+				ast_channel_exten(chan),
+				ast_channel_priority(chan));
+		}
+	}
+
+	/* Discard after bridge goto datastore. */
+	ast_datastore_free(datastore);
+
+	return goto_failed;
+}
+
+void ast_bridge_run_after_goto(struct ast_channel *chan)
+{
+	int goto_failed;
+
+	goto_failed = ast_bridge_setup_after_goto(chan);
+	if (goto_failed || ast_pbx_run(chan)) {
+		ast_hangup(chan);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Set after bridge goto location of channel.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ * \param run_h_exten TRUE if the h exten should be run.
+ * \param specific TRUE if the context/exten/priority is exactly specified.
+ * \param context Context to goto after bridge.
+ * \param exten Exten to goto after bridge. (Could be NULL if run_h_exten)
+ * \param priority Priority to goto after bridge.
+ * \param parseable_goto User specified goto string. (Could be NULL)
+ *
+ * \details Add a channel datastore to setup the goto location
+ * when the channel leaves the bridge and run a PBX from there.
+ *
+ * If run_h_exten then execute the h exten found in the given context.
+ * Else if specific then goto the given context/exten/priority.
+ * Else if parseable_goto then use the given context/exten/priority
+ *   as the relative position for the parseable_goto.
+ * Else goto the given context/exten/priority+1.
+ *
+ * \return Nothing
+ */
+static void __after_bridge_set_goto(struct ast_channel *chan, int run_h_exten, int specific, const char *context, const char *exten, int priority, const char *parseable_goto)
+{
+	struct ast_datastore *datastore;
+	struct after_bridge_goto_ds *after_bridge;
+
+	/* Sanity checks. */
+	ast_assert(chan != NULL);
+	if (!chan) {
+		return;
+	}
+	if (run_h_exten) {
+		ast_assert(run_h_exten && context);
+		if (!context) {
+			return;
+		}
+	} else {
+		ast_assert(context && exten && 0 < priority);
+		if (!context || !exten || priority < 1) {
+			return;
+		}
+	}
+
+	/* Create a new datastore. */
+	datastore = ast_datastore_alloc(&after_bridge_goto_info, NULL);
+	if (!datastore) {
+		return;
+	}
+	after_bridge = ast_calloc(1, sizeof(*after_bridge));
+	if (!after_bridge) {
+		ast_datastore_free(datastore);
+		return;
+	}
+
+	/* Initialize it. */
+	after_bridge->parseable_goto = ast_strdup(parseable_goto);
+	after_bridge->context = ast_strdup(context);
+	after_bridge->exten = ast_strdup(exten);
+	after_bridge->priority = priority;
+	after_bridge->run_h_exten = run_h_exten ? 1 : 0;
+	after_bridge->specific = specific ? 1 : 0;
+	datastore->data = after_bridge;
+	if ((parseable_goto && !after_bridge->parseable_goto)
+		|| (context && !after_bridge->context)
+		|| (exten && !after_bridge->exten)) {
+		ast_datastore_free(datastore);
+		return;
+	}
+
+	/* Put it on the channel replacing any existing one. */
+	ast_channel_lock(chan);
+	ast_bridge_discard_after_goto(chan);
+	ast_channel_datastore_add(chan, datastore);
+	ast_channel_unlock(chan);
+}
+
+void ast_bridge_set_after_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
+{
+	__after_bridge_set_goto(chan, 0, 1, context, exten, priority, NULL);
+}
+
+void ast_bridge_set_after_h(struct ast_channel *chan, const char *context)
+{
+	__after_bridge_set_goto(chan, 1, 0, context, NULL, 1, NULL);
+}
+
+void ast_bridge_set_after_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto)
+{
+	char *p_goto;
+
+	if (!ast_strlen_zero(parseable_goto)) {
+		p_goto = ast_strdupa(parseable_goto);
+		ast_replace_subargument_delimiter(p_goto);
+	} else {
+		p_goto = NULL;
+	}
+	__after_bridge_set_goto(chan, 0, 0, context, exten, priority, p_goto);
+}
diff --git a/main/bridging_basic.c b/main/bridging_basic.c
index 8fcad7560d8d66a1235c327ba15a6620b4c49b08..7f9cb71fdb668f9e47e4636cc3cf8112244e70c8 100644
--- a/main/bridging_basic.c
+++ b/main/bridging_basic.c
@@ -36,13 +36,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/linkedlists.h"
 #include "asterisk/bridging.h"
 #include "asterisk/bridging_basic.h"
+#include "asterisk/bridging_after.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/features_config.h"
 #include "asterisk/pbx.h"
 #include "asterisk/file.h"
 #include "asterisk/app.h"
 #include "asterisk/bridging_internal.h"
-#include "asterisk/bridging_channel_internal.h"
 #include "asterisk/dial.h"
 #include "asterisk/stasis_bridging.h"
 
@@ -279,13 +279,13 @@ static int basic_hangup_hook(struct ast_bridge *bridge, struct ast_bridge_channe
 
 	ast_bridge_channel_lock_bridge(bridge_channel);
 	AST_LIST_TRAVERSE(&bridge->channels, iter, entry) {
-		if (iter != bridge_channel && iter->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		if (iter != bridge_channel && iter->state == BRIDGE_CHANNEL_STATE_WAIT) {
 			++bridge_count;
 		}
 	}
 	if (2 <= bridge_count) {
 		/* Just allow this channel to leave the multi-party bridge. */
-		ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 	}
 	ast_bridge_unlock(bridge_channel->bridge);
 	return 0;
@@ -2300,7 +2300,7 @@ static int bridge_personality_atxfer_push(struct ast_bridge *self, struct ast_br
 
 static void transfer_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct attended_transfer_properties *props)
 {
-	if (self->num_channels > 1 || bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+	if (self->num_channels > 1 || bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
 		return;
 	}
 
@@ -2794,13 +2794,13 @@ static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_c
 	if (!ast_strlen_zero(goto_on_blindxfr)) {
 		ast_debug(1, "After transfer, transferer %s goes to %s\n",
 				ast_channel_name(bridge_channel->chan), goto_on_blindxfr);
-		ast_after_bridge_set_go_on(bridge_channel->chan, NULL, NULL, 0, goto_on_blindxfr);
+		ast_bridge_set_after_go_on(bridge_channel->chan, NULL, NULL, 0, goto_on_blindxfr);
 	}
 
 	if (ast_bridge_transfer_blind(0, bridge_channel->chan, exten, context, blind_transfer_cb,
 			bridge_channel->chan) != AST_BRIDGE_TRANSFER_SUCCESS &&
 			!ast_strlen_zero(goto_on_blindxfr)) {
-		ast_after_bridge_goto_discard(bridge_channel->chan);
+		ast_bridge_discard_after_goto(bridge_channel->chan);
 	}
 
 	return 0;
diff --git a/main/bridging_channel.c b/main/bridging_channel.c
index 385545c2c85445b8f888b4e52372019ee67aaa61..284f08732d62d0fc3b15e04263c7425240ddba07 100644
--- a/main/bridging_channel.c
+++ b/main/bridging_channel.c
@@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/timing.h"
 #include "asterisk/bridging.h"
 #include "asterisk/bridging_channel.h"
+#include "asterisk/bridging_after.h"
 #include "asterisk/bridging_channel_internal.h"
 #include "asterisk/bridging_internal.h"
 #include "asterisk/stasis_bridging.h"
@@ -67,18 +68,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  */
 typedef int (*ast_bridge_channel_post_action_data)(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen);
 
-struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request)
-{
-	struct ast_bridge *bridge;
-
-	ast_bridge_channel_lock_bridge(bridge_channel);
-	bridge = bridge_channel->bridge;
-	ao2_ref(bridge, +1);
-	bridge_merge_inhibit_nolock(bridge, request);
-	ast_bridge_unlock(bridge);
-	return bridge;
-}
-
 void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
 {
 	struct ast_bridge *bridge;
@@ -101,6 +90,25 @@ void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
 	}
 }
 
+int ast_bridge_channel_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking)
+{
+	struct ast_frame action = {
+		.frametype = AST_FRAME_BRIDGE_ACTION,
+		.subclass.integer = started_talking
+			? BRIDGE_CHANNEL_ACTION_TALKING_START : BRIDGE_CHANNEL_ACTION_TALKING_STOP,
+	};
+
+	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 */
 static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel)
 {
 	if (!pthread_equal(pthread_self(), bridge_channel->thread)) {
@@ -111,6 +119,102 @@ 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)
+{
+/* BUGBUG need cause code for the bridge_channel leaving the bridge. */
+	if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
+		return;
+	}
+
+	ast_debug(1, "Setting %p(%s) state from:%d to:%d\n",
+		bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state,
+		new_state);
+
+	/* Change the state on the bridge channel */
+	bridge_channel->state = new_state;
+
+	bridge_channel_poke(bridge_channel);
+}
+
+struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge *bridge = bridge_channel->bridge;
+	struct ast_bridge_channel *other = NULL;
+
+	if (bridge_channel->in_bridge && bridge->num_channels == 2) {
+		AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
+			if (other != bridge_channel) {
+				break;
+			}
+		}
+	}
+
+	return other;
+}
+
+void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel)
+{
+	/* Restore original formats of the channel as they came in */
+	if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) {
+		ast_debug(1, "Bridge is returning %p(%s) to read format %s\n",
+			bridge_channel, ast_channel_name(bridge_channel->chan),
+			ast_getformatname(&bridge_channel->read_format));
+		if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) {
+			ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n",
+				bridge_channel, ast_channel_name(bridge_channel->chan),
+				ast_getformatname(&bridge_channel->read_format));
+		}
+	}
+	if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) {
+		ast_debug(1, "Bridge is returning %p(%s) to write format %s\n",
+			bridge_channel, ast_channel_name(bridge_channel->chan),
+			ast_getformatname(&bridge_channel->write_format));
+		if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) {
+			ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n",
+				bridge_channel, ast_channel_name(bridge_channel->chan),
+				ast_getformatname(&bridge_channel->write_format));
+		}
+	}
+}
+
+struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request)
+{
+	struct ast_bridge *bridge;
+
+	ast_bridge_channel_lock_bridge(bridge_channel);
+	bridge = bridge_channel->bridge;
+	ao2_ref(bridge, +1);
+	bridge_merge_inhibit_nolock(bridge, request);
+	ast_bridge_unlock(bridge);
+	return bridge;
+}
+
+void ast_bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
+{
+	struct ast_bridge_channel *other = NULL;
+	struct ast_bridge *bridge = bridge_channel->bridge;
+	const char *oldest_linkedid = ast_channel_linkedid(bridge_channel->chan);
+
+	AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
+		if (other == swap) {
+			continue;
+		}
+		oldest_linkedid = ast_channel_oldest_linkedid(oldest_linkedid, ast_channel_linkedid(other->chan));
+	}
+
+	if (ast_strlen_zero(oldest_linkedid)) {
+		return;
+	}
+
+	ast_channel_linkedid_set(bridge_channel->chan, oldest_linkedid);
+	AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
+		if (other == swap) {
+			continue;
+		}
+		ast_channel_linkedid_set(other->chan, oldest_linkedid);
+	}
+}
+
 void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
 {
 	struct ast_bridge *bridge = bridge_channel->bridge;
@@ -156,30 +260,120 @@ void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_ch
 	}
 }
 
-void ast_bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
+/*!
+* \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)
 {
-	struct ast_bridge_channel *other = NULL;
-	struct ast_bridge *bridge = bridge_channel->bridge;
-	const char *oldest_linkedid = ast_channel_linkedid(bridge_channel->chan);
+	struct ast_bridge_features *features = bridge_channel->features;
+	struct ast_bridge_hook *hook;
+	struct ao2_iterator iter;
 
-	AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
-		if (other == swap) {
+	/* Run any hangup hooks. */
+	iter = ao2_iterator_init(features->other_hooks, 0);
+	for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
+		int remove_me;
+
+		if (hook->type != AST_BRIDGE_HOOK_TYPE_HANGUP) {
 			continue;
 		}
-		oldest_linkedid = ast_channel_oldest_linkedid(oldest_linkedid, ast_channel_linkedid(other->chan));
+		remove_me = hook->callback(bridge_channel->bridge, 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));
+			ao2_unlink(features->other_hooks, hook);
+		}
 	}
+	ao2_iterator_destroy(&iter);
 
-	if (ast_strlen_zero(oldest_linkedid)) {
-		return;
-	}
+	/* Default hangup action. */
+	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
+}
 
-	ast_channel_linkedid_set(bridge_channel->chan, oldest_linkedid);
-	AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
-		if (other == swap) {
-			continue;
-		}
-		ast_channel_linkedid_set(other->chan, oldest_linkedid);
-	}
+/*!
+ * \internal
+ * \brief Write an \ref ast_frame onto the bridge channel
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to queue the frame onto.
+ * \param frame The frame to write onto the bridge_channel
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+	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).
+ *
+ * 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.
+ */
+	bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame);
+	ast_bridge_unlock(bridge_channel->bridge);
+
+	/*
+	 * Claim successful write to bridge.  If deferred frame
+	 * support is added, claim successfully deferred.
+	 */
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Queue an action frame onto the bridge channel with data.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to queue the frame onto.
+ * \param action Type of bridge action frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen)
+{
+	struct ast_frame frame = {
+		.frametype = AST_FRAME_BRIDGE_ACTION,
+		.subclass.integer = action,
+		.datalen = datalen,
+		.data.ptr = (void *) data,
+	};
+
+	return ast_bridge_channel_queue_frame(bridge_channel, &frame);
+}
+
+/*!
+ * \internal
+ * \brief Write an action frame onto the bridge channel with data.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to queue the frame onto.
+ * \param action Type of bridge action frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen)
+{
+	struct ast_frame frame = {
+		.frametype = AST_FRAME_BRIDGE_ACTION,
+		.subclass.integer = action,
+		.datalen = datalen,
+		.data.ptr = (void *) data,
+	};
+
+	return bridge_channel_write_frame(bridge_channel, &frame);
 }
 
 int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr)
@@ -206,7 +400,7 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st
 	}
 
 	ast_bridge_channel_lock(bridge_channel);
-	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+	if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
 		/* Drop frames on channels leaving the bridge. */
 		ast_bridge_channel_unlock(bridge_channel);
 		ast_frfree(dup);
@@ -218,44 +412,8 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st
 		ast_log(LOG_ERROR, "We couldn't write alert pipe for %p(%s)... something is VERY wrong\n",
 			bridge_channel, ast_channel_name(bridge_channel->chan));
 	}
-	ast_bridge_channel_unlock(bridge_channel);
-	return 0;
-}
-
-/*!
- * \brief Queue an action frame onto the bridge channel with data.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel to queue the frame onto.
- * \param action Type of bridge action frame.
- * \param data Frame payload data to pass.
- * \param datalen Frame payload data length to pass.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-static int ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen)
-{
-	struct ast_frame frame = {
-		.frametype = AST_FRAME_BRIDGE_ACTION,
-		.subclass.integer = action,
-		.datalen = datalen,
-		.data.ptr = (void *) data,
-	};
-
-	return ast_bridge_channel_queue_frame(bridge_channel, &frame);
-}
-
-int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen)
-{
-	struct ast_frame frame = {
-		.frametype = AST_FRAME_CONTROL,
-		.subclass.integer = control,
-		.datalen = datalen,
-		.data.ptr = (void *) data,
-	};
-
-	return ast_bridge_channel_queue_frame(bridge_channel, &frame);
+	ast_bridge_channel_unlock(bridge_channel);
+	return 0;
 }
 
 int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
@@ -279,60 +437,16 @@ int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_
 	return not_written;
 }
 
-void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel)
-{
-	/* Restore original formats of the channel as they came in */
-	if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) {
-		ast_debug(1, "Bridge is returning %p(%s) to read format %s\n",
-			bridge_channel, ast_channel_name(bridge_channel->chan),
-			ast_getformatname(&bridge_channel->read_format));
-		if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) {
-			ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n",
-				bridge_channel, ast_channel_name(bridge_channel->chan),
-				ast_getformatname(&bridge_channel->read_format));
-		}
-	}
-	if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) {
-		ast_debug(1, "Bridge is returning %p(%s) to write format %s\n",
-			bridge_channel, ast_channel_name(bridge_channel->chan),
-			ast_getformatname(&bridge_channel->write_format));
-		if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) {
-			ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n",
-				bridge_channel, ast_channel_name(bridge_channel->chan),
-				ast_getformatname(&bridge_channel->write_format));
-		}
-	}
-}
-
-static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
-{
-	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).
- *
- * 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.
- */
-	bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame);
-	ast_bridge_unlock(bridge_channel->bridge);
-
-	/*
-	 * Claim successful write to bridge.  If deferred frame
-	 * support is added, claim successfully deferred.
-	 */
-	return 0;
-}
-
-static int ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen)
+int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen)
 {
 	struct ast_frame frame = {
-		.frametype = AST_FRAME_BRIDGE_ACTION,
-		.subclass.integer = action,
+		.frametype = AST_FRAME_CONTROL,
+		.subclass.integer = control,
 		.datalen = datalen,
 		.data.ptr = (void *) data,
 	};
 
-	return bridge_channel_write_frame(bridge_channel, &frame);
+	return ast_bridge_channel_queue_frame(bridge_channel, &frame);
 }
 
 int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen)
@@ -373,6 +487,7 @@ 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 */
 static int run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args)
 {
 	int res = 0;
@@ -394,42 +509,6 @@ static int run_app_helper(struct ast_channel *chan, const char *app_name, const
 	return res;
 }
 
-/*!
-* \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)
-{
-	struct ast_bridge_features *features = bridge_channel->features;
-	struct ast_bridge_hook *hook;
-	struct ao2_iterator iter;
-
-	/* Run any hangup hooks. */
-	iter = ao2_iterator_init(features->other_hooks, 0);
-	for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
-		int remove_me;
-
-		if (hook->type != AST_BRIDGE_HOOK_TYPE_HANGUP) {
-			continue;
-		}
-		remove_me = hook->callback(bridge_channel->bridge, 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));
-			ao2_unlink(features->other_hooks, hook);
-		}
-	}
-	ao2_iterator_destroy(&iter);
-
-	/* Default hangup action. */
-	ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
-}
-
 void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
 {
 	if (moh_class) {
@@ -444,7 +523,6 @@ void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const
 	}
 }
 
-
 struct bridge_run_app {
 	/*! Offset into app_name[] where the MOH class name starts.  (zero if no MOH) */
 	int moh_offset;
@@ -471,6 +549,10 @@ static void bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, st
 		data->moh_offset ? &data->app_name[data->moh_offset] : NULL);
 }
 
+/*!
+ * \internal
+ * \brief Marshal an application to be executed on a bridge_channel
+ */
 static int payload_helper_app(ast_bridge_channel_post_action_data post_it,
 	struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
 {
@@ -497,13 +579,13 @@ static int payload_helper_app(ast_bridge_channel_post_action_data post_it,
 
 int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
 {
-	return payload_helper_app(ast_bridge_channel_write_action_data,
+	return payload_helper_app(bridge_channel_write_action_data,
 		bridge_channel, app_name, app_args, moh_class);
 }
 
 int ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
 {
-	return payload_helper_app(ast_bridge_channel_queue_action_data,
+	return payload_helper_app(bridge_channel_queue_action_data,
 		bridge_channel, app_name, app_args, moh_class);
 }
 
@@ -560,6 +642,10 @@ static void bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, s
 		payload->moh_offset ? &payload->playfile[payload->moh_offset] : NULL);
 }
 
+/*!
+ * \internal
+ * \brief Marshal a file to be played on a bridge_channel
+ */
 static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it,
 	struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
 {
@@ -582,13 +668,13 @@ static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it,
 
 int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
 {
-	return payload_helper_playfile(ast_bridge_channel_write_action_data,
+	return payload_helper_playfile(bridge_channel_write_action_data,
 		bridge_channel, custom_play, playfile, moh_class);
 }
 
 int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
 {
-	return payload_helper_playfile(ast_bridge_channel_queue_action_data,
+	return payload_helper_playfile(bridge_channel_queue_action_data,
 		bridge_channel, custom_play, playfile, moh_class);
 }
 
@@ -618,6 +704,10 @@ static void bridge_channel_do_callback(struct ast_bridge_channel *bridge_channel
 	data->callback(bridge_channel, data->payload_exists ? data->payload : NULL, data->payload_size);
 }
 
+/*!
+ * \internal
+ * \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)
 {
@@ -644,13 +734,13 @@ static int payload_helper_cb(ast_bridge_channel_post_action_data post_it,
 
 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)
 {
-	return payload_helper_cb(ast_bridge_channel_write_action_data,
+	return payload_helper_cb(bridge_channel_write_action_data,
 		bridge_channel, 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)
 {
-	return payload_helper_cb(ast_bridge_channel_queue_action_data,
+	return payload_helper_cb(bridge_channel_queue_action_data,
 		bridge_channel, callback, payload, payload_size);
 }
 
@@ -661,6 +751,10 @@ struct bridge_park {
 	char parkee_uuid[0];
 };
 
+/*!
+ * \internal
+ * \brief Park a bridge_cahnnel
+ */
 static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload)
 {
 	ast_bridge_channel_park(bridge_channel, payload->parkee_uuid,
@@ -668,6 +762,10 @@ static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struc
 		payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL);
 }
 
+/*!
+ * \internal
+ * \brief Marshal a park action onto a bridge_channel
+ */
 static int payload_helper_park(ast_bridge_channel_post_action_data post_it,
 	struct ast_bridge_channel *bridge_channel,
 	const char *parkee_uuid,
@@ -694,50 +792,10 @@ static int payload_helper_park(ast_bridge_channel_post_action_data post_it,
 
 int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data)
 {
-	return payload_helper_park(ast_bridge_channel_write_action_data,
+	return payload_helper_park(bridge_channel_write_action_data,
 		bridge_channel, parkee_uuid, parker_uuid, app_data);
 }
 
-int ast_bridge_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking)
-{
-	struct ast_frame action = {
-		.frametype = AST_FRAME_BRIDGE_ACTION,
-		.subclass.integer = started_talking
-			? BRIDGE_CHANNEL_ACTION_TALKING_START : BRIDGE_CHANNEL_ACTION_TALKING_STOP,
-	};
-
-	return ast_bridge_channel_queue_frame(bridge_channel, &action);
-}
-
-struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_bridge *bridge = bridge_channel->bridge;
-	struct ast_bridge_channel *other = NULL;
-
-	if (bridge_channel->in_bridge && bridge->num_channels == 2) {
-		AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
-			if (other != bridge_channel) {
-				break;
-			}
-		}
-	}
-
-	return other;
-}
-
-struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan)
-{
-	struct ast_bridge_channel *bridge_channel;
-
-	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-		if (bridge_channel->chan == chan) {
-			break;
-		}
-	}
-
-	return bridge_channel;
-}
-
 /*!
  * \internal
  * \brief Suspend a channel from a bridge.
@@ -748,7 +806,7 @@ struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct
  *
  * \return Nothing
  */
-void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel)
+void bridge_channel_internal_suspend_nolock(struct ast_bridge_channel *bridge_channel)
 {
 	bridge_channel->suspended = 1;
 	if (bridge_channel->in_bridge) {
@@ -772,7 +830,7 @@ void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel)
 static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel)
 {
 	ast_bridge_channel_lock_bridge(bridge_channel);
-	bridge_channel_suspend_nolock(bridge_channel);
+	bridge_channel_internal_suspend_nolock(bridge_channel);
 	ast_bridge_unlock(bridge_channel->bridge);
 }
 
@@ -786,7 +844,7 @@ static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel)
  *
  * \return Nothing
  */
-void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel)
+void bridge_channel_internal_unsuspend_nolock(struct ast_bridge_channel *bridge_channel)
 {
 	bridge_channel->suspended = 0;
 	if (bridge_channel->in_bridge) {
@@ -815,7 +873,7 @@ void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel)
 static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel)
 {
 	ast_bridge_channel_lock_bridge(bridge_channel);
-	bridge_channel_unsuspend_nolock(bridge_channel);
+	bridge_channel_internal_unsuspend_nolock(bridge_channel);
 	ast_bridge_unlock(bridge_channel->bridge);
 }
 
@@ -913,14 +971,15 @@ static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_cha
 	}
 }
 
+/*! \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 ast_bridge_channel_write_action_data(bridge_channel,
+	return bridge_channel_write_action_data(bridge_channel,
 		BRIDGE_CHANNEL_ACTION_DTMF_STREAM, dtmf, strlen(dtmf) + 1);
 }
 
 /*!
- * \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.
  */
@@ -1017,6 +1076,7 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
 	}
 }
 
+/*! \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;
@@ -1051,13 +1111,13 @@ static void bridge_channel_dtmf_stream(struct ast_bridge_channel *bridge_channel
 	ast_dtmf_stream(bridge_channel->chan, NULL, dtmf, 0, 0);
 }
 
-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);
-}
+/*! \brief Data specifying where a blind transfer is going to */
+struct blind_transfer_data {
+	char exten[AST_MAX_EXTENSION];
+	char context[AST_MAX_CONTEXT];
+};
 
+/*! \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);
@@ -1094,15 +1154,25 @@ static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *da
 	ast_party_connected_line_free(&connected_target);
 }
 
-static void after_bridge_move_channel_fail(enum ast_after_bridge_cb_reason reason, void *data)
+/*! \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);
 
 	ast_log(LOG_WARNING, "Unable to complete transfer: %s\n",
-		ast_after_bridge_cb_reason_string(reason));
+		ast_bridge_after_cb_reason_string(reason));
 	ast_softhangup(chan_target, AST_SOFTHANGUP_DEV);
 }
 
+/*! \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);
+}
+
+/*! \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)
 {
@@ -1122,11 +1192,11 @@ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_c
 	ao2_ref(chan_bridged, +1);
 	ast_bridge_channel_unlock(bridge_channel);
 
-	if (ast_after_bridge_callback_set(chan_bridged, after_bridge_move_channel,
+	if (ast_bridge_set_after_callback(chan_bridged, after_bridge_move_channel,
 		after_bridge_move_channel_fail, ast_channel_ref(chan_target))) {
 		ast_softhangup(chan_target, AST_SOFTHANGUP_DEV);
 
-		/* Release the ref we tried to pass to ast_after_bridge_callback_set(). */
+		/* Release the ref we tried to pass to ast_bridge_set_after_callback(). */
 		ast_channel_unref(chan_target);
 	}
 	bridge_channel_handle_hangup(bridge_channel);
@@ -1214,7 +1284,7 @@ static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_chann
  *
  * \return Nothing
  */
-static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel)
+static void bridge_channel_dissolve_check(struct ast_bridge_channel *bridge_channel)
 {
 	struct ast_bridge *bridge = bridge_channel->bridge;
 
@@ -1230,7 +1300,7 @@ static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel)
 	}
 
 	switch (bridge_channel->state) {
-	case AST_BRIDGE_CHANNEL_STATE_END:
+	case BRIDGE_CHANNEL_STATE_END:
 		/* Do we need to dissolve the bridge because this channel hung up? */
 		if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP)
 			|| (bridge_channel->features->usable
@@ -1246,18 +1316,7 @@ static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel)
 /* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */
 }
 
-/*!
- * \internal
- * \brief Pull the bridge channel out of its current bridge.
- * \since 12.0.0
- *
- * \param bridge_channel Channel to pull.
- *
- * \note On entry, bridge_channel->bridge is already locked.
- *
- * \return Nothing
- */
-void bridge_channel_pull(struct ast_bridge_channel *bridge_channel)
+void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel)
 {
 	struct ast_bridge *bridge = bridge_channel->bridge;
 
@@ -1306,25 +1365,13 @@ void bridge_channel_pull(struct ast_bridge_channel *bridge_channel)
 		ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING);
 	}
 
-	bridge_dissolve_check(bridge_channel);
+	bridge_channel_dissolve_check(bridge_channel);
 
 	bridge->reconfigured = 1;
 	ast_bridge_publish_leave(bridge, bridge_channel->chan);
 }
 
-/*!
- * \internal
- * \brief Push the bridge channel into its specified bridge.
- * \since 12.0.0
- *
- * \param bridge_channel Channel to push.
- *
- * \note On entry, bridge_channel->bridge is already locked.
- *
- * \retval 0 on success.
- * \retval -1 on failure.  The channel did not get pushed.
- */
-int bridge_channel_push(struct ast_bridge_channel *bridge_channel)
+int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
 {
 	struct ast_bridge *bridge = bridge_channel->bridge;
 	struct ast_bridge_channel *swap;
@@ -1345,8 +1392,8 @@ int bridge_channel_push(struct ast_bridge_channel *bridge_channel)
 
 	/* Add channel to the bridge */
 	if (bridge->dissolved
-		|| bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT
-		|| (swap && swap->state != AST_BRIDGE_CHANNEL_STATE_WAIT)
+		|| bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT
+		|| (swap && swap->state != BRIDGE_CHANNEL_STATE_WAIT)
 		|| bridge->v_table->push(bridge, bridge_channel, swap)
 		|| ast_bridge_channel_establish_roles(bridge_channel)) {
 		ast_debug(1, "Bridge %s: pushing %p(%s) into bridge failed\n",
@@ -1373,8 +1420,8 @@ int bridge_channel_push(struct ast_bridge_channel *bridge_channel)
 
 	ast_bridge_publish_enter(bridge, bridge_channel->chan);
 	if (swap) {
-		ast_bridge_channel_leave_bridge(swap, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
-		bridge_channel_pull(swap);
+		ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+		bridge_channel_internal_pull(swap);
 	}
 
 	/* Clear any BLINDTRANSFER and ATTENDEDTRANSFER since the transfer has completed. */
@@ -1517,7 +1564,7 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe
 		break;
 	default:
 		/* Write the frame to the channel. */
-		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_SIMPLE;
+		bridge_channel->activity = BRIDGE_CHANNEL_THREAD_SIMPLE;
 		ast_write(bridge_channel->chan, fr);
 		break;
 	}
@@ -1661,7 +1708,7 @@ static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel)
 
 	/* Wait for data to either come from the channel or us to be signaled */
 	ast_bridge_channel_lock(bridge_channel);
-	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+	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 */
 		ast_debug(1, "Bridge %s: %p(%s) is going into a signal wait\n",
@@ -1687,10 +1734,10 @@ static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel)
 			ast_bridge_unlock(bridge_channel->bridge);
 		}
 		ast_bridge_channel_lock(bridge_channel);
-		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_FRAME;
+		bridge_channel->activity = BRIDGE_CHANNEL_THREAD_FRAME;
 		ast_bridge_channel_unlock(bridge_channel);
 		if (!bridge_channel->suspended
-			&& bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+			&& bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
 			if (chan) {
 				bridge_handle_trip(bridge_channel);
 			} else if (-1 < outfd) {
@@ -1700,7 +1747,7 @@ static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel)
 				bridge_channel_handle_interval(bridge_channel);
 			}
 		}
-		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_IDLE;
+		bridge_channel->activity = BRIDGE_CHANNEL_THREAD_IDLE;
 		return;
 	}
 	ast_bridge_channel_unlock(bridge_channel);
@@ -1747,7 +1794,7 @@ 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_join(struct ast_bridge_channel *bridge_channel)
+void bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
 {
 	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));
@@ -1777,12 +1824,12 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel)
 		bridge_channel->bridge->callid = ast_read_threadstorage_callid();
 	}
 
-	if (bridge_channel_push(bridge_channel)) {
-		ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+	if (bridge_channel_internal_push(bridge_channel)) {
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 	}
 	bridge_reconfigured(bridge_channel->bridge, 1);
 
-	if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+	if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
 		/*
 		 * Indicate a source change since this channel is entering the
 		 * bridge system only if the bridge technology is not MULTIMIX
@@ -1795,7 +1842,7 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel)
 
 		ast_bridge_unlock(bridge_channel->bridge);
 		bridge_channel_event_join_leave(bridge_channel, AST_BRIDGE_HOOK_TYPE_JOIN);
-		while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		while (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
 			/* Wait for something to do. */
 			bridge_channel_wait(bridge_channel);
 		}
@@ -1803,7 +1850,7 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel)
 		ast_bridge_channel_lock_bridge(bridge_channel);
 	}
 
-	bridge_channel_pull(bridge_channel);
+	bridge_channel_internal_pull(bridge_channel);
 	bridge_reconfigured(bridge_channel->bridge, 1);
 
 	ast_bridge_unlock(bridge_channel->bridge);
@@ -1836,48 +1883,7 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel)
 	ast_bridge_channel_restore_formats(bridge_channel);
 }
 
-void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum ast_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);
-}
-
-void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state)
-{
-/* BUGBUG need cause code for the bridge_channel leaving the bridge. */
-	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
-		return;
-	}
-
-	ast_debug(1, "Setting %p(%s) state from:%d to:%d\n",
-		bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state,
-		new_state);
-
-	/* Change the state on the bridge channel */
-	bridge_channel->state = new_state;
-
-	bridge_channel_poke(bridge_channel);
-}
-
-/*!
- * \internal
- * \brief Queue a blind transfer action on a transferee bridge channel
- *
- * This is only relevant for when a blind transfer is performed on a two-party
- * bridge. The transferee's bridge channel will have a blind transfer bridge
- * action queued onto it, resulting in the party being redirected to a new
- * destination
- *
- * \param transferee The channel to have the action queued on
- * \param exten The destination extension for the transferee
- * \param context The destination context for the transferee
- * \param hook Frame hook to attach to transferee
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int bridge_channel_queue_blind_transfer(struct ast_channel *transferee,
+int bridge_channel_internal_queue_blind_transfer(struct ast_channel *transferee,
 		const char *exten, const char *context,
 		transfer_channel_cb new_channel_cb, void *user_data)
 {
@@ -1899,11 +1905,11 @@ int bridge_channel_queue_blind_transfer(struct ast_channel *transferee,
 	ast_copy_string(blind_data.exten, exten, sizeof(blind_data.exten));
 	ast_copy_string(blind_data.context, context, sizeof(blind_data.context));
 
-	return ast_bridge_channel_queue_action_data(transferee_bridge_channel,
+	return bridge_channel_queue_action_data(transferee_bridge_channel,
 		BRIDGE_CHANNEL_ACTION_BLIND_TRANSFER, &blind_data, sizeof(blind_data));
 }
 
-int bridge_channel_queue_attended_transfer(struct ast_channel *transferee,
+int bridge_channel_internal_queue_attended_transfer(struct ast_channel *transferee,
 		struct ast_channel *unbridged_chan)
 {
 	RAII_VAR(struct ast_bridge_channel *, transferee_bridge_channel, NULL, ao2_cleanup);
@@ -1920,7 +1926,114 @@ int bridge_channel_queue_attended_transfer(struct ast_channel *transferee,
 	ast_copy_string(unbridged_chan_name, ast_channel_name(unbridged_chan),
 		sizeof(unbridged_chan_name));
 
-	return ast_bridge_channel_queue_action_data(transferee_bridge_channel,
+	return bridge_channel_queue_action_data(transferee_bridge_channel,
 		BRIDGE_CHANNEL_ACTION_ATTENDED_TRANSFER, unbridged_chan_name,
 		sizeof(unbridged_chan_name));
 }
+
+int bridge_channel_internal_allows_optimization(struct ast_bridge_channel *bridge_channel)
+{
+	return bridge_channel->in_bridge
+		&& AST_LIST_EMPTY(&bridge_channel->wr_queue);
+}
+
+/*!
+ * \internal
+ * \brief Close a pipe.
+ * \since 12.0.0
+ *
+ * \param my_pipe What to close.
+ *
+ * \return Nothing
+ */
+static void pipe_close(int *my_pipe)
+{
+	if (my_pipe[0] > -1) {
+		close(my_pipe[0]);
+		my_pipe[0] = -1;
+	}
+	if (my_pipe[1] > -1) {
+		close(my_pipe[1]);
+		my_pipe[1] = -1;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Initialize a pipe as non-blocking.
+ * \since 12.0.0
+ *
+ * \param my_pipe What to initialize.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int pipe_init_nonblock(int *my_pipe)
+{
+	int flags;
+
+	my_pipe[0] = -1;
+	my_pipe[1] = -1;
+	if (pipe(my_pipe)) {
+		ast_log(LOG_WARNING, "Can't create pipe! Try increasing max file descriptors with ulimit -n\n");
+		return -1;
+	}
+	flags = fcntl(my_pipe[0], F_GETFL);
+	if (fcntl(my_pipe[0], F_SETFL, flags | O_NONBLOCK) < 0) {
+		ast_log(LOG_WARNING, "Unable to set read pipe nonblocking! (%d: %s)\n",
+			errno, strerror(errno));
+		return -1;
+	}
+	flags = fcntl(my_pipe[1], F_GETFL);
+	if (fcntl(my_pipe[1], F_SETFL, flags | O_NONBLOCK) < 0) {
+		ast_log(LOG_WARNING, "Unable to set write pipe nonblocking! (%d: %s)\n",
+			errno, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+/* Destroy elements of the bridge channel structure and the bridge channel structure itself */
+static void bridge_channel_destroy(void *obj)
+{
+	struct ast_bridge_channel *bridge_channel = obj;
+	struct ast_frame *fr;
+
+	if (bridge_channel->callid) {
+		bridge_channel->callid = ast_callid_unref(bridge_channel->callid);
+	}
+
+	if (bridge_channel->bridge) {
+		ao2_ref(bridge_channel->bridge, -1);
+		bridge_channel->bridge = NULL;
+	}
+
+	/* Flush any unhandled wr_queue frames. */
+	while ((fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list))) {
+		ast_frfree(fr);
+	}
+	pipe_close(bridge_channel->alert_pipe);
+
+	ast_cond_destroy(&bridge_channel->cond);
+}
+
+struct ast_bridge_channel *bridge_channel_internal_alloc(struct ast_bridge *bridge)
+{
+	struct ast_bridge_channel *bridge_channel;
+
+	bridge_channel = ao2_alloc(sizeof(struct ast_bridge_channel), bridge_channel_destroy);
+	if (!bridge_channel) {
+		return NULL;
+	}
+	ast_cond_init(&bridge_channel->cond, NULL);
+	if (pipe_init_nonblock(bridge_channel->alert_pipe)) {
+		ao2_ref(bridge_channel, -1);
+		return NULL;
+	}
+	if (bridge) {
+		bridge_channel->bridge = bridge;
+		ao2_ref(bridge_channel->bridge, +1);
+	}
+
+	return bridge_channel;
+}
diff --git a/main/features.c b/main/features.c
index ddbd04b4687971c463deb475e9f05fdfb9e14a8c..d1b33d5b94005ad6c2c9404b667eb96ccb28d775 100644
--- a/main/features.c
+++ b/main/features.c
@@ -73,6 +73,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/test.h"
 #include "asterisk/bridging.h"
 #include "asterisk/bridging_basic.h"
+#include "asterisk/bridging_after.h"
 #include "asterisk/stasis.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/features_config.h"
@@ -839,15 +840,15 @@ static void *bridge_call_thread(void *data)
 	}
 
 	if (tobj->return_to_pbx) {
-		ast_after_bridge_set_goto(tobj->chan, ast_channel_context(tobj->chan),
+		ast_bridge_set_after_goto(tobj->chan, ast_channel_context(tobj->chan),
 			ast_channel_exten(tobj->chan), ast_channel_priority(tobj->chan));
-		ast_after_bridge_set_goto(tobj->peer, ast_channel_context(tobj->peer),
+		ast_bridge_set_after_goto(tobj->peer, ast_channel_context(tobj->peer),
 			ast_channel_exten(tobj->peer), ast_channel_priority(tobj->peer));
 	}
 
 	ast_bridge_call(tobj->chan, tobj->peer, &tobj->bconfig);
 
-	ast_after_bridge_goto_run(tobj->chan);
+	ast_bridge_run_after_goto(tobj->chan);
 
 	ast_free(tobj);
 
@@ -3544,7 +3545,7 @@ static void bridge_check_monitor(struct ast_channel *chan, struct ast_channel *p
  */
 static void bridge_failed_peer_goto(struct ast_channel *chan, struct ast_channel *peer)
 {
-	if (ast_after_bridge_goto_setup(peer)
+	if (ast_bridge_setup_after_goto(peer)
 		|| ast_pbx_start(peer)) {
 		ast_autoservice_chan_hangup_peer(chan, peer);
 	}
@@ -4399,7 +4400,7 @@ static int action_bridge(struct mansession *s, const struct message *m)
 		return 0;
 	}
 
-	ast_after_bridge_set_go_on(chana, chana_context, chana_exten, chana_priority, NULL);
+	ast_bridge_set_after_go_on(chana, chana_context, chana_exten, chana_priority, NULL);
 	if (ast_bridge_add_channel(bridge, chana, NULL, playtone & PLAYTONE_CHANNEL1, xfer_cfg_a ? xfer_cfg_a->xfersound : NULL)) {
 		snprintf(buf, sizeof(buf), "Unable to add Channel1 to bridge: %s", ast_channel_name(chana));
 		astman_send_error(s, m, buf);
@@ -4407,7 +4408,7 @@ static int action_bridge(struct mansession *s, const struct message *m)
 		return 0;
 	}
 
-	ast_after_bridge_set_go_on(chanb, chanb_context, chanb_exten, chanb_priority, NULL);
+	ast_bridge_set_after_go_on(chanb, chanb_context, chanb_exten, chanb_priority, NULL);
 	if (ast_bridge_add_channel(bridge, chanb, NULL, playtone & PLAYTONE_CHANNEL2, xfer_cfg_b ? xfer_cfg_b->xfersound : NULL)) {
 		snprintf(buf, sizeof(buf), "Unable to add Channel2 to bridge: %s", ast_channel_name(chanb));
 		astman_send_error(s, m, buf);
@@ -5046,7 +5047,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
 		extension = ast_strdupa(ast_channel_exten(chan));
 		priority = ast_channel_priority(chan);
 		ast_channel_unlock(chan);
-		ast_after_bridge_set_go_on(current_dest_chan, context, extension, priority,
+		ast_bridge_set_after_go_on(current_dest_chan, context, extension, priority,
 			opt_args[OPT_ARG_CALLEE_GO_ON]);
 	} else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) {
 		ast_channel_lock(current_dest_chan);
@@ -5054,7 +5055,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
 		extension = ast_strdupa(ast_channel_exten(current_dest_chan));
 		priority = ast_channel_priority(current_dest_chan);
 		ast_channel_unlock(current_dest_chan);
-		ast_after_bridge_set_goto(current_dest_chan, context, extension, priority);
+		ast_bridge_set_after_goto(current_dest_chan, context, extension, priority);
 	}
 
 	if (ast_bridge_features_init(&chan_features)) {
diff --git a/res/parking/parking_bridge_features.c b/res/parking/parking_bridge_features.c
index ced288caf04e49b19cca95a9cea6fc11c1ac6b59..15d4980d2b45cf02f72081d9608e7ebe083693eb 100644
--- a/res/parking/parking_bridge_features.c
+++ b/res/parking/parking_bridge_features.c
@@ -34,7 +34,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/pbx.h"
 #include "asterisk/bridging.h"
 #include "asterisk/bridging_internal.h"
-#include "asterisk/bridging_channel_internal.h"
 #include "asterisk/bridging_channel.h"
 #include "asterisk/bridging_features.h"
 #include "asterisk/features.h"
@@ -404,7 +403,7 @@ static int parking_duration_callback(struct ast_bridge *bridge, struct ast_bridg
 	user->resolution = PARK_TIMEOUT;
 	ao2_unlock(user);
 
-	ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 
 	/* Set parking timeout channel variables */
 	snprintf(parking_space, sizeof(parking_space), "%d", user->parking_space);
@@ -492,14 +491,14 @@ void say_parking_space(struct ast_bridge_channel *bridge_channel, const char *pa
 	if (sscanf(payload, "%u %u", &hangup_after, &numeric_value) != 2) {
 		/* If say_parking_space is called with a non-numeric string, we have a problem. */
 		ast_assert(0);
-		ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 		return;
 	}
 
 	ast_say_digits(bridge_channel->chan, numeric_value, "", ast_channel_language(bridge_channel->chan));
 
 	if (hangup_after) {
-		ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE);
 	}
 }