Going Places

How Connection Manager Connects

Marcus Perryman

Code download available from the MSDN Code Gallery

Contents

PDP Contexts
Modeling the Network Topology
Making Connections
Connection Requests and Arbitration
Using the SDK Sample Code

I remember it was day three of the conference. I was again sitting in an icy blast of air-conditioning, my mind slightly fuzzy from two previous days of information overload—and maybe being a little too late leaving the bar the night before—listening to the lead PM expound the wonders of the all-new Connection Manager technology.

I came out thinking, what's the point of all that? I mean, who needs a technology that tells me if my device is connected? At the time, connectivity was only possible via umbilical cables or colossal appendages that required a PhD in network protocol analysis and unending patience to operate: if the users didn't know that the device was connected, they weren't qualified to own it!

Thankfully technology has moved on, and today the average mobile phone offers such a plethora of exotic radio technologies it could fry the contents of your lunch box at 40 paces. Windows Mobile devices occupy the more generous end of this scale, and married with the Windows CE preemptive multitasking kernel, the foresight of the Connection Manager team has become very evident.

Without Connection Manager, for all but the simplest network environments, the only way an application could choose a connection would be by presenting the user with a list and asking him to select one—which puts you back to requiring a PhD in network protocol analysis. Connection Manager neatly moves this problem away from the user—who, after all, just wants the application to work—and makes it the mobile operator and OEM's problem, reasoning that if they can't figure it out, the poor user has no hope.

There are two main reasons why the correct use of Connection Manager is vital when a Windows Mobile application requires network data. First, it's not just your code that needs to connect. Many of today's applications are connected or at least offer some form of online ability to download updates or additional features. This increases the likelihood that two applications will contend for connectivity at some point on the device. For enterprise users using push or poll e-mail solutions, this is almost a certainty. Second, Windows Mobile devices are sold and used around the world. An application that successfully appeals to a global market must navigate any network configuration a mobile operator might conceivably dream up—and in my experience mobile operators have active imaginations when it comes to network topology!

I'm a firm believer that better understanding leads to better use. In this column, I want to pull up the hood and shine a light on some of Connection Manager's inner workings, specifically looking at how it adds value and removes complexity when connecting over wide-area cell phone networks. This might appear more relevant for C++ developers who commonly call Connection Manager APIs directly. Managed code developers have many classes, such as HttpWebRequest, that use Connection Manager without the developer being directly involved. However, even for .NET developers, understanding how the managed code is powered by Connection Manager hidden under the surface can be useful for resolving complex connection issues.

PDP Contexts

Much of the network complexity is because mobile operators must tread a narrow path among three competing requirements. First, they have customer demand to match the blistering pace of change in radio network technologies. Second, they provide radio coverage to huge geographical regions through a vast infrastructure. And third, they try not to break the bank for their shareholders.

One area that highlights the challenge and directly affects software developers is related to Packet Data Protocol (PDP) contexts. A PDP context describes an agreement between a phone handset and the Global System for Mobile Communication (GSM) network and contains important information, such as the Access Point Name (APN) and IP address. This agreement is immutable—once created, it can't be changed. (A more complete explanation can be found in the GPRS Core Network article on Wikipedia.)

Before any application can send and receive data, the device must first establish a PDP context by making a request to the base station, sending the name of the desired APN. The request is typically forwarded into the mobile operator's billing network to authorize the request, making sure the user has a valid contract and sufficient available credit, for example. Upon completion, an IP address is supplied by the APN and handed to the base station and on to the handset. At this point, the device can send and receive data under the assigned IP address.

Surely one PDP context is enough? Not always—APNs provide a convenient way to vary the billing or traffic restrictions in different ways. For example, a specific APN might provide a direct bridge to an enterprise's private network, restricting access to the APN via the operator's billing systems, or one APN might be used for per-megabyte billing and another for an "all you can eat" service. This practice is widespread. A real-world example is for a mobile operator to require all push e-mail data to go via a specific APN and other Internet traffic through another.

As I said earlier, the PDP request typically routes through the billing infrastructure, and that can take some time. It varies between operators and networks but can take up to 20 seconds to create the context. Remember, PDP contexts are immutable, so the APN cannot be dynamically swapped. Instead, the whole context must be dropped and a new one created.

For performance alone, multiple concurrent PDP context support would be a good thing. But as you would imagine, this requires extra handset and base-station capabilities. Fortunately, most modern handsets support three or more contexts, but it's a little more interesting when it comes to base stations. Although most new installations support this capability, there are still a good number of first-generation 3G base stations in use that only support single PDP contexts per handset.

That all leads to quite a complex set of rules to ensure each application is connecting in the right way—and the right way may frequently change as the connected base station and radio service level changes.

Modeling the Network Topology

Connection Manager is primarily designed to do three things:

  • Be sufficiently flexible and configurable so mobile operators and OEMs can describe their many and varied network topology and device capabilities.
  • Reduce and simplify the steps a developer must take to select and connect to network resources on any handset in any network.
  • Provide arbitration among applications contending for limited resources.

Considering the complexities of PDP contexts I described earlier and that Connection Manager extends its support beyond General Pocket Radio Service (GPRS) to include WiFi, dial-up, wired, and even desktop pass-through connections, this is not an easy target to aim for!

The first step is to give Connection Manager a complete picture of the available connections, modeled in a sufficiently abstract manner to encompass emerging technologies that become mainstream during the system's lifetime. Connection Manager provides a suite of simple and consistent configuration primitives that allow mobile operators and OEMs to model their hardware and network topology, all of which are configurable via Open Mobile Alliance (OMA) Device Management, OMA Client Provisioning, or, in some cases, through the settings user interface.

Here is a simple OMA Client Provisioning XML sample to give you an idea of what it looks like:

<wap-provisioningdoc>
  <characteristic type="CM_GPRSEntries"> 
    <characteristic type="Internet GPRS"> 
      <parm name="DestId" value="{436EF144-B4FB-4863-A041-8F905A62C572}" /> 
      <characteristic type="DevSpecificCellular"> 
        <parm name="GPRSInfoValid" value="1" /> 
        <parm name="GPRSInfoAccessPointName" value="MyInternetAPN" /> 
      </characteristic> 
    </characteristic> 
</wap-provisioningdoc>

The basic components start with the meta-network. This is the most abstract concept that Connection Manager uses and can't really be mapped to anything truly tangible, such as a network card or a computer. However, these entries are considered the endpoints or nodes of the network model, often given descriptive names, such as the Internet, DMZ, or Walled Garden, and are identified by GUIDs. There is a predefined list of commonly used destinations, such as "Internet" and "work," published in the MSDN Library and deployed on all devices. However, the list is fully extensible. CM_Networks is the OMA Client Provisioning configuration name when provisioning.

GPRS and other packet data connections are described in their own configuration section that includes relevant information for connecting, such as the APN name, user/password information, and many other settings. These settings must also contain the ID of a meta-network that is available when the connection is established—this is called the destination meta-network. There is no source network for packet data entries because it must always be the device. CM_GPRSEntries is used for OMA Client Provisioning.

There are separate entries for WiFi, dial-up, and other hardware that can be managed by Connection Manager. VPN and proxy entries both have their own configurations but are slightly different because they provide a connection from one meta-network to another—there is a source meta-network and a destination meta-network.

I need to add one more very important configuration setting: the mapping table (CM_Mappings is used for OMA Client Provisioning). Strictly speaking, it doesn't model anything about the network topology; instead, this table is for applications to discover to which meta-network they need to connect. An application is likely to know the URL that it needs but has no clue which meta-network is able to access that URL because it changes from operator to operator, device to device.

The mapping table defers the choice of meta-network to the mobile operator or OEM via an API that converts the provided URL into a meta-network GUID. Without the mapping table, the destination meta-network would either be hardwired or provided by the user, which brings us back to the old issue of relying on the user.

Figure 1 shows an example of how the network topology might be modeled and a corresponding mapping table. The mapping table is an ordered list of URL patterns, each paired with a meta-network GUID and interrogated by the ConnMgrMapURL API. An application passes its target URL to ConnMgrMapURL, which walks through the table starting at the lowest ID looking for the first pattern match with the URL. If a match is found, the GUID is returned and can be handed to ConnMgrEstablishConnection to make the connection.

fig01.gif

Figure 1 Example Network Topology (Click the image for a larger view)

Figure 2 shows an example of what the mapping table might look like in XML form. It shows six mapping entries, 501 being the first. It also shows most of the pattern match possibilities. The pattern isn't quite a regular expression, but it provides a good range of flexibility. * is used as the wild character. So, for example, *://*.operator.*/* will map to any protocol—any address that contains the text pattern .operator. followed by a / and any trailing page name. The following URLs would match this destination network:

https://www.operator.com/
rtsp:// rtsp.operator.media.com/20070707_ABHDD3227DD/today_news1.sd
https://my.long.name.operator.da/index.aspx

Figure 2 Example of XML for Mapping Table

<wap-provisioningdoc>
  <characteristic type="CM_Mappings">
    <characteristic type="501">
      <parm name="Pattern" value="*://*/*.3gp"/>
      <parm name="Network" value="{D3B2D798-9E69-4B65-A75B-6DDFBECEAAAA}"/>
    </characteristic>
    <characteristic type="610">
      <parm name="Pattern" value="*://*.operator.*"/>
      <parm name="Network" value="{7022E968-5A97-4051-BC1C-C578E2FBA5D9}"/>
    </characteristic>
    <characteristic type="536870912">
      <parm name="Pattern" value="wsp://*/*"/>
      <parm name="Network" value="{7022E968-5A97-4051-BC1C-C578E2FBA5D9}"/>
    </characteristic>
    <characteristic type="553648128">
      <parm name="Pattern" value="wsps://*/*"/>
      <parm name="Network" value="{F28D1F74-72BE-4394-A4A7-4E296219390C}"/>
    </characteristic>
    <characteristic type="570425344">
      <parm name="Pattern" value="*://*.*/*"/>
      <parm name="Network" value="{436EF144-B4FB-4863-A041-8F905A62C572}"/>
    </characteristic>
    <characteristic type="587202560">
      <parm name="Pattern" value="*://*/*"/>
      <parm name="Network" value="{A1182988-0D73-439E-87AD-2A5B369F808B}"/>
    </characteristic>
  </characteristic>
</wap-provisioningdoc>

The last four entries are the default entries found on Windows Mobile Standard, and the last two entries identify Internet (with . in the address) and work traffic, respectively.

Sure, this is ideal for general-purpose applications, such as a browser or media player where many and varied URLs are input from the user, but isn't it overkill for an application with just one URL? Even for the one-URL scenario, I would strongly encourage you to make use of the mapping table each time you connect because that's the only way to get the right meta-network in every network configuration the world over.

Making Connections

With a model of the network, Connection Manager can do its magic. There is little value in regurgitating the API documentation here—the Connection Manager API documentation in the MSDN Library does a much better job of explaining. Instead, I think it's more valuable to highlight the normal sequence of APIs and call out key settings.

To make a connection, the sequence is usually this:

  1. Call ConnMgrMapUrl for the meta-network to connect to.
  2. Prepare the CONNMGR_CONNECTIONINFO structure that describes what you want Connection Manager to do.
  3. Call ConnMgrEstablishConnection (there is also a synchronous version of this API) to create the request.
  4. Monitor Connection Manager messages and track any status changes that may occur.
  5. When you are finished with the connection, call ConnMgrReleaseConnection to close the request and allow resources to be reallocated.

Figure 3 shows a simplified example of the API sequence for connecting. There are several bits of the CONNMGR_CONNECTION­INFO structure that are important and often set incorrectly (see the CONNMGR_CONNECTIONINFO article on MSDN Library for details of the full structure). I will explain some of the important pieces here:

DWORD dwFlags There are several flags developers need to be aware of. For example, the Proxy Aware options tell Connection Manager with which types of proxy server your code can communicate. Based on these flags, it will include or exclude certain meta-networks available via those types of proxy server. Wherever possible, you should write proxy-aware code (HTTP proxy is usually enough). It's not difficult! For native C++ code, make use of the InternetOpen or InternetOpenURL WinInet APIs and it's all pretty much done for you. Managed code is even easier, as many of the .NET Compact Framework managed classes (HttpWeb­Request, for example) already support proxy servers.

DWORD dwPriority Priority setting is very important because this is the primary way Connection Manager decides how to share scarce connection resources. I'll expand on this later.

BOOL bExclusive This setting can be used to stop Connection Manager from sharing the network connection with other applications, even for the same destination meta-network. It might be useful as security mitigation but should be employed with care.

Figure 3 Example Code for Using Connection Manager

#include <windows.h>
#include <Connmgr.h>
#include <Wininet.h>

HRESULT ConnectionManager_Connect() {
  HRESULT hr = S_OK;
  GUID dest;
  CONNMGR_CONNECTIONINFO connInfo;

  // look up the URL and connect to the specified destination
  hr = ConnMgrMapURL(g_urlString, &dest, NULL);
  if (SUCCEEDED(hr)) {
    memset(&connInfo, 0, sizeof( connInfo));
    connInfo.cbSize = sizeof( connInfo );
    connInfo.bDisabled = FALSE;
    connInfo.bExclusive = FALSE;
    connInfo.hWnd = g_hWnd;
    connInfo.guidDestNet = dest;
    connInfo.dwParams = CONNMGR_PARAM_GUIDDESTNET;
    connInfo.dwFlags = CONNMGR_FLAG_PROXY_HTTP;
    connInfo.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;
    connInfo.uMsg = WM_USER+101;    // Message callback.
    hr = ConnMgrEstablishConnection(&connInfo, &g_ConnHandle);

    if (!SUCCEEDED(hr)) {
      LogMessage( TEXT("Failed to Establish connection") );
    }
  }

  return hr;
}

HRESULT ProcessConnectionManagerMessage() {
  HRESULT hr = S_OK;
  DWORD status = 0;

  // Connection Manager status change
  hr = ConnMgrConnectionStatus( g_ConnHandle, &status);

  if (SUCCEEDED(hr)) {
    switch (status) {
      case CONNMGR_STATUS_CONNECTED:
        // Do something useful here…
        // Done with the connection—tear it down.
        ConnMgrReleaseConnection(g_ConnHandle, 1);
        g_ConnHandle = NULL;
        break;

      case CONNMGR_STATUS_NOPATHTODESTINATION:
      case CONNMGR_STATUS_CONNECTIONFAILED:
      case CONNMGR_STATUS_CONNECTIONCANCELED:
      case CONNMGR_STATUS_CONNECTIONLINKFAILED:
      case CONNMGR_STATUS_CONNECTIONDISABLED:
      case CONNMGR_STATUS_AUTHENTICATIONFAILED:
        // Non transient error state.
        ConnMgrReleaseConnection(g_ConnHandle, 1);
        g_ConnHandle = NULL;
        break;

      default:
        // These states will eventually end up at as connected 
        // or not. Just ignore for now.
        break;
    }
  }
  return hr;
}

LRESULT CALLBACK WndProc(
  HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {

  switch (message) {
    case WM_USER+101:
      ProcessConnectionManagerMessage();
      break;

    case WM_COMMAND:
      if (IDM_GET == LOWORD(wParam)) {
        ConnectionManager_Connect();
      }
      break;

    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0;
}

Calling ConnMgrEstablishConnection creates an internal connection request (CR) for tracking the life of the connection. The CR is released when ConnMgrReleaseConnection is called.

Here is a summary of the steps Connection Manager runs through when processing a CR. First, Connection Manager verifies that a connection path can be found to the destination meta-network. Connection Manager examines the CR and, using the network model (GPRS, WiFi, VPN, or whatever else might be available, along with the proxy details), it selects the best set of connectors that span to the destination meta-network.

Connection Manager isn't limited to just one connector. For example, in order to get to the Work meta-network it might need a GPRS connection plus a VPN connection. Connection Manager might also choose different paths depending on the current connection state of the device. For example, if the device is not currently connected, then a direct GPRS connector may be best. However, if the device is currently connected to a Work network, then simply using a proxy server may be sufficient. If Connection Manager is unable to find an appropriate path to the destination, the application is notified and no further steps are taken.

Next, the CR's state transition journey begins. Every CR will travel through a number of transient states before ending up eventually at one of the five or so closed states (connected state is also transient). The requesting application will receive windows messages when state changes occur. Once a CR reaches a closed state, it won't change again. So if the application still needs to be connected, it should close the CR and create a new CR.

A picture is worth a thousand words, and the best Connection Manager state transition diagram I've seen so far is on Adam Dyba's blog.

Connection Requests and Arbitration

Just because you created a new CR doesn't mean it will immediately get connected. Arbitration takes place: CRs are ordered by request time within priority order, then resources are allocated from the top of the list down so that the highest priority, most recently requested CR gets first pickings and the lowest priority, oldest CR is last in the list.

Arbitration is triggered not only by new CRs but also by changing a CR priority, releasing a CR, and by events from network resources, such as changing base station.

Voice calls also use Connection Manager and use a reserved highest priority flag, CONNMGR_PRIORITY_VOICE, that overrides all other CRs. When there is a voice connection, connected CRs that share the voice resource can be placed in a suspended state for the duration of the voice call, then automatically reconnected when the voice CR has been released.

Network complexities, such as PDP contexts, can have an impact on arbitration. If only one context is available, then just the top CR in the list will get connected—other CRs will go to the WAITINGFORRESOURCE state. It's actually a little more complex because any lower priority CRs for the same meta-network may also enter the CONNECTED state depending on the exclusive settings in each CR.

With multiple PDP contexts, Connection Manager will connect a number of CRs starting at the top of the list. It's important to remember, as stated previously, that new CRs always take priority over existing CRs of the same priority.

Think about this for a second—there is a very nasty trap here that unwary developers can easily fall into. Let's say for a moment that I have an always-connected application and my application requires connection to a private APN with no Internet access. My application is running in the background, and its CR is connected so data merrily flows back and forth to the server.

Additional Resources

Connection Manager Documentation in MSDN

Connection Manager API

Write an IM App with the .NET Compact Framework

Accessing Data from a Mobile Application

Presently the user fires up a Web browser to see how well her stock portfolio is doing. To access general Internet content, another unrestricted APN connection is required, so the browser issues a CR for a new destination meta-network. With multiple PDP contexts, the second CR would connect and quite happily run side-by-side. However, with a single PDP context, the later CR will supersede my application's request and Connection Manager will close my CR to connect the browser.

Say I wrote my application to be resilient to connection failures, knowing that the user is frequently on the road. When my original CR is closed, my application immediately requests a new one. Because it's at the same priority and newer than the browser CR, it will immediately win back the resource and my application gets connected once again. This all happens very quickly, so the browser won't get to a connected state and is likely to report an error to the user. It's unclear to the user why the browser failed, so she tries again, going round the loop but never actually getting connected.

Now imagine instead of a browser you have another background application. It's more than likely that you just created a denial-of-service situation as the applications battle to stay connected, but neither one can.

What's the application doing wrong? Most likely it's not setting the right connection priority: USERINTERACTIVE is overused and should only be set when the user has directly caused the connection to be established—like a browser page request. Later, if the application is no longer user interactive—for example, a new application window is displayed over yours—ensure the priority of your CR is changed to something lower, such as USERBACKGROUND, using the ConnMgrSetConnectionPriority priority API. This makes race conditions less likely.

Additionally, it may be appropriate to implement back-off logic so that, when the connection is dropped, a new CR isn't immediately raised. Instead, a delay related to the number of consecutive failures is introduced before trying again.

The final step in setting up connections is for Connection Manager to disconnect demoted CRs and connect new ones. Connection Manager first drops released resources before connecting new requests. If no new request is required when a CR is dropped, then Connection Manager has the ability to immediately disconnect or hold the connection cached for a configurable period of time, which can be useful for avoiding reconnect costs.

You may have spotted an interesting challenge that comes about with multiple PDP support—when multiple PDPs are connected, the device has multiple IP addresses for the same adapter, one from each context. How can applications use the right IP address for the right network?

This is a little more magic from Connection Manager. When a CR becomes connected, the IP address from the PDP context is used by the socket library code for all subsequent automatic socket bindings in that process. Notice this is done at process boundary granularity and, as the documentation states, Connection Manager supports only one CR per process. The API doesn't stop you from making more than one CR from a process, but if you do, make very sure all CRs resolve to the same PDP context and subsequent IP address. Otherwise, the behavior of Connection Manager is undetermined.

Using the SDK Sample Code

It's worth pointing out that the SDK sample code for Connection Manager is intended as exactly that—sample code to show the capabilities of the feature. When moving from the sample to production code, be sure to update the priority handling correctly and update the state change handling to process transient states correctly. States like WAITINGFORRESOURCE or WAITINGFORNETWORK are intended as hints so that visual progress or status can be correctly shown to the user.

The sample code treats almost all transient states as failures and destroys the CR. Production code needs to be more resilient to these intermediate states and only consider the connection failed when it's reached a non-transient state.

Connection Manager is a powerful and often underestimated technology. I would encourage you to take another look at how you use it to make sure all applications play well together on even the most obscure network configuration.

Send your questions and comments to goplaces@microsoft.com .

Marcus Perryman has worked at Microsoft for 11 years in various technical roles, including Developer Evangelist and Developer Consultant. At present, Marcus works as a Software Design Engineer in the Windows Mobile product group designing and developing the next generation of mobile operating systems.