Skip Headers
Oracle® Communications Converged Application Server Developer's Guide
Release 5.1

Part Number E27707-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
PDF · Mobi · ePub

22 Using Announcements

This chapter describes how to implement announcement support as defined in IR.92 Supplementary Services using the Service Foundation Toolkit (SFT).

About Announcements

Announcements are service-related messages played to a recipient to inform them about the state of a call. Announcements can be provided using either audio or video content.

SFT supports the following approaches to playing announcements:

APIs for Announcement Support

Announcement support makes use of the following classes in the com.oracle.sft.api package. For more information on these interfaces and their usage, refer to the Converged Application Server API Reference.

Table 22-1 lists the methods defined by the Interaction interface to replace a participant in a communication with another participant. In the context of announcement support, the other participant you add to the communication is the media server.

Table 22-1 Methods Defined by the Interaction Interface

Method Description

addParticipant(Class<P> type, String name)

addParticipant(Class<P> type, String name, javax.media.mscontrol.join.Joinable j)

addParticipant(Participant p)

addParticipant(String name)

Adds a participant to the interaction.

removeParticipant(Participant p)

removeParticipant(String name)

Removes a participant from the interaction.

replaceParticipant(Participant replaced, Participant replacing

replaceParticipant(Participant replaced, Participant replacing, boolean purge)

Enhanced function for participant replacement. If purge is set to false, the two methods provides the same are functionality.

IMConversation, IMConference and QueryInteration do not support this function


Table 22-2 lists the methods defined by the Conversation interface.

Table 22-2 Methods Defined by the Conversation Interface

Method Description

getOtherParty()

Gets the other party of the specified participant in the Conversation.

getMediaPartner()

Returns the MediaPartner joined in the Conversation instance if such a MediaPartner exists.


Table 22-3 lists the methods defined by the Participant interface.

Table 22-3 Methods Defined by the Participant Interface

Method Description

getExtension(ConversationParticipantExtension.class)

Gets a ConversationParticipantExtension.


Table 22-4 lists the methods defined by the ConversationParticipantExtension interface

Table 22-4 Methods Defined by the ConversationParticipantExtension Interface

Method Description

deferMediaInfoExchange()

Decides if media information exchange of this participant involved need to be deferred. Only effective on the called party. When this method is invoke, the media information exchange between the called part and the calling party is deferred. A use case is when the called party needs to exchange media information with a third party, for example a media server.


MediaPartner

The MediaPartner interface extends the MediaParticpant interface, and allows you to add a Mediapartner, which represents a media server that will play an announcement. Table 22-5 lists the methods defined by the MediaPartner interface.

Table 22-5 Methods Defined by the MediaPartner Interface



attach(UserParticipant up)

Attaches with the specified UserParticipant, and acts as each other's partner.

detach()

Detach from the user partner and restore Communication to the original state prior to the attach operation.

getUserPartner()

Return the UserParticipant if the MediaPartner have already attached with a UserParticipant using attach(UserParticipant) method.

play(String... uris)

play(URI... uris)

Start playing the announcement from the specified URI.

record(String uri)

record(URI uri)

Record to the specified URI.

setExclusive(boolean exclusive)

Purges the party when it is replaced by this MediaPartner if exclusive is true. Useful in situations where the number of user participants is limited.

This method must be invoked before calling attach(...).

stop(MediaPartner.MediaOperation operation)

Stop the specified operation this MediaPartner is related to.


CommunicationEvent Enumeration Types

The following @CommunicationEvent enumeration types are used in the playing of announcements.

Table 22-6 CommunicationEvents for Announcements

Enumeration Description

FINISHING

Indicates that the Participant is requesting to finish (or end) an established Communication.

FORWARDING

Indicates that a call is being forwarded.

HELD

Indicates that a call is in a held state.

HOLDING

Indicates that a call is being held.

PICKUP

Indicates that the called party has picked-up the phone (answered the call).

MEDIA_INFO_EARLY_EXCHANGED

Indicates that end-to-end media information is to be exchanged before the called party answers the call (picks-up the phone). End-to-end refers to information being exchanged between the calling party (the caller) to the called party (the callee).

MEDIA_RESOURCE_RESERVED

Indicates that a media resource has been reserved. This event is triggered after a media exchange between the calling and called party when the media server finishes streaming content.

You can trigger this event by adding a MediaParticipant.

RESUMED

Indicates that a call is already resumed.

RESUMING

Indicates that a call is being resumed.


ParticipantEvent Enumeration Types

The following @ParticipantEvent enumeration types are used in the playing of announcements.

Table 22-7 ParticipantEvents for Announcements

Enumeration Description

INITIALIZATION

This is the first Participant event during the Participant's life-cycle. This event allows the CommunicationBean to set the Participant's attributes and properties, altering it's subsequent behaviors. Operations that lead to the state transition of the Communication or Participant are not permitted to use this event.

Although this event occurs prior to any other disposal on this participant, however it occurs later than the INITIALIZATION event used by the CommunicationEvent. A typical use case is to invoke deferMediaInfoExchange() on a called party during this event.

BEING_BANNED

Indicates that the Participant is to be rejected by the AS (call barring).


About the MediaPartner and UserPartner Interfaces

The MediaPartner allows the UserPartner to access media-related functions such as the playing or recording of announcements. A Conversation can have one MediaPartner in addition to the maximum two UserParticipant objects. Each MediaPartner interacts with one UserParticipant. MediaPartner.attach(UserParticipant) establishes a relationship between the MediaPartner and the UserParticipant. Before it attaches to the UserParticpant, the MediaPartner must first be added to the Conversation.

Assume that a MediaPartner has been added to a Conversation, and that User A represents a member of the Conversation. If the MediaPartner attaches to User A via MediaPartner.attach(UserA), it starts the process of reserving the media resource. During the set-up process there is an SDP exchange between User A and the media server that will play the announcement. If in addition to User A, the Conversation has another member identified as User B, then the MediaPartner replaces User B. If the MediaPartner is exclusive, then User B will be purged (using a BYE/CANCEL/REJECTED message) from the Conversation.

If User B is not purged, the called party (callee) or calling party (caller) is temporarily replaced by another participant. In the case of playing an announcement the other participant is represented by the MediaPartner. When the announcement is finished and the MediaPartner terminates, the participant that was temporarily replaced is restored as the callee or caller, and participates in any subsequent stages of the conversation.

MediaPartner.play(uri) invokes the play operation of the underlying JSR309 API. Once a media resource is reserved, SFT throws a corresponding event in which MediaPartner.play(uri) is invoked to play an announcement. Note that MediaPartner.play(url) must co-operate with MediaPartner.attach(UserParticipant).

When MediaPartner.detach() is invoked, the MediaPartner is removed from the Conversation and purged, the temporarily replaced participant is restored, and both UserPartner attached to the MediaPartner and the MediaPartner of User A are purged.

Callout Announcement

Callout announcements play an announcement to the calling party when initiating a call, but prior to the call being forwarded to the called party. For example, a Change of Service announcement that informs the caller that the phone number they are calling has been changed, recites the new phone number, and then forwards the call to the new number.

Callout announcement is defined in section of A.1.1 of TS 24.628.

Example 22-1 shows the example code for a Communication Bean that plays a call out announcement. The trigger to play the announcement is defined in the method using CommunicationEvent.Type.MEDIA_RESOURCE_RESERVED.

Example 22-1 Callout Announcement

@ServiceAttributes(mscontrolJndiName = "mscontrol/dlg309")
@CommunicationBean(type = Conversation.class)
public class CalloutAnnouncementBean {
 
  @Context CommunicationContext<Conversation, UserParticipant> ctx;
  @Context CommunicationSession session;

  String userSubsCalloutPrompt = "bob@example.com";
  String uriStr = "file://prompts/generic/en_US/num_dialed.wav";
  
  @CommunicationEvent(type = CommunicationEvent.Type.INITIALIZATION)
  public void handleInit() {
    Conversation conv = (Conversation)ctx.getCommunication();
    if(conv.getCaller().getName().equals(userSubsCalloutPrompt)) {
      UserParticipant caller = (UserParticipant)conv.getCaller();
      conv.addParticipant(MediaPartner.class, "theMP");
      MediaPartner mediaPartner = conv.getMediaPartner();
      mediaPartner.attach(caller);
    }

  @CommunicationEvent(type = CommunicationEvent.Type.MEDIA_RESOURCE_RESERVED)
  public void handleEarlyEstablished() {
    Conversation conv = (Conversation)ctx.getCommunication();
    conv.getMediaPartner().play(media_file_uri);
  }

 //Handle end of the announcement playback
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
  void handleMediaEnded(){
    Conversation conv = ctx.getCommunication();
    //Media partner quit the call, restore former call process.
    conv.getMediaPartner().detach();
  }

  @ParticipantEvent(type = ParticipantEvent.Type.INITIALIZATION)
  public void handlePartInit(){
    Participant currPart = ctx.getParticipant();
    Conversation conv = (Conversation)ctx.getCommunication();
    Participant callee = conv.getCallee();
    if (currPart.equals(callee)){
      if(conv.getCaller().getName().equals(userSubsCalloutPrompt)) {
        callee.getExtension(ConversationParticipantExtension.class).
                            deferMediaInfoExchange();
      }
    }
  }
}

Call Barring Announcement

SFT supports the following call barring announcements, which are defined in Section 4.2.4 of the 3GPP TS 24.628 specification:

Call Barring Announcement Using Error-Info

When rejecting the calling party, this scenario inserts an Error-Info header in the error response field (3xx, 4xx, 5xx, or 6xx). This header can also carry a media URI, allowing the calling party to play an announcement, or carry an indication about the call barring.

You can populate the Error-Info header with a response code using the EventReason.createReasonData(Reason reason) method. Each of the reason types is translated into a corresponding SIP response code by SFT. See the Converged Application Server API Reference for more information.

Example 22-2 illustrates how to initiate a call barring announcement using the Error-Info event when a call barring event is identified, but before invoking the UserParticipant.reject event.

Example 22-2 Call Barring Announcement by Error-Info

@ParticipantEvent(type = ParticipantEvent.Type.JOINING)
  void handleJoining() {
    Conversation conv = (Conversation) ctx.getCommunication();
    Participant currPart = ctx.getParticipant();
    if(conv.getCaller().equals(currPart)){
//Application initiates call barring.
      UserParticipant caller = (UserParticipant)conv.getCaller();
      String promptTone = "http://localhost/media/nopermission.wav";
      AnnouncementIndication ai = 
      ctx.getContextElement(AnnouncementIndication.class);
      ai.createErrorIndication(promptTone);
      caller.reject(Reason.DECLINE);
    }
  }

In Example 22-3 the ParticipantEvent.Type.BEING_BANNED event occurs after the UserParticipant.reject(Reason.DECLINE) method is invoked by the application, but prior to the call barring service being activated. This scenario also lets you play an announcement using the Error-Info header.

Example 22-3

@ParticipantEvent(type = ParticipantEvent.Type.JOINING)
  void handleJoining() {
    Conversation conv = (Conversation) ctx.getCommunication();
    Participant currPart = ctx.getParticipant();

//Application initiates call barring via the Reason.DECLINE reject event.
    if(conv.getCaller().equals(currPart)){
      UserParticipant caller = (UserParticipant)conv.getCaller();
      caller.reject(Reason.DECLINE);
    }
  }
  
  @ParticipantEvent(type = ParticipantEvent.Type.BEING_BANNED)
  void handleBarring() {
    String promptTone = "http://localhost/media/wav/nopermission.wav";
    AnnouncementIndication ai = 
    ctx.getContextElement(AnnouncementIndication.class);
    ai.createErrorIndication(promptTone);
  }

Call Barring Announcement Using Early Media

Early media is the ability to play an announcement prior to establishing a SIP session (before sending a 2xx response code). In conjunction with call barring, the early media announcement plays, and when it finishes playback the incoming call is barred

Audible announcements can be also provided when an Application Server is rejecting the establishment of a session (before sending a 2xx response code). In this scenario a caller sends an INVITE request that is received by the AS, which determines to reject the call. Before barring the call, the AS can provide an audible announcement to the caller, potentially indicating the reasons for the call rejection.

Example 22-4 shows how to initiate playback of the announcement prior to rejecting the call using the ParticipantEvent.Type.BEING_BANNED event.

  • During the ParticipantEvent.Type.JOINING method, the application invokes caller.reject(Reason.DECLINE), initiating call barring.

  • When the ParticipantEvent.Type.BEING_BANNED event is subsequently thrown, the MediaParticipant.getMediaPartner() method plays the announcement.

  • After the announcement plays, the CommunicationEvent.Type.MEDIAENDED event signifies that the media playback from the media server is over, releasing the media resource. Call barring can then be completed.

Example 22-4 Call Barring Announcement Using Early Media

@ParticipantEvent(type = ParticipantEvent.Type.JOINING)
  void handleJoining() {
    Conversation conv = (Conversation) ctx.getCommunication();
    Participant currPart = ctx.getParticipant();
    if(conv.getCaller().equals(currPart)){
      //Initiate call barring using Reason.DECLINE
      UserParticipant caller = (UserParticipant)conv.getCaller();
      caller.reject(Reason.DECLINE);
    }
  }
  
  @ParticipantEvent(type = ParticipantEvent.Type.BEING_BANNED)
  void handleBarring() {
    Conversation conv = (Conversation) ctx.getCommunication();
    UserParticipant caller = (UserParticipant)conv.getCaller();
    caller.getMediaPartner().play(uri);
  }
  
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
  void handleMediaEnded(){
    MediaParticipant mp = (MediaParticipant)ctx.getParticipant();
    //MediaParticipant is released from the communication
    mp.unjoin();
  }
//Call reject process can now resume.

Another possibility is to play the announcement upon determining that the calling party is to be barred, and then invoking the caller reject event after removing the media resource.

Example 22-5 invokes the getMediaPartner() method during the ParticipantEvent.Type.JOINING event. Once the announcement is finished playing, the caller.reject() method performs the call barring.

Example 22-5 Call Barring Announcement Using Early Media

@ParticipantEvent(type = ParticipantEvent.Type.JOINING)
  void handleJoining() {
    Conversation conv = (Conversation) ctx.getCommunication();
    Participant currPart = ctx.getParticipant();
    if(conv.getCaller().equals(currPart)){
      //Application determines to bar the caller...
      UserParticipant caller = (UserParticipant)conv.getCaller();
      caller.getMediaPartner().play(uri);
    }
  }
  
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
  void handleMediaEnded(){
    Conversation conv = ctx.getCommunication();
    //Release media resource.
    MediaParticipant mp = (MediaParticipant)ctx.getParticipant();
    conv.removeParticipant(mp);
    UserParticipant caller = (UserParticipant)conv.getCaller();
    caller.reject(Reason.DECLINE);
  }

Playing a Call Barring Announcement With Established Sessions

In this scenario the call is barred not by an error response, but with a BYE request in the Reason header. When the application determines the call is to be barred, a media session to play the announcement is established for the calling party. When the announcement finishes playing, the application releases the communication and includes an appropriate Reason header as the reject code in the BYE request.

Example 22-6 calls the EventReason.createReasonData(Reason reason) method, which inserts a BYE request in the Reason header. The communication is then terminated with a method call to Communication.end().

Note:

Unlike the early media scenario described earlier in this chapter, playing an announcement in an established session requires that you create an exclusive media partner for the caller to play the announcement.

Example 22-6 Playing an Announcement Before Rejecting the Call

@ParticipantEvent(type = ParticipantEvent.Type.JOINING)
  void handleJoining() {
    Conversation conv = (Conversation) ctx.getCommunication();
    Participant currPart = ctx.getParticipant();
    if(conv.getCaller().equals(currPart)){
      //Application determines to bar the caller.
      UserParticipant caller = (UserParticipant)conv.getCaller();
      //Create a media partner to play the announcement.
      caller.getMediaPartner(true).play(uri);
    }
  }
 
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
  void handleMediaEnded(){
    Conversation conv = ctx.getCommunication();
    //Insert a BYE request in the Reason header.
    EventReason er = ctx.getContextElement(EventReason.class);
    er.createReasonData(Reason.DECLINE);
    //End the call.
    conv.end();
  }

Example 22-7 shows how an application can terminate an established communication using caller.reject(Reason), and play an announcement. The application bars the calling party, and in the subsequent ParticipantEvent.Type.BEING_BANNED event, plays an announcement with an exclusive media partner. After the announcement plays, the communication ends.

Example 22-7 Terminating a Communication and playing an Announcement

@ParticipantEvent(type = ParticipantEvent.Type.JOINING)
void handleJoining() {
    Conversation conv = (Conversation) ctx.getCommunication();
    Participant currPart = ctx.getParticipant();
    if(conv.getCaller().equals(currPart)){
      //Application determines to bar the caller.
      UserParticipant caller = (UserParticipant)conv.getCaller();
      //The caller is rejected.
      caller.reject(Reason.DECLINE);
    }
  }
//The participant will be rejected by the AS (call barring).
@ParticipantEvent(type = ParticipantEvent.Type.BEING_BANNED)
void handleBarring() {
    Conversation conv = (Conversation) ctx.getCommunication();
    UserParticipant caller = (UserParticipant)conv.getCaller();
    //Create a media partner to play the announcement.
    caller.getMediaPartner(true).play(uri);
  }
//When the announcement ends, unjoin the MediaParticipant.player.
@CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
void handleMediaEnded(){
    MediaParticipant player = (MediaParticipant)ctx.getParticipant();
    player.unjoin();
  }

Playing a Colorful Ring Tone

Colorful Ring Tone (CRT) (defined in RFC 3959) allows an application to play a distinctive audio or video announcement to the called party to replace the default ring tone. SFT supports CRT by adding an Alert-Info header to the SIP INVITE sent to the callee. The Alert-Info header value is a media URI that the UE of the callee can play as an announcement. SFT adds the media URI to the Alert-Info header using the AnnouncementIndication.createDRIndication(uri) method.

To specify an announcement as a CRT, use the ParticipantEvent.Type.INITIALIZATION event to identify the callee. In this way the callee is subscribed to the CRT announcement.

Example 22-8 creates a method to handle the INITIALIZATION event, in which the callee (alice@example.com) will receive the URI identifying the audio file ringtone.wav to be played as an announcement indication.

Example 22-8

@ParticipantEvent(type= ParticipantEvent.Type.INITIALIZATION)
void handleInit() {
    Participant currPart = ctx.getParticipant();
    Conversation conv = (Conversation) ctx.getCommunication();
    UserParticipant callee = (UserParticipant)conv.getCallee();
    if(currPart.equals(callee)){
      //Alice subscribes to the CRT service, which plays the specifed ringtone.
      if(conv.getCaller().getName().equals("alice@example.com")){
        String url = "http://localhost/media/ringtone.wav";
        //Add the media URI to the Alert-Info header.
        AnnouncementIndication ai = 
        ctx.getContextElement(AnnouncementIndication.class);
        ai.createDRIndication(url);
      }
    }
  }

Playing Colorful Ring Back Tone

Colorful Ring Back Tone, (also referred to as Caller Ring Back Tone), allows an application to play a distinctive audio or video announcement to the calling party to replace the default ring tone. SFT supports the following methods to create a Colorful Ring Back Tone (CRBT):

The above approaches for creating a CRBT application are defined in Section 4.2.2 of the 3GPP TS 24.628 specification. CRBT is also described in RFC 5009.

Colorful Ring Back Tone by Alert-Info

In this scenario, an Alert-Info header containing a media URI is inserted into the SIP message in response to the 180 Ringing response code. The Alert-Info header value can be a media URI that the UE of the calling party plays as announcement, or a conventional indication by which the UE of the calling party determines what media to play. SFT adds the media URI to the Alert-Info header using the AnnouncementIndication.createDRIndication(uri) method.

To specify an announcement as a CRBT, use the ParticipantEvent.Type.JOINING event to identify the callee. In this way the callee is subscribed to the CRBT announcement.

Example 22-9 creates a method to handle the JOINING event, in which the callee (bob@example.com) receives the URI identifying the audio file ringtone.wav to be played as an announcement indication.

Example 22-9 CRBT Using a Media URI in the Alert-Info Header

@ParticipantEvent(type= ParticipantEvent.Type.JOINING)
  void handleJoining() {
    Conversation conv = (Conversation) ctx.getCommunication();
    Participant callee = conv.getCallee();
    Participant currPart = ctx.getParticipant();
    if(currPart.equals(callee)){
      //Bob subscribes to the CRBT service, which plays the specifed ringtone. 
      if(callee.getName().equals("bob@example.com")){
        String url = "http://localhost/media/ringtone.wav";
        AnnouncementIndication ai = 
        ctx.getContextElement(AnnouncementIndication.class);
        ai.createDRIndication(url);
      }
    }
  }

Colorful Ring Back Tone Without Early Media Exchange

To specify an announcement as a CRBT without early media exchange, use the ParticipantEvent.Type.JOINING event to identify the callee. In this way the callee is subscribed to the CRBT announcement.

Example 22-10 CRBT Without Early Media Exchange

@ServiceAttributes(mscontrolJndiName = "mscontrol/dlg309")
@CommunicationBean(type = Conversation.class)
public class ColorRingBackToneBean {
  @Context CommunicationContext<Conversation, UserParticipant> ctx;
  @Context CommunicationSession session;
  
  @ParticipantEvent(type = ParticipantEvent.Type.INITIALIZATION)
  public void handlePartInit(){
    Conversation conv = ctx.getCommunication();
    Participant currPart = ctx.getParticipant();
    Participant callee = conv.getCallee();
    //Bob subscribes to the CRBT service.
    if (callee.equals(currPart)&& callee.getName().equals("bob@example.com")) {
       callee.deferMediaInfoExchange();
    }
  }
  
  @ParticipantEvent(type = ParticipantEvent.Type.JOINING)
  public void handleJoining() {
    Conversation conv = (Conversation)ctx.getCommunication();
    Participant callee = conv.getCallee();
    Participant currPart = ctx.getParticipant();
    if (callee.equals(currPart)&& callee.getName().equals("bob@example.com")) {
      String uriStr = "file://prompts/generic/en_US/numDialed.wav";
      UserParticipant caller = (UserParticipant)conv.getCaller();
      caller.getMediaPartner().play(uriStr);
    }
  }
 
  //End announcement and release the media resource.
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
  void handleMediaEnded(){
    MediaParticipant mp = (MediaParticipant)ctx.getParticipant();
    mp.unjoin();
  }

CRBT After Early Media Exchange

To specify an announcement as a CRBT after early media exchange, use the ParticipantEvent.Type.MEDIA_INFO_EARLY_EXCHANGED event to identify the callee. In this way the callee is subscribed to the CRBT announcement.

Example 22-11 CRBT After Early Media Exchange

@ServiceAttributes(mscontrolJndiName = "mscontrol/dlg309")
@CommunicationBean(type = Conversation.class)
public class ColorRingBackToneBean02 {
  @Context  CommunicationContext<Conversation, UserParticipant> ctx;
  @Context  CommunicationSession session;
  
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIA_INFO_EARLY_EXCHANGED)
  public void handleMediaEarlyExchange() {
    Conversation conv = (Conversation)ctx.getCommunication();
    Participant callee = conv.getCallee();
    //Bob subscribes to the CRBT service.
    if(callee.getName().equals("bob@example.com")){
      String uriStr = "file://prompts/generic/en_US/numDialed.wav";
      UserParticipant caller = (UserParticipant)conv.getCaller();
      caller.getMediaPartner().play(uriStr);
    }
  }
 
  //End announcement and release the media resource.
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
  void handleMediaEnded(){
    MediaParticipant mp = (MediaParticipant)ctx.getParticipant();
    mp.unjoin();
  }
}

Playing a Call Rejection Announcement

Call rejection announcements play when a callee rejects a caller. SFT supports the following methods to create a call rejection announcement:

Call Rejection Using Error-Info

In this scenario, an Error-Info header containing a media URI is inserted into the SIP message in response to a 480 Temporarily Unavailable response code. The Error-Info header value can be a media URI that the UE of the calling party plays as announcement, or a conventional indication by which the UE of the calling party determines what media to play. SFT adds the media URI to the Error-Info header using the AnnouncementIndication.createDRIndication(uri) method.

To play an announcement in response to a call rejection, use the ParticipantEvent.Type.REJECTED event to identify the callee. If the EventReason interface returns NOTAVAILABLE, BUSY, or DECLINE reason types, then a call rejection announcement can be played in response.

Example 22-12 creates a method to handle the REJECTED event, in which the callee (bob@example.com) receives the URI identifying the audio file reject.wav to be played as a rejection announcement.

Example 22-12 Call Rejection Using Error-Info

@ParticipantEvent(type= ParticipantEvent.Type.REJECTED)
void handleRejected() {
    Conversation conv = (Conversation) ctx.getCommunication();
    EventReason er = ctx.getContextElement(EventReason.class);
    if (er!=null){
      ReasonData rd = er.getReasonData().get(0);
      if(rd.getReasonType()==Reason.NOTAVAILABLE||
      rd.getReasonType()==Reason.BUSY||
      rd.getReasonType()==Reason.DECLINE){
        UserParticipant caller = (UserParticipant)conv.getCaller();
        if(caller.getName().equals("bob@example.com")){
          String promptTone = "http://localhost/media/reject.wav";
          AnnouncementIndication ai = 
          ctx.getContextElement(AnnouncementIndication.class);
          ai.createErrorIndication(promptTone);
        }
      }
    }
  }

Call Rejection Announcements Using Early Media

To specify an announcement as a call rejection after early media exchange, use the ParticipantEvent.Type.REJECTED event to identify and reject the callee.

Example 22-13 Call Rejection Announcement Using Early Media

@ParticipantEvent(type= ParticipantEvent.Type.REJECTED)
  void handleRejected() {
    Conversation conv = (Conversation) ctx.getCommunication();
    EventReason er = ctx.getContextElement(EventReason.class);
    if (er!=null){
      ReasonData rd = er.getReasonData().get(0);
      if(rd.getReasonType()==Reason.NOT_AVAILABLE||
        rd.getReasonType()==Reason.BUSY||
        rd.getReasonType()==Reason.DECLINE){
        if(conv.getCaller().getName().equals("bob@example.com")){
          UserParticipant caller = (UserParticipant)conv.getCaller();
          conv.addParticipant(MediaPartner.class, "theMP");
          conv.getMediaPartner().attach(caller);
        }
      }
    }
  }
 
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIA_RESOURCE_RESERVED)
  public void handleEarlyEstablished() {
    Conversation conv = (Conversation)ctx.getCommunication();
    String uri = "file:///prompts/en_US/rejected.wav";
    conv.getMediaPartner().play(uri);
  }
  
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
  void handleMediaEnded(){
    MediaPartner player = ctx.getParticipant();
    player.detach();
  }

Call Forwarding Announcements

For all supported call forwarding modes, SFT supports announcements to the calling party using early media prior to forwarding the call. Call Forwarding announcements are referenced in RFC 5009.

Un-Conditional Call Forwarding Announcement

Unconditional forwarding routes all incoming calls to a second phone number specified by the user of the service. The second number can be a work phone, voice-mail account, or other end-point in the network where the user would like their incoming calls to be received. In contrast, conditional call forwarding occurs when the user's phone is out of the service area, on another call, or the phone is turned off, and the call is forwarded to a secondary number.

SFT supports Unconditional Call Forwarding announcements using the following event types:

  • ParticipantEvent.Type.JOINING (Caller side)

  • CommunicationEvent.Type.STARTED

  • CommunicationEvent.Type.INITIALIZATION

Example 22-14 shows how to implement a Direct Call Forwarding Announcement by initiating the announcement without first triggering the Call Forwarding event. In this example Call Forwarding is triggered using the CommunicationEvent.Type.MEDIAENDED event.

Example 22-14 Direct Call Forwarding Announcement Without A Call Forwarding Event

@CommunicationEvent(type = CommunicationEvent.Type.INITIALIZATION)
  public void handleInit() {
    Conversation conv = (Conversation)ctx.getCommunication();
    UserParticipant caller = (UserParticipant)conv.getCaller();
    // Bob subscribes to call forwarding service.
    // Opensp subscribes to call forwarding announcement service.
    if(caller.getName().equals("opensp@example.com") && 
    conv.getCallee().getName().equals("bob@example.com")) {
      conv.addParticipant(MediaPartner.class, "theMP");
      conv.getMediaPartner().attach(caller);
    }
  }
 
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIA_RESOURCE_RESERVED)
  public void handleMediaResourceReserved() {
    Conversation conv = (Conversation)ctx.getCommunication();
    String uri = "file://prompts/generic/en_US/num_changed.wav";
    conv.getMediaPartner().play(uri);
  }
  
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
  void handleMediaEnded(){
    Conversation conv = ctx.getCommunication();
    UserParticipant p = session.createParticipant(UserParticipant.class, 
    "amy@example.com");
    MediaParticipant mp = conv.getMediaPartner();
    conv.replaceParticipant(mp, p);
  }
  
  @ParticipantEvent(type = ParticipantEvent.Type.INITIALIZATION)
  public void handlePartInit(){
    Participant currPart = ctx.getParticipant();
    Conversation conv = (Conversation)ctx.getCommunication();
    Participant callee = conv.getCallee();
    if (currPart.equals(callee)){
      if(conv.getCaller().getName().equals("opensp@example.com")
      &&callee.getName().equals("amy@example.com"))
 callee.getExtension(ConversationParticipantExtension.class).deferMediaInfoExchange();
      }
    }
  }

Conditional Call Forwarding

Conditional call forwarding (also referred to as call diversion) routes all incoming calls to the specified phone number when a corresponding condition is met. Common call forwarding conditions include:

  • No answer after specified period of time

  • Unreachable due to no signal or phone powered off

  • Busy on another call

Example 22-15 shows how to initiate the call forwarding announcement in the CommunicationEvent.Type.FORWARDING event.

Example 22-15 Call Forwarding Announcement During CommunicationEvent.Type.FORWARDING

@ParticipantEvent(type= ParticipantEvent.Type.REJECTED)
  void handleIncomingResponse() {
    Conversation call = (Conversation) ctx.getCommunication();
    EventReason er = ctx.getContextElement(EventReason.class);
    if (er!=null){
      ReasonData rd = er.getReasonData().get(0);
      if(rd.getReasonType()==Reason.NOTAVAILABLE||
      rd.getReasonType()==Reason.BUSY||
      rd.getReasonType()==Reason.DECLINE){
        if(call.getCallee().getName().equals("bob@example.com")){
       Participant newCallee = session.createParticipant
       (UserParticipant.class, "amy@example.com");
       //Use not removeParticipant+addParticipant but 
       //replaceParticipant to assure both CF and announcement work fine.
        call.replaceParticipant(call.getCallee(), newCallee, true);
        }
      }
    }
  }
 
  @CommunicationEvent(type = CommunicationEvent.Type.FORWARDING)
  void handleForwarding(){
    Conversation conv = (Conversation) ctx.getCommunication();
    if(conv.getCaller().getName().equals("opensp@example.com")){
      UserParticipant caller = (UserParticipant)conv.getCaller();
      conv.addParticipant(MediaPartner.class, "theMP");
      conv.getMediaPartner().attach(caller);
    }
  }
 
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIA_RESOURCE_RESERVED)
  public void handleEarlyEstablished() {
    Conversation conv = (Conversation)ctx.getCommunication();
    String uri = "file:////opt/snowshore/prompts/generic/en_US/num_changed.wav";
    conv.getMediaPartner().play(uri);
  }
  
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
  void handleMediaEnded() {
    MediaPartner player = (MediaPartner)ctx.getParticipant();
    player.detach();
  }
 
  @ParticipantEvent(type = ParticipantEvent.Type.INITIALIZATION)
  public void handlePartInit(){
    Participant currPart = ctx.getParticipant();
    Conversation conv = (Conversation)ctx.getCommunication();
    Participant callee = conv.getCallee();
    if (currPart.equals(callee)){
      if(conv.getCaller().getName().equals("opensp@example.com")&&callee.getName().equals("amy@example.com")) {
        callee.getExtension(ConversationParticipantExtension.class).deferMediaInfoExchange();
      }
    }
  }

Call Waiting Announcement

Call waiting announcement plays an announcement to the calling parties in a communication waiting state. SFT supports call waiting announcement for all call waiting scenarios (including NDUB and UDUB). SFT provides two methods by which to implement communication waiting announcement:

When implementing either of these approaches, the announcement logic should be in the CommunicationEvent.Type.WAITING event.

Call Waiting Announcement Using ALERT-INFO

SFT's implementation of call waiting announcement using the Alert-Info header complies with RFC 3261, TS 24.628, TS 24.615, and the Alert-Info URNs for the Session Initiation Protocol (SIP).

Example 22-16 plays a Distinctive Ringing announcement (Alert-Info) to the calling party held in the call waiting state. The Alert-Info header is carried in the 180 response which forwards the calling party. The code example shown below is common for different call waiting scenarios.

Example 22-16

//Communication waiting logic appears pior to this.

@CommunicationEvent(type = CommunicationEvent.Type.WAITING)
void handleCallWaiting() {
     String uri = "http://localhost/myapp/media/wav/busy.wav";
     AnnouncementIndication ai = 
     ctx.getContextElement(AnnouncementIndication.class);
      ai.createDRIndication(uri);
    }

Call Waiting Announcement Using Early Media

Example 22-17 shows how to initiate playback of an announcement to a calling party in a call waiting state using early media. The code example shown below is common to different call waiting scenarios using early media to play announcements.

Example 22-17 Call Waiting Announcement Using Early Media

@ParticipantEvent(type = ParticipantEvent.Type.INITIALIZATION)
public void handlePartInit(){
    Conversation conv = (Conversation)ctx.getCommunication();
    Participant currPart = ctx.getParticipant();
    Participant callee = conv.getCallee();
    if (currPart.equals(callee)&& callee.getName().equals(userSubsCWPrompt)) {
      callee.getExtension(ConversationParticipantExtension.class)
      .deferMediaInfoExchange();
      }
    }
//Add the MediaPartner as a Participant to the call.
@CommunicationEvent(type = CommunicationEvent.Type.WAITING)
void handleCallWaiting() {
    Conversation conv = (Conversation) ctx.getCommunication();
    UserParticipant caller = (UserParticipant)conv.getCaller();
    conv.addParticipant(MediaPartner.class, "theMP");
    conv.getMediaPartner().attach(caller);
    }
//
@CommunicationEvent(type = CommunicationEvent.Type.MEDIA_RESOURCE_RESERVED)
public void handleEarlyEstablished() {
    Conversation conv = (Conversation)ctx.getCommunication();
    String uri = "file:////prompts/generic/en_US/circuit_busy.wav";
      conv.getMediaPartner().play(uri);
    }    
//This method is optional to this Call Waiting case.
@CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
void handleMediaEnded(){
      MediaPartner mp = (MediaPartner)ctx.getParticipant();
      mp.detach();
    }

Pickup Announcement

Pickup announcements play an announcement to the called party when they pickup the phone (answer the call). Pickup Announcement is defined in Section 4.2.6 of the 3GPP TS 24.628 specification.

SFT supports Pickup Announcement using a media session. When the called party (the callee) answers the phone, the CommunicationEvent.Type.PICKUP event is triggered; the SFT application must initiate pickup announcement during this event.

Example 22-18 illustrates the use of the CommunicationEvent.Type.PICKUP event to play an announcement to a callee when they answer the phone. In this example the callee—identified by the SIP URI bob@example.com—will be played an announcement when he answers the incoming call.

Example 22-18 Pickup Announcement

@ParticipantEvent(type = ParticipantEvent.Type.INITIALIZATION)
  public void handlePartInit(){
    Conversation conv = (Conversation)ctx.getCommunication();
    Participant currPart = ctx.getParticipant();
    Participant callee = conv.getCallee();
    if (currPart.equals(callee)&& callee.getName().equals("bob@example.com")) {
       callee.getExtension(ConversationParticipantExtension.class).deferMediaInfoExchange();
    }
  }
  
  @CommunicationEvent(type= CommunicationEvent.Type.PICKUP)
  void handlePickup() {
    UserParticipant pickupParty = ctx.getParticipant();
    //The bob@exmaple.com is subscribed to the pickup announcement.
    if(pickupParty.getName().equals("bob@example.com")){
      Conversation conv = ctx.getCommunication();
      conv.addParticipant(MediaPartner.class, "theMP");
      conv.getMediaPartner().attach(pickupParty);
    }
  }
 
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIA_RESOURCE_RESERVED)
  public void handleEarlyEstablished() {
    Conversation conv = ctx.getCommunication();
    String uri = "file:////opt/snowshore/prompts/generic/en_US/pickup_who.wav";
    conv.getMediaPartner().play(uri);
  }
 
  @CommunicationEvent(type = CommunicationEvent.Type.MEDIAENDED)
  void handleMediaEnded(){
    MediaPartner player = (MediaPartner)ctx.getParticipant();
    player.detach();
  }