Upgrade calls from cellular to VoIP in a Windows Phone Silverlight 8.1 app

[ This article is for Windows Phone 8 developers. If you’re developing for Windows 10, see the latest documentation. ]

Update your app manifest

All VoIP apps must include the ID_CAP_VOIP capability in their app WMAppmanifest.xml file. To implement the seamless call upgrade scenario, you also need to include the ID_CAP_CONTACTS capability.

Add the following extensions elements to your WMAppManifest.xml file to enable VoIP call upgrade and contact bindings.

<Extensions>
  <Extension 
    ExtraFile="Extensions\\Extras.xml" 
    TaskID="_default" 
    ConsumerID="{bedab396-3404-490c-822e-13309c687e97}"
    ExtensionName="VoIP_DirectlyInitiateVideoCall"/>
  <Extension
    ExtensionName="People_Connect"
    ConsumerID="{bedab396-3404-490c-822e-13309c687e97}"
    TaskID="_default" />
  </Extensions>

VoIP apps that support call upgrade must provide an additional XML configuration file. If it does not already exist, create an XML file named Extras.xml in a folder named Extensions. The following example shows an Extras.xml file that enables VoIP call upgrades.

<?xml version="1.0" encoding="utf-8" ?>
<ExtrasInfo>
  <AppTitle>
    <default>VoipAppWithCapabilityAppOne</default>
  </AppTitle>
  <Consumer ConsumerID="{bedab396-3404-490c-822e-13309c687e97}">
    <ExtensionInfo>
      <Extensions>
        <ExtensionName>VoIP_DirectlyInitiateVideoCall</ExtensionName>
      </Extensions>
      <VoIPSettings>
        <HasSeamlessEscalation value = "true"/>
      </VoIPSettings>
    </ExtensionInfo>
  </Consumer>
</ExtrasInfo>

In your app’s Package.appxmanifest file, you need to add the following Extensions element inside the Applications element.

<Extensions>
  <Extension Category="windows.backgroundTasks" EntryPoint="AgHost.BackgroundVoipTask">
    <BackgroundTasks>
      <Task Type="systemEvent" />
    </BackgroundTasks>
  </Extension>
</Extensions>

You also need to add the following Extensions element under the Package element.

<Extensions>
  <Extension Category="windows.activatableClass.inProcessServer">
    <InProcessServer>
      <Path>AgHostSvcs.dll</Path>
      <ActivatableClass ActivatableClassId="AgHost.BackgroundVoipTask" ThreadingModel="both" />
    </InProcessServer>
  </Extension>
</Extensions>

Use contact bindings to bind a contact with your app

Contact binding is a feature you can use to associate a local contact on the device with a contact that is owned and maintained by your app’s own web service. For information about implementing contact bindings, see Implementing contact bindings in a Windows Phone Silverlight 8.1 app.

One additional step you must take to implement contact bindings for a VoIP call upgrade is specify the value CallUpgradeIdentifier as the value to the AppBindingAttributes property of the bindings you create. After a contact has been bound with your app this way, the contact is then recognized by the system as video-capable.

Create a Uri mapper to navigate to the correct page when the system launches your app for call upgrade

With a Uri mapper you can intercept navigation to a page in your app and change the destination based on the Uri passed in. For a seamless upgrade VoIP app, the system launches your app if the user taps on the connect tile created for your contact binding or for a seamless or non-seamless VoIP call upgrade. The following code example shows a Uri mapper that parses the Uri and redirects the navigation to the relevant page.

class VoipURIMapper : UriMapperBase
{
  private string tempUri;

  public override Uri MapUri(Uri uri)
  {
    tempUri = System.Net.HttpUtility.UrlDecode(uri.ToString());
    if (tempUri.Contains("PeopleExtension"))
    {
        Debug.WriteLine("Launching from connect pivot");
        return new Uri("/MainPage.xaml", UriKind.Relative);
    }
    else if (tempUri.Contains("Video"))
    {
      if (tempUri.Contains("CallUpgradeGuid"))
      {
        Debug.WriteLine("Launching seamless call upgrade from call progress");
        return new Uri(tempUri.Replace("MainPage.xaml", "UpgradeStatus.xaml"), UriKind.Relative);
      }
      else
      {
        Debug.WriteLine("Launching non-seamless call upgrade from call progress");
        return new Uri(tempUri.Replace("MainPage.xaml", "UpgradeStatus.xaml"), UriKind.Relative);
      }
    }
    // Otherwise perform normal launch.
    return uri;
  }
}

Add a Scheduled Task Agent to your app

For the seamless call upgrade scenario to work, your app must include a scheduled task agent in your solution and create a reference to it from your foreground app project. The code for implementing VoIP call upgrade does not use this agent, but it is required by the system. Your app can use this agent to perform tasks on a periodic basis if you choose. For information on adding a Scheduled Task Agent to your app, see How to implement background agents for Windows Phone 8.

Handling seamless upgrade in the CallInProgressAgent

In your app’s CallInProgressAgent, the OnFirstCallStarting method is called by the system when a call is ready to be upgraded to VoIP. In this method, you obtain the default instance of VoipCallCoordinator and use it to call GetNextOperation to retrieve the VoIP operation provided by the system. Check to see if the VoipOperationType is QueryRemotePartySeamless. Query your web service to see if the remote caller supports seamless call upgrade. If it does, call NotifyCompletion with the values true and SeamlessCallUpgradeSupport.Supported to let the system know that seamless upgrade is supported for this call.

The following code example shows a simple implementation of OnFirstCallStarting.

protected override void OnFirstCallStarting()
{

  Debug.WriteLine("[CallInProgressAgentImpl] The first call has started.");

  VoipCallCoordinator callCoordinator = VoipCallCoordinator.GetDefault();
  IVoipOperation voipOperation = null;
  try
  {
    voipOperation = callCoordinator.GetNextOperation();
  }
  catch (Exception exc)
  {
    Debug.WriteLine(exc.Message + (exc.InnerException == null ? string.Empty : exc.InnerException.Message));
  }
  while (voipOperation != null)
  {
    switch (voipOperation.Type)
    {
      case VoipOperationType.QueryRemotePartySeamless:
      {
        QuerySeamlessUpgradeSupportOperation querySeamlessOperation = voipOperation as QuerySeamlessUpgradeSupportOperation;
        querySeamlessOperation.NotifyCompletiInon(true, SeamlessCallUpgradeSupport.Supported);
      }
      break;

    default:
    {
      QuerySeamlessUpgradeSupportOperation querySeamlessOperation = voipOperation as QuerySeamlessUpgradeSupportOperation;
      querySeamlessOperation.NotifyCompletion(true, SeamlessCallUpgradeSupport.Supported);
    }
    break;
  }

  voipOperation = callCoordinator.GetNextOperation();
}
        }

Requesting an outgoing seamless upgrade call

Your app calls RequestOutgoingUpgradeToVideoCall when it makes an outgoing seamless upgrade call. This is to help signal to the call receiver that it’s an upgrade call, as opposed to a regular incoming call, so the call receiver can avoid putting the existing cellular call on hold and keep the experience seamless. The following code illustrates the sequence of events in an outgoing seamless upgrade call request.

VoipPhoneCall outgoingCall; 

// The will be parsed from the deep link URL that the system
// will use to launch this action"
Guid upgradeGUID = Guid.NewGuid(); 
String callInProgressPageUri = @"/CallStatusPage.xaml";
String recipientName("Bob"); //This will be resolved by the app

var callCoordinator = VoipCallCoordinator.GetDefault();
callCoordinator.RequestOutgoingUpgradeToVideoCall(
upgradeGUID,
callInProgressPageUri,
recipientName,
"VoIP Chatterbox",
out outgoingCall);

//VoIP app begins setting up the VoIP call
//....
//Call is now set up (connection wise)
//....
//Now the call is ringing on the other side and user picks up
outgoingCall.NotifyCallActive();
//VoIP app is now setting up all the necessary streams (audio + video)
//....
//Streams are ready
outgoingCall.NotifyCallReady(); //This is called to let us know the 
// upgrade is complete and the cellular call should be terminated

// At this point, the system ends the cellular call

Requesting an incoming seamless upgrade call

Your app calls RequestIncomingUpgradeToVideoCall when it’s receiving an incoming upgrade call. This is to help signal to the system on the receiver’s end that this is an upgrade request, as opposed to a new incoming call. As described in the previous section, this occurs so the system doesn’t put the existing cellular call on hold, maintaining the seamless experience. The following code illustrates the sequence of events in an outgoing seamless upgrade call request.

//VoIP app is setting up the Video call in the background
//....
VoipPhoneCall incomingCall;

String callInProgressPageUri = @"/CallStatusPage.xaml";
String recipientName = "Bob";
String recipientNumber("555-5555");

Uri contactImageUri;
contactImageUri = new Uri("Assets\\DefaultContactImage.png");
Uri brandingImageUri;
brandingImageUri = new Uri("Assets\\ApplicationIcon.png");
Uri ringtoneUri;
ringtoneUri = new Uri("Assets\\Ringtone.wma");
TimeSpan timeout;
timeout = TimeSpan.FromTicks(90 * 10 * 1000 * 1000);

//VoIP app is done setting up the VoIP connection and is ready to show the incoming call UI to the user
var callCoordinator = VoipCallCoordinator.GetDefault();
callCoordinator.RequestIncomingUpgradeToVideoCall(       
callInProgressPageUri,
recipientName,
recipientNumber,
contactImageUri,
"VoIP Chatterbox",
brandingImageUri,
"", //Extra Call details
ringtoneUri,
timeout,
out incomingCall);

// User accepts the incoming call
//....
// Streams are being set up
incomingCall.NotifyCallActive();
// Streams are done being set up
incomingCall.NotifyCallReady();

At this point, the phone team ends the cell call

UI Requirements

If you want to provide a custom icon that will be displayed on the VoIP call upgrade button in the built-in phone UI, it must be a monochrome white PNG file named VideoUpgradeIcon.png. Transparency is supported.