Posted by: rcosic | 08/11/2011

Handling transfers in Lync Client API

Note: this post is part of Lync Client development series. You can find the following topics here:

Hello! As promised, I’ll talk about transferring calls when developing in Lync Client API.

First of all, let me tell you this – it is just not straightforward, as you may seem. There is no “Transferred” state within conversation states (only thing besides Inactive, Active, Invalid and Terminated state, you have Parked state, which is something really different). Also, although there is a Transferring state for audio/video modality available, it is not quite clear when and if this event arises. For more precise explanation, we should revise the call workflow Lync does when doing a call transfer…

In Lync, like in any other system, you can achive a blind or a supervised transfer. Blind transfer means that a call will be transferred to a target user without knowing whether the target user will be able to accept the call. Supervised (or consultative) transfer means that a calling user will be first put ‘on hold’ (either implicitly or automatically by Lync during establishing a second call), another user will be called and queried whether he is available to pick up the call. After that, first call will be transferred to the target user. Let’s see some scenarios…

First scenario: user1 calls user2, who will in turn call another user (user3), and transfer the first call to him. This is the case of a blind transfer.

The first user is not interesting at all, since the first call is actually simple outgoing call to another user. So, we’ll skip the explanation of it. But, let’s see what happens from the perspective of a second user in this workflow (user2):

  • new converstation is added,
  • new audio/video call is registered,
  • ConversationState = Active,
  • AVModalityState = Notified, (this is the indication of an incoming call),
  • AVModalityState = Joining,
  • AVModalityState = Connected,
  • user’s availability changes to Busy (‘In a call’),
  • AVModalityState = OnHold,
  • AVModalityState = Transferring,
  • ConversationState = Inactive,
  • AVModalityState = Disconnected,
  • Call is ended,
  • user’s availability is changed to Free (‘Available’),
  • ConversationState = Terminated, and
  • conversation is removed.

So far, understandable: a call has been put on hold (automatically by Lync), marked as being transferred, and then deactivated and terminated. But, what happens on the side of ‘third participant’ (user3) ?

  • new converstation is added,
  • new audio/video call is registered,
  • ConversationState = Active,
  • AVModalityState = Notified, (this is the indication of an incoming call),
  • AVModalityState = Joining,
  • AVModalityState = Connected,
  • user’s availability changes to Busy (‘In a call’),
  • ….

If you see the wokflow, nothing special is telling us that this call was originated from transferring the call to us. So, if you want to handle it, you have to think about something else…

As you know, Notified modality state tells us that a call should be incoming call. When this event happens, it is suitable to figure out what we also have in place to figure out if this is a transferred call or not. For this, I will first extract participants of the call: firstly, myself as a SelfParticipant and secondly, another participant. When another (remote) participant is referred to another party, there is a property on participant object holding this information – ReferredByUri. Unluckily, there is only an URI representing the actual contact, but it is enough for most cases. So, this would be clear indication that a call is transferred to here and then we can query some other properties, but now on underlying conversation object – TransferredBy property, which is filled in case of a blind transfer, and Replaced property in case of a supervised transfer. Note, though, that this Replaced property actually applies to a conference, but it is also filled in this special case. After this figured out, I just fill the internal variables to store participants in more meaningful way: a caller, a called party, and a connected party, all representing by its URIs. Take a look on the following code snippet:

Participant selfParticipant = m_conversation.SelfParticipant;
Participant anotherParticipant = m_conversation.Participants[1];

// get the transferrer’s URI when a remote participant was referred by another party
if (anotherParticipant.Properties.ContainsKey(ParticipantProperty.ReferredByUri))
{
// this is an indication of some type of transfer
if (anotherParticipant.Properties[ParticipantProperty.ReferredByUri] != null)
{
m_strTransferredById = anotherParticipant.Properties[ParticipantProperty.ReferredByUri] as string;
}

// the call has been transferred to here

// get the Id of the conversation that originated the transfer
if (m_conversation.Properties.ContainsKey(ConversationProperty.TransferredBy))
{
// this should be an indication of a blind transfer
if (m_conversation.Properties[ConversationProperty.TransferredBy] != null)
{
m_strRelatedConversationId = m_conversation.Properties[ConversationProperty.TransferredBy] as string;
}
}

// get the Id of the conference that originated the transfer
if (m_conversation.Properties.ContainsKey(ConversationProperty.Replaced))
{
// this should be an indication of a supervised (consultative) transfer
if (m_conversation.Properties[ConversationProperty.Replaced] != null)
{
m_strRelatedConversationId = m_conversation.Properties[ConversationProperty.Replaced] as string;
}
}

m_bIsTransferred = true;
}
else
{
m_bIsTransferred = false;
}

if (m_eCallDirection == ELyncCallDirection.CALL_DIRECTION_OUTGOING)
{
m_strCallerId = selfParticipant.Contact.Uri;

// in case we have been transferred to another user
if (m_bIsTransferred)
{
m_strCalledId = m_strTransferredById;
m_strConnectedId = anotherParticipant.Contact.Uri;
}
else
{
m_strCalledId = anotherParticipant.Contact.Uri;
m_strConnectedId = m_strCalledId;
}
}
else if (m_eCallDirection == ELyncCallDirection.CALL_DIRECTION_INCOMING)
{
// in case of call being transferred to us
if (m_bIsTransferred)
{
m_strCallerId = m_strTransferredById;
m_strCalledId = selfParticipant.Contact.Uri;
m_strConnectedId = anotherParticipant.Contact.Uri;
}
else
{
m_strCallerId = anotherParticipant.Contact.Uri;
m_strCalledId = selfParticipant.Contact.Uri;
m_strConnectedId = m_strCallerId;
}
}

Another thing to consider, when handling transfers, is to keep track of active conversations (and windows).

When you are a ‘second party’ (user who handle the transfer), you will have to consider two (or more) calls/conversations to handle on the same time. This also includes figuring out which conversation is bound to which conversation, and how to properly handle the ‘switching’ between them. To make it short, a second conversation is created when there is a second call. In this point of time, you might to react on it to remove it and handle it as the same ‘logical conversation’, which has its own flow. For that to make, you have to consider that first conversation gets on hold, deactivated and finally disconnected. And that, in all this time, some error or rejection might occur. It’s up to you to handle it properly and try to think about all possible cases.


Responses

  1. Hello. What’s eCallDirection? I assume its a way to detect whether the call is incoming or outgoing. Can you please share this piece of code? Thanks

    • Hello Bilal,

      yes, I found a way to determine call ‘direction’, since it’s unavailable in Lync Client API. I’m using modality state change event to inspect two indicative states: Connecting and Notified for it. Make note that Connecting state may occur as a result of conference call:

      void m_audioVideo_ModalityStateChanged(object sender, ModalityStateChangedEventArgs e)

      switch (e.NewState)

      case ModalityState.Notified:
      m_eCallDirection = ELyncCallDirection.CALL_DIRECTION_INCOMING;
      CalculateCallProperties();

      case ModalityState.Connecting:
      m_eCallDirection = (!string.IsNullOrEmpty(m_strConferenceInviterId)) ? ELyncCallDirection.CALL_DIRECTION_INCOMING : m_eCallDirection = ELyncCallDirection.CALL_DIRECTION_OUTGOING;
      CalculateCallProperties();

      You can find out conference inviter id on subscribing to the conversation property changed event and inspecting the ConferencingUri property.
      Also, I’m calling the routine to calculate other properties, as explainer above in the post.

      I hope it helps.
      Ratko.

  2. Hello Ratko, Thank you for the great series of articles on Lync. I have a question with respect to: TransferredBy and Replaced. To which conversations these refer to? In the case we have User1 -> User2 -> User3. Is it correct to say, TransferredBy is (User1 -> User2) and Replaced is (User2 -> User3)? Thanks for your assistance.


Leave a comment

Categories