Make DTMF attended transfer support feature-complete.

This greatly modifies the operation of DTMF attended transfers so that
the full range of options from features.conf applies.

In addition, a new option has been added that allows for a transferer
to switch between bridges during a transfer before completing the
transfer.

(closes issue ASTERISK-21543)
reported by Matt Jordan

Review: https://reviewboard.asterisk.org/r/2654



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@395151 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Mark Michelson
2013-07-23 15:28:11 +00:00
parent e148c6e867
commit bf22391b8d
14 changed files with 2896 additions and 497 deletions

View File

@@ -63,6 +63,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/core_local.h"
#include "asterisk/core_unreal.h"
#include "asterisk/features_config.h"
#include "asterisk/bridging_internal.h"
/*! All bridges container. */
static struct ao2_container *bridges;
@@ -77,7 +78,6 @@ static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
static void cleanup_video_mode(struct ast_bridge *bridge);
static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
static void bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags remove_flags);
/*! Default DTMF keys for built in features */
static char builtin_features_dtmf[AST_BRIDGE_BUILTIN_END][MAXIMUM_DTMF_FEATURE_STRING];
@@ -463,19 +463,7 @@ void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channe
}
}
/*!
* \internal
* \brief Helper function to find a bridge channel given a channel.
*
* \param bridge What to search
* \param chan What to search for.
*
* \note On entry, bridge is already locked.
*
* \retval bridge_channel if channel is in the bridge.
* \retval NULL if not in bridge.
*/
static struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan)
struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan)
{
struct ast_bridge_channel *bridge_channel;
@@ -757,7 +745,7 @@ static int bridge_channel_push(struct ast_bridge_channel *bridge_channel)
|| ast_bridge_channel_establish_roles(bridge_channel)) {
ast_debug(1, "Bridge %s: pushing %p(%s) into bridge failed\n",
bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
return -1;
}
bridge_channel->in_bridge = 1;
@@ -1719,7 +1707,7 @@ static int bridge_base_push(struct ast_bridge *self, struct ast_bridge_channel *
*/
static void bridge_base_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
{
bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
}
/*!
@@ -4164,24 +4152,7 @@ static void bridge_channel_change_bridge(struct ast_bridge_channel *bridge_chann
ao2_ref(old_bridge, -1);
}
/*!
* \internal
* \brief Do the merge of two bridges.
* \since 12.0.0
*
* \param dst_bridge Destination bridge of merge.
* \param src_bridge Source bridge of merge.
* \param kick_me Array of channels to kick from the bridges.
* \param num_kick Number of channels in the kick_me array.
*
* \return Nothing
*
* \note The two bridges are assumed already locked.
*
* This moves the channels in src_bridge into the bridge pointed
* to by dst_bridge.
*/
static void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick,
void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick,
unsigned int optimized)
{
struct ast_bridge_channel *bridge_channel;
@@ -4440,21 +4411,7 @@ int ast_bridge_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg
return res;
}
/*!
* \internal
* \brief Move a bridge channel from one bridge to another.
* \since 12.0.0
*
* \param dst_bridge Destination bridge of bridge channel move.
* \param bridge_channel Channel moving from one bridge to another.
* \param attempt_recovery TRUE if failure attempts to push channel back into original bridge.
*
* \note The dst_bridge and bridge_channel->bridge are assumed already locked.
*
* \retval 0 on success.
* \retval -1 on failure.
*/
static int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery,
int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery,
unsigned int optimized)
{
struct ast_bridge *orig_bridge;
@@ -5646,17 +5603,7 @@ static void hooks_remove_heap(struct ast_heap *hooks, enum ast_bridge_hook_remov
ast_heap_unlock(hooks);
}
/*!
* \internal
* \brief Remove marked bridge channel feature hooks.
* \since 12.0.0
*
* \param features Bridge features structure
* \param remove_flags Determinator for whether hook is removed.
*
* \return Nothing
*/
static void bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags remove_flags)
void ast_bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags remove_flags)
{
hooks_remove_container(features->dtmf_hooks, remove_flags);
hooks_remove_container(features->hangup_hooks, remove_flags);

File diff suppressed because it is too large Load Diff

View File

@@ -374,6 +374,26 @@ int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *
return setup_bridge_role_option(role, option, value);
}
int ast_channel_has_role(struct ast_channel *channel, const char *role_name)
{
return get_role_from_channel(channel, role_name) ? 1 : 0;
}
const char *ast_channel_get_role_option(struct ast_channel *channel, const char *role_name, const char *option)
{
struct bridge_role *role;
struct bridge_role_option *role_option;
role = get_role_from_channel(channel, role_name);
if (!role) {
return NULL;
}
role_option = get_role_option(role, option);
return role_option ? role_option->value : NULL;
}
int ast_bridge_channel_has_role(struct ast_bridge_channel *bridge_channel, const char *role_name)
{
if (!bridge_channel->bridge_roles) {

View File

@@ -1433,9 +1433,10 @@ static void cel_attended_transfer_cb(
switch (xfer->dest_type) {
case AST_ATTENDED_TRANSFER_DEST_FAIL:
return;
/* handle these two the same */
/* handle these three the same */
case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE:
case AST_ATTENDED_TRANSFER_DEST_LINK:
case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
extra = ast_json_pack("{s: s, s: s, s: s}",
"bridge1_id", bridge1->uniqueid,
"channel2_name", channel2->name,

View File

@@ -3212,37 +3212,37 @@ static int setup_bridge_features_builtin(struct ast_bridge_features *features, s
if (!builtin_feature_get_exten(chan, "blindxfer", dtmf, sizeof(dtmf))
&& !ast_strlen_zero(dtmf)) {
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_BLINDTRANSFER, dtmf,
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
}
if (!builtin_feature_get_exten(chan, "atxfer", dtmf, sizeof(dtmf)) &&
!ast_strlen_zero(dtmf)) {
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, dtmf,
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
}
}
if (ast_test_flag(flags, AST_FEATURE_DISCONNECT) &&
!builtin_feature_get_exten(chan, "disconnect", dtmf, sizeof(dtmf)) &&
!ast_strlen_zero(dtmf)) {
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_HANGUP, dtmf,
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
}
if (ast_test_flag(flags, AST_FEATURE_PARKCALL) &&
!builtin_feature_get_exten(chan, "parkcall", dtmf, sizeof(dtmf)) &&
!ast_strlen_zero(dtmf)) {
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_PARKCALL, dtmf,
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
}
if (ast_test_flag(flags, AST_FEATURE_AUTOMON) &&
!builtin_feature_get_exten(chan, "automon", dtmf, sizeof(dtmf)) &&
!ast_strlen_zero(dtmf)) {
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_AUTOMON, dtmf,
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
}
if (ast_test_flag(flags, AST_FEATURE_AUTOMIXMON) &&
!builtin_feature_get_exten(chan, "automixmon", dtmf, sizeof(dtmf)) &&
!ast_strlen_zero(dtmf)) {
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_AUTOMIXMON, dtmf,
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
}
return res ? -1 : 0;

View File

@@ -106,6 +106,16 @@
the transferees, and the transfer target all being in a single bridge together.</para>
</description>
</configOption>
<configOption name="atxferswap" default="*4">
<synopsis>Digits to dial to toggle who the transferrer is currently bridged to during an attended transfer</synopsis>
<description>
<para>This option is only available to the transferrer during an attended
transfer operation. Pressing this DTMF sequence will result in the transferrer swapping
which party he is bridged with. For instance, if the transferrer is currently bridged with
the transfer target, then pressing this DTMF sequence will cause the transferrer to be
bridged with the transferees.</para>
</description>
</configOption>
<configOption name="pickupexten" default="*8">
<synopsis>Digits used for picking up ringing calls</synopsis>
<description>
@@ -355,6 +365,7 @@
#define DEFAULT_ATXFER_ABORT "*1"
#define DEFAULT_ATXFER_COMPLETE "*2"
#define DEFAULT_ATXFER_THREEWAY "*3"
#define DEFAULT_ATXFER_SWAP "*4"
/*! Default pickup options */
#define DEFAULT_PICKUPEXTEN "*8"
@@ -846,6 +857,8 @@ static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
ast_string_field_set(xfer, atxfercomplete, value);
} else if (!strcasecmp(name, "atxferthreeway")) {
ast_string_field_set(xfer, atxferthreeway, value);
} else if (!strcasecmp(name, "atxferswap")) {
ast_string_field_set(xfer, atxferswap, value);
} else {
/* Unrecognized option */
res = -1;
@@ -879,6 +892,8 @@ static int xfer_get(struct ast_features_xfer_config *xfer, const char *field,
ast_copy_string(buf, xfer->atxfercomplete, len);
} else if (!strcasecmp(field, "atxferthreeway")) {
ast_copy_string(buf, xfer->atxferthreeway, len);
} else if (!strcasecmp(field, "atxferswap")) {
ast_copy_string(buf, xfer->atxferswap, len);
} else {
/* Unrecognized option */
res = -1;
@@ -1629,6 +1644,8 @@ static int load_config(void)
DEFAULT_ATXFER_COMPLETE, xfer_handler, 0);
aco_option_register_custom(&cfg_info, "atxferthreeway", ACO_EXACT, global_options,
DEFAULT_ATXFER_THREEWAY, xfer_handler, 0);
aco_option_register_custom(&cfg_info, "atxferswap", ACO_EXACT, global_options,
DEFAULT_ATXFER_SWAP, xfer_handler, 0);
aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
DEFAULT_PICKUPEXTEN, pickup_handler, 0);

View File

@@ -221,12 +221,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name="Bridge"><para>The transfer was accomplished by merging two bridges into one.</para></enum>
<enum name="App"><para>The transfer was accomplished by having a channel or bridge run a dialplan application.</para></enum>
<enum name="Link"><para>The transfer was accomplished by linking two bridges together using a local channel pair.</para></enum>
<enum name="Threeway"><para>The transfer was accomplished by placing all parties into a threeway call.</para></enum>
<enum name="Fail"><para>The transfer failed.</para></enum>
</enumlist>
</parameter>
<parameter name="DestBridgeUniqueid">
<para>Indicates the surviving bridge when bridges were merged to complete the transfer</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Bridge</literal></para></note>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Bridge</literal> or <literal>Threeway</literal></para></note>
</parameter>
<parameter name="DestApp">
<para>Indicates the application that is running when the transfer completes</para>
@@ -333,6 +334,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="LocalTwoUniqueid">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="DestTransfererChannel">
<para>The name of the surviving transferer channel when a transfer results in a threeway call</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Threeway</literal></para></note>
</parameter>
</syntax>
<description>
<para>The headers in this event attempt to describe all the major details of the attended transfer. The two transferer channels
@@ -835,6 +840,11 @@ static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_mes
ast_str_append(&variable_data, 0, "%s", ast_str_buffer(local1_state));
ast_str_append(&variable_data, 0, "%s", ast_str_buffer(local2_state));
break;
case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
ast_str_append(&variable_data, 0, "DestType: Threeway\r\n");
ast_str_append(&variable_data, 0, "DestBridgeUniqueid: %s\r\n", transfer_msg->dest.threeway.bridge_snapshot->uniqueid);
ast_str_append(&variable_data, 0, "DestTransfererChannel: %s\r\n", transfer_msg->dest.threeway.channel_snapshot->name);
break;
case AST_ATTENDED_TRANSFER_DEST_FAIL:
ast_str_append(&variable_data, 0, "DestType: Fail\r\n");
break;
@@ -941,6 +951,39 @@ void ast_bridge_publish_attended_transfer_bridge_merge(int is_external, enum ast
stasis_publish(ast_bridge_topic_all(), msg);
}
void ast_bridge_publish_attended_transfer_threeway(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
struct ast_bridge_channel_pair *final_pair)
{
RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
transfer_msg = attended_transfer_message_create(is_external, result, transferee, target);
if (!transfer_msg) {
return;
}
transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_THREEWAY;
if (final_pair->channel == transferee->channel) {
transfer_msg->dest.threeway.channel_snapshot = transfer_msg->to_transferee.channel_snapshot;
} else {
transfer_msg->dest.threeway.channel_snapshot = transfer_msg->to_transfer_target.channel_snapshot;
}
if (final_pair->bridge == transferee->bridge) {
transfer_msg->dest.threeway.bridge_snapshot = transfer_msg->to_transferee.bridge_snapshot;
} else {
transfer_msg->dest.threeway.bridge_snapshot = transfer_msg->to_transfer_target.bridge_snapshot;
}
msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
if (!msg) {
return;
}
stasis_publish(ast_bridge_topic_all(), msg);
}
void ast_bridge_publish_attended_transfer_app(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
const char *dest_app)