Merged revisions 164201 via svnmerge from
authorRussell Bryant <russell@russellbryant.com>
Mon, 15 Dec 2008 14:40:24 +0000 (14:40 +0000)
committerRussell Bryant <russell@russellbryant.com>
Mon, 15 Dec 2008 14:40:24 +0000 (14:40 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
r164201 | russell | 2008-12-15 08:31:37 -0600 (Mon, 15 Dec 2008) | 31 lines

Handle a case where a call can be bridged to a channel that is still ringing.

The issue that was reported was about a case where a RINGING channel got
redirected to an extension to pick up a call from parking.  Once the parked
call got taken out of parking, it heard silence until the other side answered.
Ideally, the caller that was parked would get a ringing indication.  This patch
fixes this case so that the caller receives ringback once it comes out of
parking until the other side answers.

The fixes are:

 - Make sure we remember that a channel was an outgoing channel when doing
   a masquerade.  This prevents an erroneous ast_answer() call on the channel,
   which causes a bogus 200 OK to be sent in the case of SIP.

 - Add some additional comments to explain related parts of code.

 - Update the handling of the ast_channel visible_indication field.  Storing
   values that are not stateful is pointless.  Control frames that are events
   or commands should be ignored.

 - When a bridge first starts, check to see if the peer channel needs to be
   given ringing indication because the calling side is still ringing.

 - Rework ast_indicate_data() a bit for the sake of readability.

(closes issue #13747)
Reported by: davidw
Tested by: russell
Review: http://reviewboard.digium.com/r/90/

........

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@164203 65c4cc65-6c06-0410-ace0-fbb531ad65f3

main/channel.c
main/features.c

index b11caf8..3753599 100644 (file)
@@ -2854,64 +2854,126 @@ int ast_indicate(struct ast_channel *chan, int condition)
        return ast_indicate_data(chan, condition, NULL, 0);
 }
 
-int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
+static int attribute_const is_visible_indication(enum ast_control_frame_type condition)
+{
+       /* Don't include a default case here so that we get compiler warnings
+        * when a new type is added. */
+
+       switch (condition) {
+       case AST_CONTROL_PROGRESS:
+       case AST_CONTROL_PROCEEDING:
+       case AST_CONTROL_VIDUPDATE:
+       case AST_CONTROL_SRCUPDATE:
+       case AST_CONTROL_RADIO_KEY:
+       case AST_CONTROL_RADIO_UNKEY:
+       case AST_CONTROL_OPTION:
+       case AST_CONTROL_WINK:
+       case AST_CONTROL_FLASH:
+       case AST_CONTROL_OFFHOOK:
+       case AST_CONTROL_TAKEOFFHOOK:
+       case AST_CONTROL_ANSWER:
+       case AST_CONTROL_HANGUP:
+       case AST_CONTROL_T38:
+               return 0;
+
+       case AST_CONTROL_CONGESTION:
+       case AST_CONTROL_BUSY:
+       case AST_CONTROL_RINGING:
+       case AST_CONTROL_RING:
+       case AST_CONTROL_HOLD:
+       case AST_CONTROL_UNHOLD:
+               return 1;
+       }
+
+       return 0;
+}
+
+int ast_indicate_data(struct ast_channel *chan, int _condition,
+               const void *data, size_t datalen)
 {
+       /* By using an enum, we'll get compiler warnings for values not handled 
+        * in switch statements. */
+       enum ast_control_frame_type condition = _condition;
+       const struct ind_tone_zone_sound *ts = NULL;
        int res = -1;
 
        ast_channel_lock(chan);
-       /* Stop if we're a zombie or need a soft hangup */
+
+       /* 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);
                return -1;
        }
-       if (chan->tech->indicate)
+
+       if (chan->tech->indicate) {
+               /* See if the channel driver can handle this condition. */
                res = chan->tech->indicate(chan, condition, data, datalen);
+       }
+
        ast_channel_unlock(chan);
-       if (!chan->tech->indicate || res) {
-               /*
-                * Device does not support (that) indication, lets fake
-                * it by doing our own tone generation. (PM2002)
-                */
-               if (condition < 0)
-                       ast_playtones_stop(chan);
-               else {
-                       const struct ind_tone_zone_sound *ts = NULL;
-                       switch (condition) {
-                       case AST_CONTROL_RINGING:
-                               ts = ast_get_indication_tone(chan->zone, "ring");
-                               break;
-                       case AST_CONTROL_BUSY:
-                               ts = ast_get_indication_tone(chan->zone, "busy");
-                               break;
-                       case AST_CONTROL_CONGESTION:
-                               ts = ast_get_indication_tone(chan->zone, "congestion");
-                               break;
-                       }
-                       if (ts && ts->data[0]) {
-                               ast_debug(1, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
-                               ast_playtones_start(chan,0,ts->data, 1);
-                               res = 0;
-                               chan->visible_indication = condition;
-                       } else if (condition == AST_CONTROL_PROGRESS) {
-                               /* ast_playtones_stop(chan); */
-                       } else if (condition == AST_CONTROL_PROCEEDING) {
-                               /* Do nothing, really */
-                       } else if (condition == AST_CONTROL_HOLD) {
-                               /* Do nothing.... */
-                       } else if (condition == AST_CONTROL_UNHOLD) {
-                               /* Do nothing.... */
-                       } else if (condition == AST_CONTROL_VIDUPDATE) {
-                               /* Do nothing.... */
-                       } else if (condition == AST_CONTROL_SRCUPDATE) {
-                               /* Do nothing... */
-                       } else {
-                               /* not handled */
-                               ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
-                               res = -1;
-                       }
+
+       if (chan->tech->indicate && !res) {
+               /* The channel driver successfully handled this indication */
+               if (is_visible_indication(condition)) {
+                       chan->visible_indication = condition;
                }
-       } else
+               return 0;
+       }
+
+       /* The channel driver does not support this indication, let's fake
+        * it by doing our own tone generation if applicable. */
+
+       if (condition < 0) {
+               /* Stop any tones that are playing */
+               ast_playtones_stop(chan);
+               return 0;
+       }
+
+       /* Handle conditions that we have tones for. */
+       switch (condition) {
+       case AST_CONTROL_RINGING:
+               ts = ast_get_indication_tone(chan->zone, "ring");
+               break;
+       case AST_CONTROL_BUSY:
+               ts = ast_get_indication_tone(chan->zone, "busy");
+               break;
+       case AST_CONTROL_CONGESTION:
+               ts = ast_get_indication_tone(chan->zone, "congestion");
+               break;
+       case AST_CONTROL_PROGRESS:
+       case AST_CONTROL_PROCEEDING:
+       case AST_CONTROL_VIDUPDATE:
+       case AST_CONTROL_SRCUPDATE:
+       case AST_CONTROL_RADIO_KEY:
+       case AST_CONTROL_RADIO_UNKEY:
+       case AST_CONTROL_OPTION:
+       case AST_CONTROL_WINK:
+       case AST_CONTROL_FLASH:
+       case AST_CONTROL_OFFHOOK:
+       case AST_CONTROL_TAKEOFFHOOK:
+       case AST_CONTROL_ANSWER:
+       case AST_CONTROL_HANGUP:
+       case AST_CONTROL_RING:
+       case AST_CONTROL_HOLD:
+       case AST_CONTROL_UNHOLD:
+       case AST_CONTROL_T38:
+               /* Nothing left to do for these. */
+               res = 0;
+               break;
+       }
+
+       if (ts && ts->data[0]) {
+               /* We have a tone to play, yay. */
+               ast_debug(1, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
+               ast_playtones_start(chan, 0, ts->data, 1);
+               res = 0;
                chan->visible_indication = condition;
+       }
+
+       if (res) {
+               /* not handled */
+               ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
+       }
 
        return res;
 }
@@ -4039,7 +4101,7 @@ int ast_do_masquerade(struct ast_channel *original)
        /* XXX What about blocking, softhangup, blocker, and lock and blockproc? XXX */
        /* Application and data remain the same */
        /* Clone exception  becomes real one, as with fdno */
-       ast_copy_flags(original, clonechan, AST_FLAG_EXCEPTION);
+       ast_copy_flags(original, clonechan, AST_FLAG_EXCEPTION | AST_FLAG_OUTGOING);
        original->fdno = clonechan->fdno;
        /* Schedule context remains the same */
        /* Stream stuff stays the same */
@@ -4085,10 +4147,18 @@ int ast_do_masquerade(struct ast_channel *original)
                ast_log(LOG_WARNING, "Channel type '%s' does not have a fixup routine (for %s)!  Bad things may happen.\n",
                        original->tech->type, original->name);
 
-       /* If an indication is currently playing maintain it on the channel that is taking the place of original */
-       if (original->visible_indication)
+       /* 
+        * If an indication is currently playing, maintain it on the channel 
+        * that is taking the place of original 
+        *
+        * This is needed because the masquerade is swapping out in the internals
+        * 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);
-
+       }
+       
        /* Now, at this point, the "clone" channel is totally F'd up.  We mark it as
           a zombie so nothing tries to touch it.  If it's already been marked as a
           zombie, then free it now (since it already is considered invalid). */
index f791340..184196b 100644 (file)
@@ -2220,8 +2220,16 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
        if (chan && peer) {
                pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
                pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
-       } else if (chan)
+       } else if (chan) {
                pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
+       }
+
+       /* This is an interesting case.  One example is if a ringing channel gets redirected to
+        * an extension that picks up a parked call.  This will make sure that the call taken
+        * out of parking gets told that the channel it just got bridged to is still ringing. */
+       if (chan->_state == AST_STATE_RINGING && peer->visible_indication != AST_CONTROL_RINGING) {
+               ast_indicate(peer, AST_CONTROL_RINGING);
+       }
 
        if (monitor_ok) {
                const char *monitor_exec;