diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c index a6d623e7a6..35ac69d066 100644 --- a/channels/chan_misdn.c +++ b/channels/chan_misdn.c @@ -8495,6 +8495,40 @@ static void release_chan_early(struct chan_list *ch) ast_mutex_unlock(&release_lock); } +/*! + * \internal + * \brief Copy the source connected line information to the destination for a transfer. + * \since 1.8 + * + * \param dest Destination connected line + * \param src Source connected line + * + * \return Nothing + */ +static void misdn_connected_line_copy_transfer(struct ast_party_connected_line *dest, struct ast_party_connected_line *src) +{ + struct ast_party_connected_line connected; + + connected = *src; + connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; + + /* Make sure empty strings will be erased. */ + if (!connected.id.name.str) { + connected.id.name.str = ""; + } + if (!connected.id.number.str) { + connected.id.number.str = ""; + } + if (!connected.id.subaddress.str) { + connected.id.subaddress.str = ""; + } + if (!connected.id.tag) { + connected.id.tag = ""; + } + + ast_party_connected_line_copy(dest, &connected); +} + /*! * \internal * \brief Attempt to transfer the active channel party to the held channel party. @@ -8508,7 +8542,10 @@ static void release_chan_early(struct chan_list *ch) static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list *held_ch) { int retval; - struct ast_channel *bridged; + struct ast_channel *target; + struct ast_channel *transferee; + struct ast_party_connected_line target_colp; + struct ast_party_connected_line transferee_colp; switch (active_ch->state) { case MISDN_PROCEEDING: @@ -8520,23 +8557,116 @@ static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list return -1; } - bridged = ast_bridged_channel(held_ch->ast); - if (bridged) { - ast_queue_control(held_ch->ast, AST_CONTROL_UNHOLD); - held_ch->hold.state = MISDN_HOLD_TRANSFER; + ast_channel_lock(held_ch->ast); + while (ast_channel_trylock(active_ch->ast)) { + CHANNEL_DEADLOCK_AVOIDANCE(held_ch->ast); + } - chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n", - held_ch->ast->name, active_ch->ast->name); - retval = ast_channel_masquerade(active_ch->ast, bridged); - } else { + transferee = ast_bridged_channel(held_ch->ast); + if (!transferee) { /* * Could not transfer. Held channel is not bridged anymore. * Held party probably got tired of waiting and hung up. */ - retval = -1; + ast_channel_unlock(held_ch->ast); + ast_channel_unlock(active_ch->ast); + return -1; } - return retval; + target = active_ch->ast; + chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n", + held_ch->ast->name, target->name); + + ast_party_connected_line_init(&target_colp); + misdn_connected_line_copy_transfer(&target_colp, &target->connected); + ast_party_connected_line_init(&transferee_colp); + misdn_connected_line_copy_transfer(&transferee_colp, &held_ch->ast->connected); + held_ch->hold.state = MISDN_HOLD_TRANSFER; + + /* + * Before starting a masquerade, all channel and pvt locks must + * be unlocked. Any recursive channel locks held before + * ast_channel_masquerade() invalidates deadlock avoidance. Any + * recursive channel locks held before ast_do_masquerade() + * invalidates channel container locking order. Since we are + * unlocking both the pvt and its owner channel it is possible + * for "target" and "transferee" to be destroyed by their pbx + * threads. To prevent this we must give "target" and + * "transferee" a reference before any unlocking takes place. + */ + ao2_ref(target, +1); + ao2_ref(transferee, +1); + ast_channel_unlock(held_ch->ast); + ast_channel_unlock(active_ch->ast); + + /* Release hold on the transferee channel. */ + ast_indicate(transferee, AST_CONTROL_UNHOLD); + + /* Setup transfer masquerade. */ + retval = ast_channel_masquerade(target, transferee); + if (retval) { + /* Masquerade setup failed. */ + ast_party_connected_line_free(&target_colp); + ast_party_connected_line_free(&transferee_colp); + ao2_ref(target, -1); + ao2_ref(transferee, -1); + return -1; + } + ao2_ref(transferee, -1); + + /* + * Make sure masquerade is complete. + * + * After the masquerade, the "target" channel pointer actually + * points to the new transferee channel and the bridged channel + * is still the intended target of the transfer. + * + * By manually completing the masquerade, we can send connected + * line updates where they need to go. + */ + ast_do_masquerade(target); + + /* Transfer COLP between target and transferee channels. */ + { + /* + * Since "target" may not actually be bridged to another + * channel, there is no way for us to queue a frame so that its + * connected line status will be updated. Instead, we use the + * somewhat hackish approach of using a special control frame + * type that instructs ast_read() to perform a specific action. + * In this case, the frame we queue tells ast_read() to call the + * connected line interception macro configured for "target". + */ + struct ast_control_read_action_payload *frame_payload; + int payload_size; + int frame_size; + unsigned char connected_line_data[1024]; + + payload_size = ast_connected_line_build_data(connected_line_data, + sizeof(connected_line_data), &target_colp, NULL); + if (payload_size != -1) { + frame_size = payload_size + sizeof(*frame_payload); + frame_payload = alloca(frame_size); + frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO; + frame_payload->payload_size = payload_size; + memcpy(frame_payload->payload, connected_line_data, payload_size); + ast_queue_control_data(target, AST_CONTROL_READ_ACTION, frame_payload, + frame_size); + } + /* + * In addition to queueing the read action frame so that the + * connected line info on "target" will be updated, we also + * are going to queue a plain old connected line update on + * "target" to update the target channel. + */ + ast_channel_queue_connected_line_update(target, &transferee_colp, NULL); + } + + ast_party_connected_line_free(&target_colp); + ast_party_connected_line_free(&transferee_colp); + + ao2_ref(target, -1); + return 0; } diff --git a/channels/sig_pri.c b/channels/sig_pri.c index bab7ae4d2a..d0cfb46ced 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -1882,6 +1882,84 @@ static void sig_pri_mcid_event(struct sig_pri_span *pri, const struct pri_subcmd } #endif /* defined(HAVE_PRI_MCID) */ +#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) +/*! + * \internal + * \brief Copy the source connected line information to the destination for a transfer. + * \since 1.8 + * + * \param dest Destination connected line + * \param src Source connected line + * + * \return Nothing + */ +static void sig_pri_connected_line_copy_transfer(struct ast_party_connected_line *dest, struct ast_party_connected_line *src) +{ + struct ast_party_connected_line connected; + + connected = *src; + connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; + + /* Make sure empty strings will be erased. */ + if (!connected.id.name.str) { + connected.id.name.str = ""; + } + if (!connected.id.number.str) { + connected.id.number.str = ""; + } + if (!connected.id.subaddress.str) { + connected.id.subaddress.str = ""; + } + if (!connected.id.tag) { + connected.id.tag = ""; + } + + ast_party_connected_line_copy(dest, &connected); +} +#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */ + +#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) +struct xfer_rsp_data { + struct sig_pri_span *pri; + /*! Call to send transfer success/fail response over. */ + q931_call *call; + /*! Invocation ID to use when sending a reply to the transfer request. */ + int invoke_id; +}; +#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */ + +#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) +/*! + * \internal + * \brief Send the transfer success/fail response message. + * \since 1.8 + * + * \param data Callback user data pointer + * \param is_successful TRUE if the transfer was successful. + * + * \return Nothing + */ +static void sig_pri_transfer_rsp(void *data, int is_successful) +{ + struct xfer_rsp_data *rsp = data; + + pri_transfer_rsp(rsp->pri->pri, rsp->call, rsp->invoke_id, is_successful); +} +#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */ + +#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) +/*! + * \brief Protocol callback to indicate if transfer will happen. + * \since 1.8 + * + * \param data Callback user data pointer + * \param is_successful TRUE if the transfer will happen. + * + * \return Nothing + */ +typedef void (*xfer_rsp_callback)(void *data, int is_successful); +#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */ + #if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) /*! * \internal @@ -1889,96 +1967,258 @@ static void sig_pri_mcid_event(struct sig_pri_span *pri, const struct pri_subcmd * \since 1.8 * * \param pri sig_pri PRI control structure. - * \param call_1 First call involved in the transfer. - * \param call_1_held TRUE if call_1 is on hold. - * \param call_2 Second call involved in the transfer. - * \param call_2_held TRUE if call_2 is on hold. + * \param call_1_pri First call involved in the transfer. (transferee; usually on hold) + * \param call_1_held TRUE if call_1_pri is on hold. + * \param call_2_pri Second call involved in the transfer. (target; usually active/ringing) + * \param call_2_held TRUE if call_2_pri is on hold. + * \param rsp_callback Protocol callback to indicate if transfer will happen. NULL if not used. + * \param data Callback user data pointer * * \note Assumes the pri->lock is already obtained. * * \retval 0 on success. * \retval -1 on error. */ -static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1, int call_1_held, q931_call *call_2, int call_2_held) +static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_pri, int call_1_held, q931_call *call_2_pri, int call_2_held, xfer_rsp_callback rsp_callback, void *data) { + struct attempt_xfer_call { + q931_call *pri; + struct ast_channel *ast; + int held; + int chanpos; + }; int retval; - int call_1_chanpos; - int call_2_chanpos; - struct ast_channel *call_1_ast; - struct ast_channel *call_2_ast; - struct ast_channel *bridged; + int transferee_is_held; + struct ast_channel *transferee; + struct attempt_xfer_call *call_1; + struct attempt_xfer_call *call_2; + struct attempt_xfer_call *target; + struct attempt_xfer_call *swap_call; + struct attempt_xfer_call c1; + struct attempt_xfer_call c2; + struct ast_party_connected_line target_colp; + struct ast_party_connected_line transferee_colp; - call_1_chanpos = pri_find_pri_call(pri, call_1); - call_2_chanpos = pri_find_pri_call(pri, call_2); - if (call_1_chanpos < 0 || call_2_chanpos < 0) { + c1.pri = call_1_pri; + c1.held = call_1_held; + call_1 = &c1; + + c2.pri = call_2_pri; + c2.held = call_2_held; + call_2 = &c2; + + call_1->chanpos = pri_find_pri_call(pri, call_1->pri); + call_2->chanpos = pri_find_pri_call(pri, call_2->pri); + if (call_1->chanpos < 0 || call_2->chanpos < 0) { + /* Calls not found in span control. */ + if (rsp_callback) { + /* Transfer failed. */ + rsp_callback(data, 0); + } return -1; } + /* Attempt to make transferee and target consistent. */ + if (!call_1->held && call_2->held) { + /* + * Swap call_1 and call_2 to make call_1 the transferee(held call) + * and call_2 the target(active call). + */ + swap_call = call_1; + call_1 = call_2; + call_2 = swap_call; + } + /* Deadlock avoidance is attempted. */ - sig_pri_lock_private(pri->pvts[call_1_chanpos]); - sig_pri_lock_owner(pri, call_1_chanpos); - sig_pri_lock_private(pri->pvts[call_2_chanpos]); - sig_pri_lock_owner(pri, call_2_chanpos); + sig_pri_lock_private(pri->pvts[call_1->chanpos]); + sig_pri_lock_owner(pri, call_1->chanpos); + sig_pri_lock_private(pri->pvts[call_2->chanpos]); + sig_pri_lock_owner(pri, call_2->chanpos); - call_1_ast = pri->pvts[call_1_chanpos]->owner; - call_2_ast = pri->pvts[call_2_chanpos]->owner; - if (!call_1_ast || !call_2_ast) { - if (call_1_ast) { - ast_channel_unlock(call_1_ast); + call_1->ast = pri->pvts[call_1->chanpos]->owner; + call_2->ast = pri->pvts[call_2->chanpos]->owner; + if (!call_1->ast || !call_2->ast) { + /* At least one owner is not present. */ + if (call_1->ast) { + ast_channel_unlock(call_1->ast); } - if (call_2_ast) { - ast_channel_unlock(call_2_ast); + if (call_2->ast) { + ast_channel_unlock(call_2->ast); + } + sig_pri_unlock_private(pri->pvts[call_1->chanpos]); + sig_pri_unlock_private(pri->pvts[call_2->chanpos]); + if (rsp_callback) { + /* Transfer failed. */ + rsp_callback(data, 0); } - sig_pri_unlock_private(pri->pvts[call_1_chanpos]); - sig_pri_unlock_private(pri->pvts[call_2_chanpos]); return -1; } - bridged = ast_bridged_channel(call_2_ast); - if (bridged) { - if (call_1_held) { - ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD); - } - if (call_2_held) { - ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD); + for (;;) { + transferee = ast_bridged_channel(call_1->ast); + if (transferee) { + break; } - ast_verb(3, "TRANSFERRING %s to %s\n", call_2_ast->name, call_1_ast->name); - retval = ast_channel_masquerade(call_1_ast, bridged); - } else { /* Try masquerading the other way. */ - bridged = ast_bridged_channel(call_1_ast); - if (bridged) { - if (call_1_held) { - ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD); - } - if (call_2_held) { - ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD); - } + swap_call = call_1; + call_1 = call_2; + call_2 = swap_call; - ast_verb(3, "TRANSFERRING %s to %s\n", call_1_ast->name, call_2_ast->name); - retval = ast_channel_masquerade(call_2_ast, bridged); - } else { - /* Could not transfer. */ - retval = -1; + transferee = ast_bridged_channel(call_1->ast); + if (transferee) { + break; } - } - if (bridged && retval) { - /* Restore HOLD on held calls because masquerade failed. */ - if (call_1_held) { - ast_queue_control(call_1_ast, AST_CONTROL_HOLD); - } - if (call_2_held) { - ast_queue_control(call_2_ast, AST_CONTROL_HOLD); + + /* Could not transfer. Neither call is bridged. */ + ast_channel_unlock(call_1->ast); + ast_channel_unlock(call_2->ast); + sig_pri_unlock_private(pri->pvts[call_1->chanpos]); + sig_pri_unlock_private(pri->pvts[call_2->chanpos]); + + if (rsp_callback) { + /* Transfer failed. */ + rsp_callback(data, 0); } + return -1; } - ast_channel_unlock(call_1_ast); - ast_channel_unlock(call_2_ast); - sig_pri_unlock_private(pri->pvts[call_1_chanpos]); - sig_pri_unlock_private(pri->pvts[call_2_chanpos]); + target = call_2; + ast_verb(3, "TRANSFERRING %s to %s\n", call_1->ast->name, target->ast->name); - return retval; + ast_party_connected_line_init(&target_colp); + sig_pri_connected_line_copy_transfer(&target_colp, &target->ast->connected); + ast_party_connected_line_init(&transferee_colp); + sig_pri_connected_line_copy_transfer(&transferee_colp, &call_1->ast->connected); + transferee_is_held = call_1->held; + + /* + * Setup transfer masquerade. + * + * Note: There is an extremely nasty deadlock avoidance issue + * with ast_channel_masquerade(). Deadlock may be possible if + * the channels involved are proxies (chan_agent channels) and + * it is called with locks. Unfortunately, there is no simple + * or even merely difficult way to guarantee deadlock avoidance + * and still be able to send an ECT success response without the + * possibility of the bridged channel hanging up on us. + */ + ast_mutex_unlock(&pri->lock); + retval = ast_channel_masquerade(target->ast, transferee); + if (retval) { + /* Masquerade setup failed. */ + ast_party_connected_line_free(&target_colp); + ast_party_connected_line_free(&transferee_colp); + + ast_mutex_lock(&pri->lock); + + ast_channel_unlock(call_1->ast); + ast_channel_unlock(call_2->ast); + sig_pri_unlock_private(pri->pvts[call_1->chanpos]); + sig_pri_unlock_private(pri->pvts[call_2->chanpos]); + + if (rsp_callback) { + /* Transfer failed. */ + rsp_callback(data, 0); + } + return -1; + } + + /* + * Release any hold on the transferee channel before allowing + * the masquerade to happen. + */ + if (transferee_is_held) { + ast_indicate(transferee, AST_CONTROL_UNHOLD); + } + ast_mutex_lock(&pri->lock); + + /* + * Before manually completing a masquerade, all channel and pvt + * locks must be unlocked. Any recursive channel locks held + * before ast_do_masquerade() invalidates channel container + * locking order. Since we are unlocking both the pvt and its + * owner channel it is possible for "target" to be destroyed + * in the pbx thread. To prevent this we must give "target" + * a reference before any unlocking takes place. + */ + ao2_ref(target->ast, +1); + ast_channel_unlock(call_1->ast); + ast_channel_unlock(call_2->ast); + sig_pri_unlock_private(pri->pvts[call_1->chanpos]); + sig_pri_unlock_private(pri->pvts[call_2->chanpos]); + + if (rsp_callback) { + /* + * Transfer successful. + * + * Must do the callback before releasing the pri->lock to ensure + * that the protocol message goes out before the call leg is + * disconnected. + */ + rsp_callback(data, 1); + } + + /* + * Make sure masquerade is complete. + * + * After the masquerade, the "target" channel pointer actually + * points to the new transferee channel and the bridged channel + * is still the intended target of the transfer. + * + * By manually completing the masquerade, we can send the unhold + * and connected line updates where they need to go. + */ + ast_mutex_unlock(&pri->lock); + ast_do_masquerade(target->ast); + + /* Release any hold on the target. */ + if (target->held) { + ast_queue_control(target->ast, AST_CONTROL_UNHOLD); + } + + /* Transfer COLP between target and transferee channels. */ + { + /* + * Since "target" may not actually be bridged to another + * channel, there is no way for us to queue a frame so that its + * connected line status will be updated. Instead, we use the + * somewhat hackish approach of using a special control frame + * type that instructs ast_read() to perform a specific action. + * In this case, the frame we queue tells ast_read() to call the + * connected line interception macro configured for "target". + */ + struct ast_control_read_action_payload *frame_payload; + int payload_size; + int frame_size; + unsigned char connected_line_data[1024]; + + payload_size = ast_connected_line_build_data(connected_line_data, + sizeof(connected_line_data), &target_colp, NULL); + if (payload_size != -1) { + frame_size = payload_size + sizeof(*frame_payload); + frame_payload = alloca(frame_size); + frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO; + frame_payload->payload_size = payload_size; + memcpy(frame_payload->payload, connected_line_data, payload_size); + ast_queue_control_data(target->ast, AST_CONTROL_READ_ACTION, frame_payload, + frame_size); + } + /* + * In addition to queueing the read action frame so that the + * connected line info on "target" will be updated, we also + * are going to queue a plain old connected line update on + * "target" to update the target channel. + */ + ast_channel_queue_connected_line_update(target->ast, &transferee_colp, NULL); + } + + ast_party_connected_line_free(&target_colp); + ast_party_connected_line_free(&transferee_colp); + + ao2_ref(target->ast, -1); + ast_mutex_lock(&pri->lock); + return 0; } #endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */ @@ -3604,6 +3844,9 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev int index; struct ast_channel *owner; struct ast_party_redirecting ast_redirecting; +#if defined(HAVE_PRI_TRANSFER) + struct xfer_rsp_data xfer_rsp; +#endif /* defined(HAVE_PRI_TRANSFER) */ if (!subcmds) { return; @@ -3822,11 +4065,13 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev } sig_pri_unlock_private(pri->pvts[chanpos]); - pri_transfer_rsp(pri->pri, call_rsp, subcmd->u.transfer.invoke_id, - sig_pri_attempt_transfer(pri, - subcmd->u.transfer.call_1, subcmd->u.transfer.is_call_1_held, - subcmd->u.transfer.call_2, subcmd->u.transfer.is_call_2_held) - ? 0 : 1); + xfer_rsp.pri = pri; + xfer_rsp.call = call_rsp; + xfer_rsp.invoke_id = subcmd->u.transfer.invoke_id; + sig_pri_attempt_transfer(pri, + subcmd->u.transfer.call_1, subcmd->u.transfer.is_call_1_held, + subcmd->u.transfer.call_2, subcmd->u.transfer.is_call_2_held, + sig_pri_transfer_rsp, &xfer_rsp); sig_pri_lock_private(pri->pvts[chanpos]); break; #endif /* defined(HAVE_PRI_TRANSFER) */ @@ -5355,8 +5600,8 @@ static void *pri_dchannel(void *vpri) && pri->hold_disconnect_transfer) { /* We are to transfer the call instead of simply hanging up. */ sig_pri_unlock_private(pri->pvts[chanpos]); - if (!sig_pri_attempt_transfer(pri, e->hangup.call_active, 0, - e->hangup.call_held, 1)) { + if (!sig_pri_attempt_transfer(pri, e->hangup.call_held, 1, + e->hangup.call_active, 0, NULL, NULL)) { break; } sig_pri_lock_private(pri->pvts[chanpos]); diff --git a/main/channel.c b/main/channel.c index 135b0068fb..daf5b7a8e2 100644 --- a/main/channel.c +++ b/main/channel.c @@ -2789,7 +2789,6 @@ int ast_raw_answer(struct ast_channel *chan, int cdr_answer) } ast_indicate(chan, -1); - chan->visible_indication = 0; return res; } @@ -4154,8 +4153,12 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con case AST_CONTROL_RINGING: case AST_CONTROL_RING: case AST_CONTROL_HOLD: - case AST_CONTROL_UNHOLD: + /* You can hear these */ return 1; + + case AST_CONTROL_UNHOLD: + /* This is a special case. You stop hearing this. */ + break; } return 0; @@ -4176,7 +4179,6 @@ int ast_indicate_data(struct ast_channel *chan, int _condition, /* Don't bother if the channel is about to go away, anyway. */ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) { - ast_channel_unlock(chan); res = -1; goto indicate_cleanup; } @@ -4195,7 +4197,6 @@ int ast_indicate_data(struct ast_channel *chan, int _condition, /* who knows what we will get back! the anticipation is killing me. */ if (!(awesome_frame = ast_framehook_list_read_event(chan->framehooks, &frame))) { - ast_channel_unlock(chan); res = 0; goto indicate_cleanup; } @@ -4236,6 +4237,14 @@ int ast_indicate_data(struct ast_channel *chan, int _condition, break; } + if (is_visible_indication(condition)) { + /* A new visible indication is requested. */ + chan->visible_indication = condition; + } else if (condition == AST_CONTROL_UNHOLD || _condition < 0) { + /* Visible indication is cleared/stopped. */ + chan->visible_indication = 0; + } + if (chan->tech->indicate) { /* See if the channel driver can handle this condition. */ res = chan->tech->indicate(chan, condition, data, datalen); @@ -4243,13 +4252,8 @@ int ast_indicate_data(struct ast_channel *chan, int _condition, res = -1; } - ast_channel_unlock(chan); - if (!res) { /* The channel driver successfully handled this indication */ - if (is_visible_indication(condition)) { - chan->visible_indication = condition; - } res = 0; goto indicate_cleanup; } @@ -4335,7 +4339,6 @@ int ast_indicate_data(struct ast_channel *chan, int _condition, ast_playtones_start(chan, 0, ts->data, 1); ts = ast_tone_zone_sound_unref(ts); res = 0; - chan->visible_indication = condition; } if (res) { @@ -4344,6 +4347,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition, } indicate_cleanup: + ast_channel_unlock(chan); if (awesome_frame) { ast_frfree(awesome_frame); } @@ -5948,6 +5952,7 @@ int ast_do_masquerade(struct ast_channel *original) int i; int res=0; int origstate; + int visible_indication; struct ast_frame *current; const struct ast_channel_tech *t; void *t_pvt; @@ -6019,10 +6024,21 @@ int ast_do_masquerade(struct ast_channel *original) ast_debug(4, "Actually Masquerading %s(%d) into the structure of %s(%d)\n", clonechan->name, clonechan->_state, original->name, original->_state); + /* + * Stop any visible indiction on the original channel so we can + * transfer it to the clonechan taking the original's place. + */ + visible_indication = original->visible_indication; + ast_indicate(original, -1); + chans[0] = clonechan; chans[1] = original; - ast_manager_event_multichan(EVENT_FLAG_CALL, "Masquerade", 2, chans, "Clone: %s\r\nCloneState: %s\r\nOriginal: %s\r\nOriginalState: %s\r\n", - clonechan->name, ast_state2str(clonechan->_state), original->name, ast_state2str(original->_state)); + ast_manager_event_multichan(EVENT_FLAG_CALL, "Masquerade", 2, chans, + "Clone: %s\r\n" + "CloneState: %s\r\n" + "Original: %s\r\n" + "OriginalState: %s\r\n", + clonechan->name, ast_state2str(clonechan->_state), original->name, ast_state2str(original->_state)); /* Having remembered the original read/write formats, we turn off any translation on either one */ @@ -6246,8 +6262,8 @@ int ast_do_masquerade(struct ast_channel *original) * of this channel, and the new channel private data needs to be made * aware of the current visible indication (RINGING, CONGESTION, etc.) */ - if (original->visible_indication) { - ast_indicate(original, original->visible_indication); + if (visible_indication) { + ast_indicate(original, visible_indication); } /* Now, at this point, the "clone" channel is totally F'd up. We mark it as