Microsoft Lync and Skype for Business have a rich set of .NET APIs which make it easy to extend the platform and integrate it with other applications. This blog helps explain how to use those APIs.

Splitting incoming calls between the signaling and collaboration layers

Posted: June 7th, 2013 | Author: | Filed under: UCMA 4.0 | Tags: | No Comments »

If you want your UCMA application to handle some (but not all) incoming messages at the SIP level, it’s possible to split requests between the “signaling” and “collaboration” layers of UCMA. You can do this by using the InnerEndpoint property on your UserEndpoint or ApplicationEndpoint object. This property gives you access to an instance of RealTimeEndpoint, the alter ego of UserEndpoint or ApplicationEndpoint from the signaling layer. By subscribing to an event on this RealTimeEndpoint object, you can give your application an opportunity to handle calls on the signaling layer.

With UserEndpoint or ApplicationEndpoint, you receive notifications of incoming calls by calling the RegisterForIncomingCall<T> method, where T is the class that represents the type of call you want to register for. You provide the event handler as a parameter to the method call. With RealTimeEndpoint, you instead need to subscribe to the SessionReceived event, as shown below.

_userEndpoint.InnerEndpoint.SessionReceived +=
    OnInnerEndpointSessionReceived;

It’s completely possible to have an event handler for SessionReceived on the inner endpoint, while also registering an event handler using RegisterForIncomingCall<T>. So what happens in this case if a call arrives at your application?

The SessionReceived event fires first, which makes sense as it handles incoming requests at a lower level. In the SessionReceived event handler, you have the opportunity to handle the incoming request by calling BeginAccept, or by declining or forwarding the request. If you do one of these things, the collaboration layer event handler will not fire.

On the other hand, if you don’t handle the session at all in the event handler for SessionReceived, then your other event handler (the one you passed into RegisterForIncomingCall<T>) will fire, and you can handle the call with the collaboration layer.

This means that you can inspect messages in your SessionReceived event handler, and if some sign is present (say, a specific SIP header) you can handle them with the signaling layer; otherwise, you can pass them along to the collaboration layer. Here’s an example of a SessionReceived event handler that does just this:

void OnInnerEndpointSessionReceived(object sender,
    SessionReceivedEventArgs e)
{
    Console.WriteLine(
        "Session received on signaling layer.");

    if (e.RequestData.SignalingHeaders.Any(
        h => h.Name == "My-Custom-Header"))
    {
        Console.WriteLine(
            "Handling session on the signaling layer.");

        e.Session.OfferAnswerNegotiation = _myOfferAnswer;

        try
        {
            e.Session.BeginAccept(ar =>
                {
                    try
                    {
                        e.Session.EndAccept(ar);
                    }
                    catch (RealTimeException ex)
                    {
                        Console.WriteLine(ex);
                    }
                },
                null);
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine(ex);
        }
    }
    else
    {
        Console.WriteLine(
            "Not handling session on the signaling layer.");
    }
}

The event handler looks for a SIP header, My-Custom-Header, and if it’s present, it accepts the call using a special implementation of IOfferAnswer. If not, it does nothing, allowing the other event handler to fire.

This works equally well if you send a response declining the INVITE, as in the following code:

void OnInnerEndpointSessionReceived(object sender,
    SessionReceivedEventArgs e)
{
    Console.WriteLine(
        "Session received on signaling layer.");

    if (e.RequestData.SignalingHeaders.Any(
        h => h.Name == "My-Custom-Header"))
    {
        Console.WriteLine(
            "Handling session on the signaling layer.");

        e.Session.TerminateWithRejection(603, "Decline", null);
    }
    else
    {
        Console.WriteLine(
            "Not handling session on the signaling layer.");
    }
}

This code calls the TerminateWithRejection method on the session to decline the incoming call, which means that the collaboration layer event handler will never see the session if it has that special header. (That’s a great method name, by the way – it sounds more like it has to do with a bad breakup than a SIP response.)

You can use this technique to give special, lower-level handling to particular types of messages that arrive at your endpoint, extracting information rather than accepting the session, or applying a customized offer/answer negotiation.



Leave a Reply

  • Note: Comment moderation is in use because of excessive spam. Your comment may not appear immediately.

  •