How to use the SIP Client API

Starting the SIP Client

To start the SIP Client:

  1. Create an instance of the CSIP class. The CSIP class creates a CSIP object and implements the callback functions defined by MSIPObserver. The CSIP object connects the client to a shared server.

    Note: The server is started if it is not active. This may take a short time while the server side resources are created and initialised.

  2. Create an instance of the CSIPConnection class to use an IAP. The CSIPConnection class must implement the callback functions defined by MSIPConnectionObserver. The CSIPConnection class creates CSIPConnection object. The CSIPConnection object forms a sub-session between the client and the server.

  3. Call the CSIPConnection::NewL() function to create an active connection. The CSIPConnection::EActive state indicates that the object can be used to send SIP messages and create registrations and dialogs. The current state is asked by calling CSIPConnection::State(). The MSIPConnectionObserver::ConnectionStateChanged(CSIPConnection:: EActive) callback function is called.

The following illustration shows the sequence of events involved in starting the SIP Client API.

Closing the SIP Client

To close the SIP Client:

  • Delete the CSIP object to close the connection to the SIP server.

  • Delete the CSIPConnection object to close the sub-session between the client and the server.

  • When the connection has been closed, operations on objects that depend on the connection fail with the KErrSIPResourceNotAvailable error code.

For example, if an application has CSIPConnection and CSIPRegistrationBinding objects, and it deletes the CSIPConnection object, then subsequent attempts to use the CSIPRegistrationBinding object fails. This is because CSIPRegistrationBinding cannot communicate with the server side of the SIP stack without the CSIPConnection object.

The following illustration shows the order for deleting SIP Client API objects.

Sending a SIP request

To send a SIP request:

  • Create a CSIPRequestElements object and enter the required fields of the request.

  • Call the CSIPConnection::SendRequestL(CSIPRequestElements*) function to take control of the CSIPRequestElements objects and communicate the request to the server side of the SIP stack.

    CSIPConnection creates a CSIPClientTransaction object and returns it to the application.

Note: SendRequestL() starts the server side processing of sending the MESSAGE request. This task takes time as address resolving and socket operations are done only after the SendRequestL() call is returned. If an error occurs after SendRequestL() returns, then the error is communicated to the application through one of the MSIPConnectionObserver::ErrorOccured() callback functions. An example of such a failure is failure to resolve the address. When SendRequestL() returns, the MESSAGE request is not yet sent to the network, and is being processed on the server side of the SIP stack.

After SendRequestL() returns and the sending of a MESSAGE request is in progress on the shared server, there is no way for the application to cancel that operation. This also applies to other operation types. However the CSIPClientTransaction::CancelL() function is used to send a CANCEL request after the application sends an INVITE request.

When the SIP response is received from the server side, the CSIPConnection object:

  1. creates a CSIPResponseElements object to represent the SIP response

  2. attaches it to the CSIPClientTransaction object

  3. notifies the application through a call to one of the MSIPConnectionObserver:: IncomingResponse() callback functions.

The application determines the kind of response received by accessing the CSIPResponseElements object through CSIPClientTransaction. As the response is provisional, the application does not delete the CSIPClientTransaction object. A '200 response' is received later, and the application then deletes the CSIPClientTransaction object.

Then, create a CSIPResponseElements object to represent the SIP response, attach it to CSIPClientTransaction and notify the application with MSIPConnectionObserver:: IncomingResponse.

The following illustration shows the sequence of events involved in sending a MESSAGE request and receiving two responses to it.

Receiving a SIP request

A standalone SIP request is received through an existing CSIPConnection object. The CSIPConnection creates CSIPRequestElements and CSIPServerTransaction objects before notifying the application through the MSIPConnectionObserver::IncomingRequest() callback function. The CSIPRequestElements and CSIPServerTransaction represent the incoming SIP request

The CSIPServerTransaction object is passed to the CSIPRequestElements object through the callback function. The application retrieves information about the request from the CSIPServerTransaction object.

To send a response, the application creates a CSIPResponseElements object and passes it to CSIPServerTransaction::SendResponseL(). This passes the response to the server side for further processing. When SendResponseL() is returned, the application no longer needs the CSIPServerTransaction object and deletes it.

The following illustration shows the sequence of events involved in receiving a MESSAGE request and sending a response to it.

Receiving a SIP request and creating a session

The SIP stack receives a SIP INVITE request from the network acting as a User Agent Server (UAS) and creates an invite server transaction for the received request. The SIP stack sends a '100 SIP response' to the remote UA. After comparing the received request with the stored application capabilities, the SIP stack routes the received INVITE request to the chosen application.

The application creates a CSIPInviteDialogAssoc object and the SIP stack creates an instance of CSIPDialog. The application sends a '200 SIP response' to the remote UA. The remote UA acknowledges the '200 SIP' response with ACK and creates a SIP session is between the local and remote UA.

The following illustration shows the sequence of events involved in receiving a SIP request and creating a SIP dialog.

Registration

An application creates a CSIPRefresh object to initiate a registration. It then creates a CSIPRegistrationBinding object, passing CSIPRefresh to it.

Note: It is not mandatory to create a CSIPRefresh object. It is required if the registration is to be refreshed by the SIP stack.

After the CSIPRegistrationBinding object exists, the application initiates the registration process by calling RegisterL() on the CSIPRegistrationBinding object. CSIPRegistrationBinding forms a REGISTER request and communicates it to the server side of the SIP stack, it instantiates the CSIPClientTransaction object and returns it to the application.

When the '200 SIP response' is received from the server side, CSIPConnection creates the CSIPResponseElements object to contain the response, and then routes the response to CSIPRegistrationBinding. CSIPRegistrationBinding attaches the response to CSIPClientTransaction. The application is informed about the response through one of the MSIPConnectionObserver::IncomingResponse() callback functions.

The application does not require the CSIPClientTransaction object and can delete it. The IsContextActive() finds out if the registration is successful.

The following illustration shows the sequence of events involved in registering with a refresh and receiving a '200 OK response'.

Configuring the registration

When registering an SIP connection, applications have two options to configure the registration : outbound proxy caching and expiration value removal. To set each of these options, call CSIPRegistrationBinding::SetProperty() with the corresponding property constant, as explained below.

The SIP stack resolves the IP address of the outbound proxy during registration. The default behavior is to resolve the address again for each SIP message sent using this registration context. However, you can also cache the address to optimise network traffic. If you set KSIPCacheOutboundProxyIP property to ETrue, the SIP stack reuses the proxy address that was resolved during registration. The proxy address is refreshed by successful REGISTER transactions, which always perform a DNS query.

By default, the SIP stack sends the expiration value in the REGISTER request. In the '200 OK' answer to a REGISTER message, the registrar returns an expiration value that may be different from the value that was sent. The SIP stack validates the returned expiration value against the minimum and maximum expiration values: if the returned expiration value is out of range, the SIP stack uses the minimum value or the maximum value.

  • The minimum expiration value is 30 seconds, to reduce network activity and battery consumption.

  • The maximum expiration value is the expiration value that you specify for the REGISTER request. By default, the maximum expiration value is 3600 seconds, to protect the application from DOS attacks that provide '200 OK' responses with a very high expiration value.

If you set KSIPSendWithExpires to EFalse, the SIP stack does not send the application's expiration value in the REGISTER message.

Creating a dialog with INVITE

To create a dialog with INVITE an application creates CSIPInviteDialogAssoc and CSIPMessageElements objects. The application must enter the relevant SIP headers in the CSIPMessageElements object before passing it to CSIPInviteDialogAssoc using the CSIPInviteDialogAssoc::SendInviteL() function.

CSIPInviteDialogAssoc then forms an INVITE request and communicates it to the server side of the SIP stack. It then creates a CSIPClientTransaction object, and returns it to the application.

When the server sends '180 response', the CSIPConnection creates a CSIPResponseElements object to contain the response, and then routes the response to CSIPDialog. CSIPDialog attaches the response to CSIPClientTransaction. The application is informed about the receipt of the '180 response' through a call to one of the MSIPConnectionObserver:: IncomingResponse() callback functions.

When the '200 response' is received, processing similar to '180 response' occurs. Then the application responds by sending an ACK request. This is done by invoking the CSIPInviteDialogAssoc::SendAckL() function, which forms the ACK request and sends it to the server side of the SIP stack.

The INVITE request might fork at a proxy, so the server side of the SIP stack waits for several possible responses. When the forking is does not happen, the server side of the SIP stack sends an InviteCompleted event. The SIP Client API forwards the InviteCompleted event to the application, indicating that the application can now delete the CSIPClientTransaction.

The following figure shows the sequence of events involved in sending an INVITE, receiving a response and sending an ACK.

Generating a '100 SIP response'

The SIP stack generates a '100 response' automatically when it receives an INVITE request from the network. Applications are not allowed to send '100 responses'.

Responding to a CANCEL request

When the SIP stack receives a CANCEL request from the network, it automatically responds to it. CANCEL requests are never passed to an application.

If the SIP stack:

  • does not find a matching transaction for a CANCEL request, it automatically generates a '481 response'.

  • finds a matching transaction for a CANCEL request, it automatically generates a '200 response'.

  • finds a matching INVITE server transaction that is in the Proceeding state (that is a final response is not yet sent), it also automatically generates a '487 response' to the INVITE transaction.

After these actions are complete, the SIP stack informs the application by calling the MSIPConnectionObserver::InviteCanceled() callback function.

Sending a SIP request and responding to a HTTP Digest challenge

A SIP server in the signalling chain challenges any SIP request initiated by the application with an HTTP Digest challenge by responding with a '401 SIP response', '407 SIP response' or a '494 SIP response'. The responses contain details of the challenge. These responses are never passed directly to the application. Instead, they are passed to the application through a call to the callback function MSIPHttpDigestChallengeObserver::ChallengeReceived().

To enable an application to respond to a challenge, it must:

  • create a CSIPHttpDigest object

  • provide an implementation of the callback function MSIPHttpDigestChallengeObserver::ChallengeReceived() .

The application can either accept or reject the challenge:

  • If it accepts the challenge, the callback function must call CSIPHttpDigest::SetCredentialsL(), specifying the username, password, and the realm passed to the callback function.

    Then, SIP stack re-sends the original SIP request with the HTTP Digest response.

  • If it rejects the challenge, the callback function must call CSIPHttpDigest::IgnoreChallenge(), specifying the realm passed to the callback function.

    Then, SIP stack calls the callback function MSIPConnectionObserver::ErrorOccured(), by passing the error code KErrSIPForbidden.

If an application does not want to receive the HTTP Digest challenge then it must not create a CSIPHttpDigest object. In this event, the SIP stack calls the callback function MSIPConnectionObserver::ErrorOccured(), passing the error code KErrSIPForbidden.

Note:

  • If an application uses the SIP Profile Agent for SIP registration, the SIP Profile Agent handles the HTTP Digest challenges related to the registration procedure according to the SIP profile information.

  • The SIP stack maintains a HTTP Digest cache which stores username and password pairs for realms. If the username and password have been configured for a proxy, or if the application has already provided them after being asked through a MSIPHttpDigestChallengeObserver::ChallengeReceived() call, then the SIP stack uses them to answer subsequent challenges for a matching realm automatically.

    For example, if the application uses a SIP Profile whose proxy is configured with HTTP Digest passwords and then sends an INVITE, it does not need to provide the passwords for the proxy as the SIP stack gets it from the HTTP Digest cache.

Notification of a failed refresh

The following example shows how an application is notified when a refresh request within a dialog terminates after an error occurs. In this example the application sends a SUBSCRIBE request with refresh, and receives a 2xx class response. The SIP stack periodically sends a refreshed SUBSCRIBE message. After successfully refreshed SUBSCRIBE requests, the remote server responds with a '481 response', and the application receives the following callback:

MSIPConnectionObserver::ErrorOccured(TInt aError, CSIPDialogAssocBase& aDialogAssoc)

If the value of aError is KErrSIPTerminatedWithResponse, then the actual SIP response causing the refresh to end can be found as follows:

  • Check that aDialogAssoc.Type() returns "SUBSCRIBE".

  • Call the function with the following signature to get the refresh instance (CSIPRefresh object):

    const CSIPRefresh* CSIPSubscribeDialogAssoc::SIPRefresh() const
  • Use the CSIPRefresh object to get the associated transaction by calling the following function:

    const CSIPClientTransaction* CSIPRefresh::SIPTransaction() const

    Note: The returned CSIPClientTransaction object is not similar to the one returned from the call to SendSubscribeL(), that the application originally used to send the SUBSCRIBE request.

  • Call the following function to get the SIP response:

    const CSIPResponseElements* CSIPClientTransaction::ResponseElements() const

Error handling

Errors are passed to an application by calling the callback functions or by leaving. For more information, see Cleanup support overview. If an error occurs during the synchronous part of an operation is initiated by the application, functions leave with an error code. If an error occurs during the asynchronous processing stage, the error is passed to the application through one of the MSIPConnectionObserver::ErrorOccured() callback functions.

There are several overloaded variants of ErrorOccured() functions. Each of them takes the error code and reference to the SIP Client API object that encountered the error:

  • A call to the variants of ErrorOccured() taking a CSIPTransactionBase or CSIPClientTransaction parameter means that transaction has entered the state CTransactionBase::ETerminated.

  • A call to the variant of ErrorOccured() taking a CSIPRegistrationBinding parameter means that the registration context is no longer active, that is CSIPRegistrationBinding::IsContextActive() == EFalse.

    If the registration was being refreshed, then the refresh has also ended and its state is CSIPRefresh::ETerminated.

If the callback function MSIPConnectionObserver::ConnectionStateChanged() is called and the aState parameter is either CSIPConnection::EInactive or CSIPConnection::EUnavailable, then all dialogs, registrations, and standalone transactions using the CSIPConnection are terminated.

To avoid an overload of callback calls in this type of situation, calls to ErrorOccured() for the individual dialog, registration and transaction objects are not sent to the application.

When the connection state changes, the MSIPConnectionObserver::ConnectionStateChanged() callback function does not provide a reason. Call CSIPConnection::GetConnectionError() to get details about the connection error that caused the change.

Refreshing a Connection

To refresh a connection, do the following steps:

  1. Start the connection on the desired IAP.

  2. Monitor the status of the connection using the MSIPConnectionObserver::ConnectionStateChanged(CSIPConnection::TState aState) function.

  3. If the connection is inactive, notify the EInactive state to the application through MSIPConnectionObserver::ConnectionStateChanged() function that is, if (CSIPConnection::TState == EInactive), then send a RefreshConnection() function to make it active.

    Note: This requires a bearer monitor on that IAP to which a refresh request is sent.

  4. Any changes to the state of the connection is notified through MSIPConnectionObserver::ConnectionStateChanged().

The following sequence diagram shows how a client refreshes a connection.

The following example code describes how to refresh a connection.

// It is assumed that the connection is started and is in an Inactive state that is, 
// (CSIPConnection::TState == EInactive)

// Refresh the IAP ID of the connection by sending a Refresh Request to the Bearer monitor of the IAP
CSIPConnection:: RefreshConnection()

// Note: The application is notified of the changes in the state of the connection through the 
// ConnectionStateChanged() function of MSIPConnectionObserver Interface.