Oracle® Communications Converged Application Server Developer's Guide Release 5.1 Part Number E27707-01 |
|
|
PDF · Mobi · ePub |
A key benefit of the Service Foundation Toolkit (SFT) is support for GSM Association's (GSMA) IR.92 specifications for delivering Voice over LTE (VoLTE).
SFT provides enhanced APIs that you can use to quickly and easily implement applications for delivering IR.92-compliant supplementary services over VoLTE. The APIs provide support for supplementary services such as Call Forwarding, Incoming and Outgoing Call Barring, ID Presentation and Restriction, Multi-Party Conferencing, and Message Waiting Indication (MWI).
GSM Association's (GSMA) IR.92 specifications defines the IP Multimedia Subsystem (IMS) profile for delivering Voice over LTE (VoLTE). The GSMA VoLTE initiative has defined IMS as the common way to deliver voice and messaging services over mobile broadband all-IP networks.
In September 2010, GSMA published the IREG Permanent Reference Document IR.92, which outlines the specifications for migrating 2G and 3G mobile voice, video and messaging services to 4G mobile broadband networks, such as LTE.
This chapter describes the following call control services:
Call Forwarding (also referred to as Call Diversion) lets users of the service (the called party, or callee) forward incoming calls to another phone number (the third party). The third party may be a mobile telephone, voice-mail box, or other telephone number where the desired called party is situated.
With Call Forwarding activated:
Users can continue to make outgoing calls from their telephone while incoming calls are forwarded.
When the telephone number the user's calls are being forwarded to is busy, callers to the forwarded number will receive a busy signal.
Converged Application Server supports the following IR.92 defined Call Forwarding modes for VoLTE:
Communication Forwarding Unconditional 3GPP TS 24.604
Communication Forwarding on No Reply 3GPP TS 24.604
Communication Forwarding on not Logged in 3GPP TS 24.604
Communication Forwarding on Busy 3GPP TS 24.604
Communication Forwarding on not Reachable 3GPP TS 24.604
Call Forwarding applications forward calls by removing the callee (the person to whom the call is being made), and adding a new participant (the third party) in the calle's place. How the SFT handles the call signaling depends on what stage the conversation is at. For example, if the Call Forwarding application replaces the callee during the STARTED event, SFT changes the callee to the new participant. However, if the application replaces the callee during the JOINING event, SFT sends a CANCEL event to the original callee before sending a SIP INVITE request to the new participant.
Example 21-1 illustrates the removal of a the callee during the INITILIZATION event, who is then with the participant: alice@example.com.
Example 21-1 Replacing the Callee With A New Participant
... @CommunicationEvent(type = CommunicationEvent.Type.INITIALIZATION) void handleInit() { Conversation call = ctx.getCommunication(); call.removeParticipant(call.getCallee()); call.addParticipant("alice@example.com"); } ...
This example creates the handleInit()
method which is annotated with @CommunicationEvent
, indicating that a communication session is being initialized (as indicated by the Type.INITIALIZATION
constant). The method declares the instance variable call
, which it initializes using the Conversation
class. The Conversation
class represents a two-party call, and extends the Communication
object which represents the communication session. The call
variable retrieves the context of the communication via the @CommunicationContext
object (stored in the previously initialized ctx
variable) using the getCommunication()
method.
After instantiating call
, the code calls the variable's removeParticipant()
method, which in turn calls the getCallee()
method to retrieve the identity of the callee from the Conversation
class and remove them. Once the callee is removed from the conversation, the call
variable can call the addParticpant()
method (which the Conversation
class inherits from the Interaction
class) and adds the new participant (in this example, alice@example.com) to the communication.
You can access a user's call forwarding (also referred to a call diversion) history and use it to create a call forwarding policy. For example, you may want to limit the total number of call forwarding diversions by looking at the caller's history.
The SIP History-Info header (defined in RFC 4244) provides a way of capturing any redirection information that may have occurred on a particular call. The SIP History-Info header is generated when a SIP request is re-directed, and can appear in any SIP request not associated with a dialog. The SIP History-Info header is generated by a User Agent (UA) or proxy, and is passed from one entity to another through requests and responses.
Example 21-1 illustrates how to retrieve call history information using the HistoryInformation
class. HistoryInformation
encapsulates the call forwarding (call diversion) history. In this example:
HistoryInformation
accesses the call forwarding history. If the number of call diversions is greater than 50, the call is rejected.
HistoryElement
retrieves the call diversion history from the History-Info header, which is maintained as a TreeSet representing the chronological order of the call diversions (see Table 21-1 for more information). If the number of call diversions is greater than 10, the call is rejected.
If the call is rejected due to too many call diversions, the initial participant (the callee) is replaced with the participant: tom@example.com
Example 21-2 Accessing Call Forwarding History
@Context CommunicationContext<Conversation, UserParticipant, ?> ctx; HistoryInformation hi = ctx.getContextElement(HistoryInformation.class); TreeSet<HistoryElement> elements = hi.getElements(); int totalDiversions = elements.size(); if (totalDiversions > 50) { //Reject the caller if the number of diversions is greater than 50. Conversation call = ctx.getCommunication(); call.removeParticipant(call.getCaller()); } //Get the history via elements. TreeSet<HistoryElement> lastLegElements = (TreeSet) elements.tailSet(elements); if (lastLegElements.size() > 10) { //Reject the caller if the number of diversions is greater than 10. } //Forward the caller if their call is rejected too many times. Conversation call = ctx.getCommunication(); call.removeParticipant(call.getCallee()); call.addParticipant("tom@example.com");
Table 21-1 lists the methods defined by the HistoryInformation
interface to access a user's call forwarding history.
Table 21-1 Methods Defined by the HistoryElement interface
Method | Description |
---|---|
|
Returns the
|
Table 21-2 lists the methods defined by the HistoryElement
interface to access a user's call forwarding history. The HistoryElement
class represents an element of call diversion history, and provides all the information about previous call diversions. SFT injects an instance of this class each time a call is diverted.
Table 21-2 Methods Defined by the HistoryInformation Interface
Methods | Description |
---|---|
|
A History element can have an alternate reason, which is embedded in the message. Returns an instance of |
|
An index of the call history. The History is in the Augmented BNF (ABNF) format. ABNF is a plain-text (non-XML) representation that is similar to traditional BNF grammar. 1*DIGIT *(DOT 1*DIGIT). |
|
Get the index of this call diversion history element. |
|
Retrieves all parameters as a Set of name-value pairs. |
|
The reason associated with this call diversion history element. Returns an instance of |
|
The host involved in the call diversion. |
|
The specific user involved in the call diversion. |
You can discover the reason a callee rejects a call using the EventReason
class to retrieve information about reject events. EventReason
implements the getReasonData()
method, which returns call reject information stored in the SIP request's Reason header. You can implement Call Forwarding in response to call rejection, and forward the call to a voice mail box or other specified end point. See RFC 3326 to learn more about the Reason header field.
Example 21-3 shows how to discover call reject reasons using the EventReason
class.
Example 21-3 Discovering Call Reject Reasons
@ParticipantEvent(type = ParticipantEvent.Type.REJECTED) void handleReject() { EventReason er = ctx.getContextElement(EventReason.class); ReasonData data = er.getReasonData(); if (data.getReasonType() == Reason.BUSY) { //Followed by logic to forward the call if getREasonType() returns BUSY. } } ...
This example creates the method handleReject()
, which uses the @ParticipantEvent
annotation to initialize it using the type.REJECTED
constant. This method is called if a participant refuses to join the communication.
The er
reference variable points to the EventReason
class, and is assigned the context of the communication via the @CommunicationContext
object (which is stored in the ctx
class variable) using the getContextElement(EventReason.class)
method. The er
variable stores the context of the type.REJECTED
communication event.
The data
reference variable points to the ReasonData
class, and is assigned the call reject information from the SIP Reason header via the er
variable's to call the getReasonData()
method, which returns the reason for call rejection. If the returned reason code is BUSY, you can implement call forwarding.
Example 21-4 shows the code for the Java class CallForwardBean.
All calls to Bob are unconditionally forwarded to Amy.
Calls to Amy are forwarded to Carol when Amy's phone is busy.
Calls to Carol are forwarded to Tom when Carol is not available.
Additionally, this example implements the HistoryInformation
and HistoryElement
interfaces to retrieve the call forwarding history.
package com.oracle.sft.demo; import java.util.Iterator; import java.util.TreeSet; import com.oracle.sft.api.CommunicationContext; import com.oracle.sft.api.CommunicationSession; import com.oracle.sft.api.Context; import com.oracle.sft.api.Conversation; import com.oracle.sft.api.Reason; import com.oracle.sft.api.UserParticipant; import com.oracle.sft.api.bean.CommunicationBean; import com.oracle.sft.api.bean.CommunicationEvent; import com.oracle.sft.api.bean.ParticipantEvent; import com.oracle.sft.api.context.EventReason; import com.oracle.sft.api.context.HistoryElement; import com.oracle.sft.api.context.HistoryInformation; import com.oracle.sft.api.context.ReasonData; @CommunicationBean public class CallForwardBean { @Context CommunicationSession session; @Context CommunicationContext<Conversation,UserParticipant,?> ctx; //Forward all incoming calls for Bob to Amy. @CommunicationEvent(type = CommunicationEvent.Type.INITIALIZATION) void handleInit() { Conversation call = (Conversation) ctx.getCommunication(); if (call.getCallee().getName().equals("bob@example.com")) { call.removeParticipant(call.getCallee()); call.addParticipant("amy@example.com"); } } //Forward Amy's calls to Carol when Amy's phone is busy. //If Carol is not availalbe, forward calls to Tom @ParticipantEvent(type= ParticipantEvent.Type.REJECTED) void handleRejected() { Conversation call = (Conversation) ctx.getCommunication(); EventReason er = ctx.getContextElement(EventReason.class); HistoryInformation hi = null; hi = ctx.getContextElement(HistoryInformation.class); printHistoryInformation(hi); if (er!=null){ ReasonData rd = er.getReasonData(); if (rd.getReasonType()==Reason.BUSY){ if (call.getCallee().getName().equals("amy@example.com")) { call.removeParticipant(call.getCallee()); call.addParticipant("carol@example.com"); } } else if(rd.getReasonType()==Reason.NOT_FOUND){ if (call.getCallee().getName().equals("carol@example.com")) { call.removeParticipant(call.getCallee()); call.addParticipant("tom@example.com"); } } else if(rd.getReasonType()==Reason.NO_RESPONSE){ if (call.getCallee().getName().equals("carol@example.com")) { call.removeParticipant(call.getCallee()); call.addParticipant("tom@example.com"); } } } } }
Call Barring lets users bar (or restrict) certain or all types of calls to and from their phone. For example, a user can restrict outgoing calls, outgoing international calls, or incoming calls from undesirable callers.
Converged Application Server supports the following IR.92 defined Call Barring modes for VoLTE:
Barring of All Incoming Calls 3GPP TS 24.611
Barring of All Outgoing Calls 3GPP TS 24.611
Barring of Outgoing International Calls 3GPP TS 24.611
Barring of Outgoing International Calls—ex Home Country 3GPP TS 24.611
Barring of Incoming Calls When Roaming 3GPP TS 24.611
To implement international call barring, the Call Barring application must have access to the phone number of the participant. To obtain the phone number, use the PhoneNumber
class, which you can access via the UserParticipant
interface. Similarly, to decide the roaming status of the user, the Call Barring application must access the private identity information available in the message. The IdentityInformation
class provides this information. Note that IdentityInformation
only represents information contained in the SIP Request that causes the current event. The application can also access this information from other sources, such as the Registrar, to determine roaming status. Similarly, the profile of the user—such as country of origin—can be obtained by the application using other interfaces (for example, the Diameter interface).
Table 21-3 lists the methods defined in the PhoneNumber
interface to access a user's telephone number information.
Table 21-3 Methods Defined In The PhoneNumber Interface
Method | Description |
---|---|
|
Returns any phone-context present in the number as a string value. |
|
Returns the phone number as a string value. |
|
Returns if the number is a global number or local number. If the number is global, it returns |
Table 21-4 lists the methods defined in the IdentityInformation
interface to access the identity information available in the context of the Event. The identity information is stored in the P headers of the SIP message.
Table 21-4 Methods defined in the IdentityInformation interface
Method | Description |
---|---|
|
Represents P-Asserted-Id header value. |
|
Represents P-Served-User header value. |
|
Represents P-Visited-Network-Id header value. |
|
Represents P-AccessNetwork-Info header value. |
Example 21-5 illustrates the use of the PhoneNumber
and IdentityInformation
classes to bar international calls when roaming.
Example 21-5 Using The PhoneNumber And IdentityInformation Interfaces
@ Context CommunicationContext<Conversation,UserParticipant> ctx = ... Conversation call = ctx.getCommunication(); UserParticipant callee = (UserParticipant) ctx.getCallee(); UserParticipant caller = (UserParticipant) ctx.getCaller(); ... IdentityInformation ii = ctx.getContextElement(IdentityInformation.class); if (isRoaming(ii)) { caller.reject(Reason.DECLINE); } ... PhoneNumber ph = callee.getPhoneNumber(); if (isInternationalCall(ph, caller)) { caller.reject(Reason.DECLINE); } ...
Example 21-6 shows code for the Java class CallBarBean, which creates rules for barring incoming, outgoing, and roaming calls.
All outgoing calls made by alice@example.com are barred
All incoming calls to amy@example.com are barred
All incoming calls to tom@example.com are barred when Tom is roaming
Example 21-6 Call Barring For Outgoing, Incoming, And Roaming Calls
package com.oracle.sft.demo; import com.oracle.sft.api.CommunicationContext; import com.oracle.sft.api.Context; import com.oracle.sft.api.Conversation; import com.oracle.sft.api.Reason; import com.oracle.sft.api.UserParticipant; import com.oracle.sft.api.bean.CommunicationBean; import com.oracle.sft.api.bean.ParticipantEvent; import com.oracle.sft.api.context.IdentityInformation; @CommunicationBean public class CallBarBean { @Context CommunicationContext<Conversation,UserParticipant> ctx; @ParticipantEvent(type= ParticipantEvent.Type.JOINING, communicationType = Conversation.class) public void hanldeStatedEvent() { Conversation call = (Conversation) ctx.getCommunication(); UserParticipant callee = (UserParticipant) call.getCallee(); UserParticipant caller = (UserParticipant) call.getCaller(); if (caller.getName().equals("alice@example.com")) { caller.reject(Reason.DECLINE); } else if (callee.getName().equals("amy@example.com")) { caller.reject(Reason.DECLINE); } else if (callee.getName().equals("tom@example.com")) { IdentityInformation ii = ctx.getContextElement(IdentityInformation.class); if (isRoaming(ii)) { caller.reject(Reason.DECLINE); } } } boolean isRoaming(IdentityInformation pi) { boolean roaming = false; if (pi.getVisitedNetworkIdentity()!=null) roaming = true; return roaming; } }
Communication Hold (also referred to Call Hold) allows a user to suspend a communication session—the reception of media stream(s) from an established IP multimedia session—and resume the media stream(s) at a later time. Placing a Communication Hold on an ongoing session is achieved by sending a Session Description Protocol (SDP) offer where each of the communications (media streams) to be held are marked with the sendonly
attribute if they were previously bidirectional media streams. To resume the session, a new SDP offer is issued in which each of the held media streams is marked with the default sendrecv
attribute.
Communication Hold also allows an AS to play music or an announcement to the held party. This is achieved using an AS that acts as a third-party call controller (3PCC), and replaces the existing session of one of the users with a session originating from an application server that plays the announcement or music until the user's session is resumed. See the 3GPP TS 24.628 specification for more information on the playing of announcements during Communication Hold.
The 3GPP TS 24.610 specification requires that the AS of the User Equipment (UE) invoking a media stream whose SDP session attribute is recvonly
use a lower bandwidth. The SDP specifies a lower bandwidth by setting the bandwidth (the b=
line in the SDP) to a lower value. The b=
line contains two elements:
The bandwidth value expressed in kilo bits per second (kbps).
An alphanumeric modifier that indicates the communication session or media stream to which to apply the specified bandwidth value.
The modifiers whose bandwidth values are specified by SFT are:
AS—Application Specific Maximum, which specifies the total bandwidth for a single media stream from one source.
RS—RTCP bandwidth allocated to active data senders.
RR—RTCP bandwidth allocated to other participants (receivers) in the RTP session.
When the bandwidth setting is enabled, SFT sets the default value for the AS bandwidth to zero (b=AS:0
). The b=RR:
and b=RS:
parameters are set to a value of 800 kbps, which is high enough to allow the continuation of the RTCP flow: b=RR:800
and b=RS:800
Example 21-7 Bandwidth Line in the Session Description Protocol
v=0 o=alice 2890844526 2890842807 IN IP4 126.16.64.4 s=SDP Seminar c=IN IP4 224.2.17.12/127 t=2873397496 2873404696 m=audio 49170 RTP/AVP 0 b=AS:0 b=RR:800 b=RS:800
Note:
The 3GPP TS 24.610 specification recommends that the AS modify the bandwidth for media streams whose SDP session attribute isrecvonly
. Media streams whose SDP session attribute are inactive
, sendonly
, and sendrecv
are not affected.While the 3GPP TS 24.610 specification recommends these values to preserve network bandwidth when a communication is placed on hold, you may need to adjust the bandwidth to better suit the requirements of the Communication Hold application. You can specify a network bandwidth for use with Communication Hold using:
The enableChangeBandwidth
and sdpBandwidthAttributes
elements of the sft.xml deployment descriptor.
The @ServiceAttributes
annotation.
The conversation.setBandWidth()
method.
Example 21-8 specifies alternate bandwidth settings using the enableChangeBandwidth
and sdpBandwidthAttributes
elements of the sft.xml deployment descriptor.
Example 21-8 Specifying Bandwidth Using the SFT.XML Deployment Descriptor
<service-attributes> <enableChangeBandwidth>true</enableChangeBandwidth> <sdpBandwidthAttributes> <totalBandwidth>0</totalBandwidth> <bandwidthForActiveDataSenders>800</bandwidthForActiveDataSenders> <bandwidthForOtherParticipants>1600</bandwidthForOtherParticipants> </sdpBandwidthAttributes> </service-attributes>
The enableChangeBandwidth
element is set to true
, which enables the alternate bandwidth settings specified by the sdpBandwidthAttributes
element. In this example the bandwidthForActiveDataSenders
sub-element specifies a bandwidth of 800 for users sending active media streams, while the bandwidthForOtherParticipants
sub-element specifies a bandwidth of 1600 for all other participants.
Example 21-9 specifies alternate bandwidth settings using the @ServiceAttributes
annotation. Use this annotation in the CommunicationBean
Java class you create to implement the Communication Hold application.
Converged Application Server supports the following Identity Presentation and Restriction services:
Originating Identification Presentation (OIP): Enables the called party to receive a network generated and trusted identity of the calling user on the screen of the mobile device. The originating user may also present a custom identity to be seen at the called party. The user generated identity is usually screened by the network of the originating user.
Originating Identification Restriction (OIR): Invoked when the calling user does not want their identity to be shown to the called party. In such cases, the network of the originating user signals to the network of the called user, to withhold the identity of the calling user.
Terminating Identification Presentation (TIP): Enables the calling party to receive the identification information of the remote party from the network. This information is provided to the originating party once the IMS communication has been accepted between the endpoints. The information is delivered regardless of the capability of the handset to process such information at the originating end.
Terminating Identification Restriction (TIR): Provides the terminating party with an option to restrict the identity to be presented to the originating party of the IMS communication. Logically speaking, TIR is the opposite of TIP.
To support the Identity Presentation and Restriction services listed above:
UE and IMS core network must support the SIP procedures described in the 3GPP TS 24.607 specification. Service configuration, as described in Section 4.10 of 3GPP TS 24.607, is optional.
UE and IMS core network must support the SIP procedures in the 3GPP TS 24.608 specification. Service configuration, as described in section 4.9 of 3GPP TS 24.608, is optional.
Table 21-5 lists the interfaces used to implement the Identity Presentation and Restriction services.
Table 21-5 Identity and Presentation Interfaces
Interface | Description |
---|---|
|
Provides information about the privacy policy, and whether it is enabled or disabled. The default value is disabled. The following classes extend the PrivacyPolicy interface: PrivacyValuePolicy, AnonymizeFromHeaderPolicy, RemovePAIheaderPolicy, RemoveFromChangeTagPolicy and ObscureIdentificationPolicy. |
|
Provides information about privacy values. Enabling this policy modifies or adds the PrivacyValues field to the message's Privacy Header.
RFC 3323 states that, when a privacy service performs one of the functions corresponding to a privacy level listed in the Privacy header, it should remove the corresponding priv-value from the Privacy header. |
|
Represents a PrivacyPolicy that specifies whether or not to anonymize the From header prior to the message being sent. The default value is disabled. |
|
Represents a PrivacyPolicy that specifies whether or not to remove the P-Asserted-Identity header prior to the message being sent. The default value is disabled. |
|
Represents a PrivacyPolicy that specifies whether or not to remove the From-Change tag prior sending the message. The default value is disabled. |
|
Represents a PrivacyPolicy that specifies whether or not to obscure identification according to the privacy header. Disabling this policy does not obscure identification contained in the Privacy header prior to the message being sent. The default value is disabled. RFC 3323, RFC 3325, and RFC 4244 define specific behaviors for each privacy value. RFC 5379 provides guidelines that are specified in RFC 3323, and subsequently extended in RFC 3325 and RFC 4244. |
|
Encapsulates Privacy information. You can use PrivacyInformation methods to return information about the privacy policy. |
The privacy service role is instantiated by a network intermediary. Typically this function is performed by entities that act as SIP proxy servers. The privacy service is designed to provide privacy functions for SIP messages that cannot otherwise be provided by the UAs themselves. Table 21-6 lists the semantics of each priv-value
, and the RFC that defines them.
Table 21-6 Types of Privacy Service Behaviors
Privacy Type | Description |
---|---|
|
Request that privacy services provide a user-level privacy function. See RFC 3323, “A Privacy Mechanism for the Session Initiation Protocol (SIP)” for more information. |
|
Request that privacy services modify headers that cannot be set arbitrarily by the user. For example, the Contact and Via headers. See RFC 3323, “A Privacy Mechanism for the Session Initiation Protocol (SIP)” for more information. |
|
Request that privacy services provide privacy for session media. See RFC 3323, “A Privacy Mechanism for the Session Initiation Protocol (SIP)” for more information. |
|
Privacy services must not perform any privacy function. See RFC 3323, “A Privacy Mechanism for the Session Initiation Protocol (SIP)” for more information. |
|
Privacy service must perform the specified services or fail the request. See RFC 3323, “A Privacy Mechanism for the Session Initiation Protocol (SIP)” for more information. |
|
Privacy requested for Third-Party Asserted Identity. See RFC 3325, “Private Extensions to the Session Initiation Protocol (SIP) for Asserted Identity within Trusted Networks” for more information. |
|
Privacy requested for History-Info headers. See RFC 4244, “An Extension to the Session Initiation Protocol (SIP) for Request History Information” for more information. |
RFC 5379 describes privacy considerations and the recommended treatment of each SIP header that may reveal user-privacy information. Section 5, “Recommended Treatment of User-Privacy-Sensitive Information” of RFC 5379 describes how each header affects privacy, the desired treatment of the value by the user agent and privacy service, and other details needed to ensure privacy.Table 21-7 lists the recommended treatment for each priv-value
contained in the SIP header. See “Section 5" of RFC 5379 “Guidelines for Using the Privacy Mechanism for SIP” for more information.
Table 21-7 Treatment of User-Privacy Information in Target SIP Headers
Target SIP Headers | Where | User | Header | Session | ID | History |
---|---|---|---|---|---|---|
Call-ID |
R |
Anonymize |
||||
Call-Info |
Rr |
Delete |
Not added |
|||
Contact |
R |
Anonymize |
||||
From |
R |
Anonymize |
||||
History-Info |
Rr |
Delete |
Delete |
Delete |
||
In-Reply-To |
R |
Delete |
||||
Organization |
Rr |
Delete |
Not added |
|||
P-Asserted-Identity |
Rr |
Delete |
Delete |
|||
Record-Route |
Rr |
Anonymize |
||||
Referred-By |
R |
Anonymize |
||||
Reply-To |
Rr |
Delete |
||||
Server |
r |
Delete |
Not added |
|||
Subject |
R |
Delete |
||||
User-Agent |
R |
Delete |
||||
Via |
R |
Anonymize |
||||
Warning |
r |
Anonymize |
Example 21-10 illustrates how to enable user-level privacy by anonymizing the From header, and removing the Call-Info, In-Reply-To, Organization. Reply-To, Subject, User-Agent, P-Asserted-Identity, and Privacy headers from the Session Initiation Protocol. In this example:
PrivacyInformation
—the Java class that encapsulates Privacy information—is stored in the pI
variable.
The PrivacyInformation.getPolicy(
class
)
method retrieves the privacy policy information from the removePAIHeaderPolicy
, anonymizeFromHeaderPolicy
, obscuringIdentificationPolicy
, and privacyValuePolicy
objects.
To enable user-level privacy, the removePAIHeaderPolicy
, anonymizeFromHeaderPolicy
, and obscuringIdentificationPolicy
objects implement the enable()
method inherited from com.oracle.sft.api.context.PrivacyPolicy
.
Example 21-10 Providing User-Level Privacy
@CommunicationEvent(type = CommunicationEvent.Type.INITIALIZATION) public void handleInitEvent() { PrivacyInformation pI = ctx.getContextElement(PrivacyInformation.class); removePAIHeaderPolicy = pI.getPolicy(RemovePAIHeaderPolicy.class); anonymizeFromHeaderPolicy = pI.getPolicy(AnonymizeFromHeaderPolicy.class); obscuringIdentificationPolicy = pI.getPolicy(ObscureIdentificationPolicy.class); privacyValuePolicy = pI.getPolicy(PrivacyValuePolicy.class); removePAIHeaderPolicy.enable(); anonymizeFromHeaderPolicy.enable(); obscuringIdentificationPolicy.enable(); printPrivacyInformation(); }
The History-Info header (defined in RFC 4244) provides a way of capturing any redirection information that may have occurred during a particular call. Without the History-Info header the redirecting information would be lost. The History-Info header is generated when a SIP request is re-directed, and can appear in any SIP request not associated with a dialog. The History-Info header is generated by a User Agent or proxy and is passed from one entity to another through requests and responses.
Example 21-11 adds history to the privacy policy, and then modifies the contents of the PRIVACY header by calling the PrivacyValuePolicy.enable()
method. The PrivacyValuePolicy.enable()
method adds a Privacy header with PrivacyValues
to the message.
Communication Waiting (also referred to as Call Waiting) informs a user (or the user equipment) that limited resources are available for incoming calls. Typically this means that the callee is involved in a communication session with another caller, and is not able to answer the incoming call from the second caller. Communication Waiting provides a mechanism by which you can create an application to inform a user that there is a second incoming call. The user then has the choice of accepting, rejecting, or ignoring the waiting call. Converged Application Server supports the 3GPP TS 24.615 and the GSMA IR.92 specifications.
When using SFT to develop Communication Waiting services, Converged Application Server supports both network- and terminal-based Communication Waiting.
Network-based Communication Waiting occurs when the AS determines that one of the following conditions has occurred:
The SIP INVITE request fulfills the Network Determined User Busy (NDUB) condition for the callee.
The caller receives a SIP message 486 Busy Here (indicating that the callee is busy) with a 370 Warning in the SIP header field indicating that there is insufficient bandwidth for the call to complete.
To support network-based Communication Waiting, the AS performs the following functions in response to receiving an appropriate Communication Waiting condition:
Modifies the SIP INVITE request and forwards or re-sends it to User B.
Provides an announcement to User C upon receipt of a 180 Ringing response from User B.
Inserts an Alert-Info header field set to urn:alert:service:call-waiting
in the 180 Ringing response and forwards it to User C
Rejects the communication by sending a 486 Busy Here response to User C upon receipt of a 415 Unsupported Media Type response.
Terminal-based Communication Waiting occurs when the AS receives the SIP message 180 Ringing with the Alert-Info header URN Indication Values set to urn:alert:service:call-waiting
.
To support terminal-based Communication Waiting, the application server performs the following functions in response to receiving an appropriate Communication Waiting condition:
Sends an announcement to the calling user (the caller).
Sends a 180 Ringing response to the caller.
Initiates the Telephony Application Server-Communication Waiting (TAS-CW) timer. This optional timer specifies the amount of time the network will wait for a response from User B, in response to the communication from User C. The value of the timer is between 0.5 and 2 minutes.
If the TAS-CW timer expires, the AS sends a CANCEL request to User B with a Reason header field set to “SIP,” and the cause set to 408 Request Time-out, indicating that the user could not be found in the allotted time. A 480 Temporarily Unavailable response is sent to User C, including a Reason header field set to ISUP Cause Code 19, indicating that these was no answer from the callee.
Communication Waiting 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.
The following examples decompose the CallWaitingBean
Java class. See "CallWaitingBean Example Code" to view the code in its entirety.
Example 21-12 creates CallWaitingBean
, a Java class that responds to Communication Waiting events.
Example 21-12 Creating the CallWaitingBean Java Class
@CommunicationBean public class CallWaitingBean { @Context CommunicationContext<Conversation,UserParticipant> ctx; ...
The @CommunicationBean
annotation creates CallWaitingBean
. The @Context
annotation injects an instance of the CommunicationContext
object, which in turn calls instances of the Conversation
and UserParticipant
classes. These interfaces represent a two-party call and a participant within the communication, and are cast in the ctx
instance variable.
Creating a Network-based Communication Waiting Event
Example 21-13 illustrates the handleInit()
method, which listens for and responds to network-based communication waiting events.
Example 21-13 Network-based Communication Waiting
@CommunicationEvent(type = CommunicationEvent.Type.INITIALIZATION) void handleInit() { Conversation call = (Conversation) ctx.getCommunication(); if (call.getCaller().getName().contains("user1")) { call.indicateCallWaiting(); } } ...
The @CommunicationEvent
annotation instantiates this method with the Type.INITIALIZATION
constant. When an initialization event occurs, handleInit()
uses the Conversation
interface to retrieve the name of the communication.
The getCommunication()
method returns the CommunicationContext
object (via the ctx
instance variable) to the Conversation
interface via the call
variable. The call
variable—which now contains the state of the communication session— invoke the indicateCallWaiting()
method which will forward the invite with call waiting indication. The Communication Waiting event will be triggered when a 180 response arrives.
Creating a Terminal-based Communication Waiting Event
Example 21-14 illustrates the use of the hanldeRejectEvent()
method to reject events. This method uses the EventReason
interface to retrieve the cause of the Communication Waiting reject event, and then triggers Terminal-based Communication Waiting.
Example 21-14 Terminal-based Communication Waiting
... @ParticipantEvent(type = ParticipantEvent.Type.REJECTED) public void hanldeRejectEvent() { EventReason eventReason = ctx.getContextElement(EventReason.class); List<ReasonData> reasonData = eventReason.getReasonData(); ReasonData reasonData = reasonData.get(); Reason reason = reasonData.getReasonType(); List<WarningData> warningData = eventReason.getWarningData(); WarningData warningData = warningData.get(); if (reasonData.getReasonCode() == 486 && warningData.getWarningCode() == 370) { Conversation call = (Conversation) ctx.getCommunication(); call.indicateCallWaiting(); } }
The @ParticipantEvent
annotation—which specifies events pertaining to a participant within a given communication session—listens for an event type of REJECTED, indicating that the participant has refused to join the communication. The hanldeRejectEvent()
method calls the EventReason
interface to get the cause of the REJECTED event. List<WarningData>
returns the list of WarningData
for the event, and the getWarningCode()
method returns the warning codes.
If the status code of the response is 486 Busy Here and the warning code is 370 Insufficient Bandwidth, the getCommunication()
method returns the CommunicationContext
object (via the ctx
instance variable) to the Conversation
interface via the call
variable. The call
variable invoke the indicateCallWaiting()
method, which re-sends the INVITE. The Communication Waiting event will be triggered when 180 response arrives.
Handling the Communication Waiting Event
Example 21-15 creates the method handleCallWaiting()
, which uses the @CommunicationEvent
annotation's type.WAITING
constant to indicate that a call is waiting. The application receives a notification that an incoming call is waiting, and can play announcements to the caller.
Example 21-16 shows the code for the previously described CallWaitingBean
in its entirety.
Example 21-16 CallWaitingBean Example Java Code
package com.oracle.sft.demo; import java.util.List; import com.oracle.sft.api.CommunicationContext; import com.oracle.sft.api.Context; import com.oracle.sft.api.Conversation; import com.oracle.sft.api.Reason; import com.oracle.sft.api.UserParticipant; import com.oracle.sft.api.bean.CommunicationBean; import com.oracle.sft.api.bean.CommunicationEvent; import com.oracle.sft.api.bean.ParticipantEvent; import com.oracle.sft.api.context.EventReason; import com.oracle.sft.api.context.ReasonData; import com.oracle.sft.api.context.WarningData; //Create the CallWaitingBean Java class using the @CommunicationBean annotation. @CommunicationBean public class CallWaitingBean { // @Context injects an instance of CommunicationContext, // which retrives instances of Conversation and UserParticipant. @Context CommunicationContext<Conversation,UserParticipant> ctx; //Annotate handleInit() with @CommunicationEvent. @CommunicationEvent(type = CommunicationEvent.Type.INITIALIZATION) void handleInit() { Conversation call = (Conversation) ctx.getCommunication(); if (call.getCaller().getName().contains("user1")) { call.indicateCallWaiting(); } } //Annotate hanldeRejectEvent() with @ParticipantEvent. @ParticipantEvent(type = ParticipantEvent.Type.REJECTED) public void hanldeRejectEvent() { EventReason eventReason = ctx.getContextElement(EventReason.class); List<ReasonData> reasonDatas = eventReason.getReasonData(); ReasonData reasonData = reasonDatas.get(0); Reason reason = reasonData.getReasonType(); //Get the warning data and codes from the EventReason interface. List<WarningData> warningDatas = eventReason.getWarningData(); WarningData warningData = warningDatas.get(0); // If the warning code is 486 Busy Here and 370 Insufficient Bandwidth, // resend the INVITE using the indicateCallWaiting() method. if (reasonData.getReasonCode() == 486 && warningData.getWarningCode() == 370) { Conversation call = (Conversation) ctx.getCommunication(); call.indicateCallWaiting(); } } // @CommunicationEvent(type = CommunicationEvent.Type.WAITING) void handleCallWaiting() { }
Message Waiting Indication (MWI) is a service that informs a user about the status of recorded messages. To use the notification feature, the user must subscribe to a notification service that makes use of the Message Waiting Indication status messages. With the Message Waiting Indication feature you can:
Send notification when a new subscription arrives.
Specify when notifications are sent in response to subscriptions.
Reject subscriptions.
Terminate subscriptions.
Note:
Typically a voice-mail server manages Message Waiting Indication accounts. When a new message arrives, the voice-mail server typically provides a listener or API that you can resister with to receive notification of new messages. How the application manages the message account is beyond the scope of the SFT Message Waiting Indication APIs.Message Waiting Indication lets the AS notify a subscriber that there is a message waiting for them. The indication is delivered to the subscriber's UE after they have successfully subscribed to the Message Waiting Indication service as defined in the 3GPP TS 24.606 specification.
When Converged Application Server receives a SUBSCRIBE message, SFT notifies the MWI application via a SUBSCRIPTION event. Once notification is received the MWI application calls instances of the MessageObservation
and ActivityParticipant
interfaces—which identify the current subscriber—using the CommunicationContext
interface.
RFC 3842 specifies that a NOTIFY message must be sent when accepting new subscriptions, the subscription has expired, or an unsubscribe event occurs. Converged Application Server's Event Notification Service sends these NOTIFY messages automatically, and application updates the MessageSummary
object during the initial SUBSCRIPTION event to ensure that the correct NOTIFY message is sent.
To configure Message Waiting Indication, use either the sft.xml deployment descriptor, or the CommunicationBean
's @ServiceAttributes
annotation. The parameters you specify corollate to the incoming SUBSCRIBE request's Subscription-State Expires header parameter, which contains an expiration time.
If the expiration time is less than zero (> 0) but greater than the minimum expiration time, Converged Application Server rejects the SUBSCRIBE request with the 423 Interval Too Brief response code.
If the expiration time is greater than zero (< 0), Converged Application Server uses the default expiration time.
If the specified expiration time in greater (>) than the maximum expiration time, Converged Application Server uses the maximum expiration time.
If the maximum number of subscriptions is reached, the next SUBSCIRBE request is rejected with the 503 Service Unavailable response code.
Example 21-17 illustrates the use of the @ServiceAttributes
annotation to configure Message Waiting Indication events. The configuration parameters are listed in the order in which you specify them:
Minimum expiration time allowed for subscriptions. The default value is 100.
Default expiration time for subscriptions. The default value is 1800.
Maximum expiration time allowed for subscriptions. The default value is 3600.
Maximum number of subscriptions per resource allowed for the service. The default value is 100.
Message Waiting Indication 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 21-8 Message Waiting Indication classes
Class | Description |
---|---|
|
|
|
|
|
Before sending a notification, the MWI application must update the |
|
|
|
|
|
|
The following examples decompose the MWIBean
Java class, which responds to Message Waiting Indication events. See "Message Waiting Indication Example" to view the application's Java code in its entirety.
Example 21-19 creates MWIBean
, a Java class whose methods handle and respond to Message Waiting Indication events.
Example 21-18 Creating the MWIBean Java Class
@CommunicationBean @ServiceAttributes(messageObservationEventConfig = {100, 1800, 3600, 100}) public class MWIBean { @Context CommunicationContext<MessageObservation,ActivityParticipant> ctx; @Context CommunicationService service; private String subscriberName; private String resourceId; ...
MWIBean
uses the @SerivceAttributes
annotation to configure the Message Waiting Indication event expiration times. This example configures the service using the default values. The @Context
annotation injects an instance of the CommunicationContext
, which in turn calls instances of MessageObservation
and ActivityParticipant
. These interfaces identify the user initiating the subscription request, and are stored in the ctx
instance variable.
The @Context
annotation injects an instance of CommunicationService
, which is referred to with the service
instance variable. CommunicationService
lets you create objects that are not related to a CommunicationSession
, such as groups.
Finally, two private instance variables are created: subscriberName
and resourceId
.
Updating MessageSummary Before Sending Notifications
Example 21-19 illustrates the hanldeSubscriptionEvent()
method. This method is annotated with the @CommunicationEvent
annotation's Type.SUBSCRIPTION
constant. When Converged Application Server receives a SIP SUBSCRIBE message, the MWI application receives notification via the SUBSCRIPTION event.
A NOTIFY message is sent when accepting new subscriptions. Converged Application Server's Event Notification Service sends these NOTIFY messages automatically, and the MWI application updates the MessageSummary
object during the initial SUBSCRIPTION event to ensure that the correct NOTIFY message is sent. Before sending a notification, the MWI application updates the MessageSummary
object. MessageSummary
is obtained via the MessageObservation
interface.
Example 21-19 Updating the MessageSummary Class
... @CommunicationEvent(type = CommunicationEvent.Type.SUBSCRIPTION) public void hanldeSubscriptionEvent() { MessageObservation messageObservation = (MessageObservation)ctx.getCommunication(); ActivityParticipant currentSubscriber = (ActivityParticipant)ctx.getParticipant(); MessageSummary messageSummary = messageObservation.getMessageSummary(); messageSummary.setMessageAccount(URI.create("sip:alice@example.com")); messageSummary.setStatusLine(true); messageSummary.setMessageSummaryLine(MessageContextClass.VOICEMESSAGE, 1, 2, 3, 4); } ...
The hanldeSubscriptionEvent()
method declares and initializes the following object reference variables:
The getCommunication()
method returns the CommunicationContext
object (via the ctx
instance variable) to the MessageObservation
interface using the messageObservation
variable.
The getParticiapnt()
method returns subscriber information from the CommunicationContext
object (via the messageObservation
instance variable). MessageSummary is to the ActivityParticipant
interface using the currentSubscriber
variable.
The getMessageSummary()
method returns message summary information from MessageObservation
(via the messageObservation
object reference variable).
MessageObservation
acts as a resource which receives notifications from the server managing the message account (for example, a voice-mail server). Users (ActivityParticipants
) subscribe to the message account. When new messages arrive the state of the MessageSummary
object is updated, and notifications are sent to all ActivityParticipants
subscribed to the account, alerting them that new messages are waiting.
The messageSummary
object variable, which serves as a reference to the MessageSummary
object, makes a series of method calls which updates the NOTIFY body with the following information:
The account to which the message-summary body corresponds. This is specified by the Message Waiting Indication application to indicate the resource. For example, the user's voice-mail account: alice@example.comThe setStatusLine(true)
method specifies that messages are waiting.
The setMessageSummaryLine()
method indicates that the waiting message types are voice messages.
As defined in RFC 3458 and RFC 3938, the following message types can provide notifications: voice-message, video-message, fax-message, pager-message, multimedia-message, text-message, or none (no message).
To send MWI notifications to subscribers, first locate the resource (for example, the voice-mail account), and then update MessageSummary
with the status of the message account. To do this you use the MessageObservation
interface to look up accounts, and the MessageSummary
class to send the notifications. Once MessageSummary
references the status of the message account, invoke resource.process()
to send NOTIFY messages to the subscribers.
Example 21-20 creates the updateResourceInfo(String resourceId)
method. This method is invoked when the status of a mail account changes. (How to get this change is beyond the scope of the SFT Message Waiting Indication APIs). This customer defined method updates messageSummary
, and sends a NOFITY message to all subscribers.
Note:
This method may be a callback method to the server managing the message account, or another application's APIs that notify MWI application that a message has arrived and is waiting. These parameters are defined by your message server's APIs.Example 21-20 Sending Notification to all Subscribers
... void updateResourceInfo(String resourceId) { MessageObservation resource = service.findByName(MessageObservation.class, resourceId); MessageSummary messageSummary = resource.getMessageSummary(); MessageSummaryLine messageSummaryLine = messageSummary.getMessageSummaryLine(MessageContextClass.VOICEMESSAGE); messageSummaryLine.setNewMessageCount(1); messageSummaryLine.setOldMessageCount(2); resource.process(); } ...
To begin the method declares and initializes the following object reference variables:
The resource
variable serves as a container for the list of subscribers (or resources) obtained via the MessageObservation.class
.
MessageObservation resource =
service.findByName(MessageObservation.class, resourceId);
The getMessagesummary()
method returns the message summary information to the messsageSummary
variable via a method call to the MessageObservation
class.
MessageSummary messageSummary = resource.getMessageSummary();
The getMessageSummaryLine()
method returns a reference to a MessageSummaryLine
which ContextClass
is specified. In this example, the type is VOICEMESSAGE
MessageSummaryLine messageSummaryLine =
messageSummary.getMessageSummaryLine(MessageContextClass.VOICEMESSAGE);
The messageSummaryLine
variable, which stores message summary line information from the MessageSummaryLine
object, makes method calls to set the old and new message counts using the setNewMessageCount(int count)
and setOldMessageCount(int count)
methods.
Finally, the resource
variable storing the subscriber list calls MessageObservation.process()
, which sends NOTIFY messages containing the updated message summary information to all of the subscribers.
messageSummaryLine.setNewMessageCount(1); messageSummaryLine.setOldMessageCount(2); resource.process();
Example 21-21 illustrates the use of the UserActivity
interface in removing a subscription (which is a type of UserActivity
). UserActivity
extends the Communication
, and provides the removeActivityParticipant()
method, which lets you remove the ActivityParticipant
from the UserActivity
.
This example declares and initializes the variable resource
, which serves as a source for the list of subscribers (or resources) obtained via the MessageObservation
class. The service
session attribute—which was declared earlier in the program—lets you call an instance of CommunicationSession
. When responding to an event, the service
session attribute injects an instance of CommunicationSession
into the CommunicationBean
.
Example 21-22 shows the code for the previously described MWIBean
in its entirety.
Example 21-22 Message Waiting Indication Scenarios
package com.oracle.sft.MWIBean; import com.oracle.sft.api.ActivityParticipant; import com.oracle.sft.api.CommunicationContext; import com.oracle.sft.api.CommunicationService; import com.oracle.sft.api.Context; import com.oracle.sft.api.MessageObservation; import com.oracle.sft.api.ProtocolMessage; import com.oracle.sft.api.MessageContextClass; import com.oracle.sft.api.bean.CommunicationBean; import com.oracle.sft.api.bean.CommunicationEvent; import com.oracle.sft.api.bean.ProtocolEvent; import com.oracle.sft.api.bean.ServiceAttributes; import com.oracle.sft.api.context.MessageObservationResource; import com.oracle.sft.api.MessageSummary; import com.oracle.sft.api.MessageSummaryLine; @CommunicationBean @ServiceAttributes(messageObservationEventConfig = {31, 61, 91, 2}) public class MWIBean { @Context CommunicationContext<MessageObservation,ActivityParticipant,?> ctx; @Context CommunicationService service; private String subscriberName; private String resourceId; //Receiving the SUBSCRIPTION Event @CommunicationEvent(type = CommunicationEvent.Type.SUBSCRIPTION) public void hanldeSubscriptionEvent() { MessageObservation messageObservation = (MessageObservation)ctx.getCommunication(); ActivityParticipant currentSubscriber = (ActivityParticipant)ctx.getParticipant(); subscriberName = currentSubscriber.getName(); resourceId = messageObservation.getName(); if (currentSubscriber.getName().contains("subscriberA")) { MessageSummary messageSummary = messageObservation.getMessageSummary(); messageSummary.setMessageAccount(URI.create("sip:alice@example.com")); messageSummary.setStatusLine(true); messageSummary.setMessageSummaryLine(MessageContextClass.VOICEMESSAGE, 1, 2, 3, 4); messageSummary.setMessageSummaryLine(MessageContextClass.FAX, 10, 20, 30, 40); messageSummary.setExtensionHeader("messageID", "to", "from", "subject", "date", "priority"); } else { //System.out.println("###### Reject the subscription. ######"); currentSubscriber.reject(); } } @CommunicationEvent(type = CommunicationEvent.Type.NOTIFICATION) public void hanldeNotificationEvent() { System.out.println("###### Receive NOTIFICATION Event ######"); System.out.println(""); } /** * Update resource info and send NOTIFY message to all subscribers. */ void updateResourceInfo(String resourceId) { MessageObservation resource = service.findByName (MessageObservation.class, resourceId); MessageSummary messageSummary = resource.getMessageSummary(); messageSummary.setStatusLine(false); MessageSummaryLine messageSummaryLine = messageSummary.getMessageSummaryLine(MessageContextClass.VOICEMESSAGE); if (messageSummaryLine != null) { messageSummaryLine.setNewMessageCount(messageSummaryLine.getNewMessageCount() + 1); messageSummaryLine.setOldMessageCount(messageSummaryLine.getOldMessageCount() + 1); } resource.process(); } /** * Send NOTIFY message to a specified subscriber to end the Subscription. */ void removeSubscriber(String resourceId, String subscriberName) { MessageObservation resource = service.findByName(MessageObservation.class, resourceId); System.out.println("###### remove Subscriber " + subscriberName); resource.removeActivityParticipant(subscriberName); } }