Detecting Network Connectivity Using D-Bus

Preface


The Intel® Mobile Internet Device (MID) platform provides a full internet experience in a pocket-sized form factor. Combining Moblin-based operating systems with the Intel® Atom™ processor, MIDs are able to run any application that has been built for the x86 architecture, including Adobe Flash 10* and Adobe AIR 1.5*. While the features of the devices that are and will be on the market vary depending on OEM and target market, there are several features that the devices share in common; small form factor, emphasis on internet connectivity rather than extensive storage, and alternate input methods.

Although there are ultra-mobile computing devices in the market that run Microsoft Windows XP* or Windows Vista* and that are built on the same hardware platform as those running a Moblin-based operating systems such as Midinux* or Ubuntu* Mobile Edition, this series of whitepapers focuses entirely on Moblin-based MIDs. For information on how to retrieve this information from Windows operating systems, please consult that platform’s documentation, or see the Intel® Mobile Platform SDK.

The functionality described in this paper is also provided via the Platform Awareness Service, a light-weight, D-Bus initiated platform information provider. However, because the final software stack for each device is influenced heavily by the OEM and Service Providers, this service may not be pre-installed on some Moblin-based devices. We have therefore written these whitepapers in order to simplify access to this information on platforms that do not have this service installed.

For further information on how to use D-Bus to retrieve information from the Platform Awareness Service, please refer to the Platform Awareness Service documentation.

Introduction


With the advances of computer networking within the last decade, devices can be connected via a myriad of technologies and protocols, wired and wireless. This connectivity has become so prevalent that many applications can’t function without it. Some application functions take advantage of network communications such as email, network gaming, web browser, etc. Some real-time applications, especialy networking games, require a large bandwidth and high network reliability. These applications must be aware of the network connectivity. Mobile computing devices such as a laptop or Mobile Internet Device (MID), frequently use wireless technology and thus network awareness becomes even more important. Instead of letting each application write its own connectivity awareness code, we propose a uniform way to convey the connectivity information to applications. Laptop/MID applications can then refer to our system to get information on network connectivity and make proper decisions at run time.

As part of a whole project, MID Platform Awareness, this paper shows how we can call connectivity awareness methods and get event notifications.

The system was developed on Moblin[4], a Linux* platform, and uses D-Bus communication[1] to transport information to users [1]. Applications which want to get connectivity information need to use D-Bus as well. In the following sections, we briefly describe D-Bus and how we implement network connectivity awareness service using D-Bus. We show examples of how an application can retrieve the network connectivity information; (1)whether or not the device is connecting to a network, (2)whether or not the device can reach a given website, (3)the latency of the network when the device does connect, (4)the data rate involved when the device reaches a website, (5)the media type of the current network, and (6)the link speed of the network.

Background on D-Bus


D-Bus is a fast, lightweight message system which allows applications to communicate one on one or to broadcast messages to subscribers, (inter-process communication). [2]. D-Bus can be used as low-level API or via a higher level binding, such as Qt, Python*, Java*, C#, or Perl. This paper shows an application written in C using Glib bindings. Glib is the base library of GNOME and provides an object-based, event-driven environment.

Two buses are defined in D-Bus: the system bus and the session bus. The system bus allows communication between an application and the operating system while the session bus is designed to allow communication between two applications.

In order to use D-Bus, we need to understand a few basics. First, an object is an endpoint on the bus that is created by an application in the context of that application’s connection to the bus. Objects have names, and these names are called object paths. We can find an object via its path. A proxy allows clients to reference objects on the bus. Once we find an object, we usually keep a proxy to that object so we can subsequently refer to that object without searching again.

An object can perform specific operations, referred to as methods. Thus, a client can send a request to an object and ask the object to invoke a method. The object then executes the method (if the method exists) and the result is sent back to the client. If a method requires input parameters, these parameters are passed with the request. The result can be one or more output parameters, which are sent back to the client in the reply message. At the D-Bus layer, this method invocation/message passing sequence occurs asynchronously; the glib wrapper for the D-Bus object proxy allows the methods to be called either synchronously or asynchronously.

An object can also emit an event, or signal. When a signal is generated by an object, it is broadcasted to any interested observers. Signals can also carry parameters. Methods and Signals are embedded members of an object that can be grouped into an interface. An object can declare one or many interfaces.

In order to generate dbus-glib binding code, we use a tool called dbus-binding-tool. We first create an XML file, referred to as an Introspection XML file, in which we describe the methods and signals. Defining the interface this way ensures that clients can discover and introspect our D-Bus service. An example of the introspection XML is shown below:

[xhtml]<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-Bus Object Introspection 1.0//EN" "http://standards.freedesktop.org/dbus/1.0/introspect.dtd"> <node name="/org/moblin/Platform"> <interface name="org.moblin.Platform.Connection"> <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="org_moblin_platform_connection"/> <method name="IsConnected"> <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="isConnected"/> <arg type="b" name="isConnected" direction="out" /> </method> <method name="IsReachable"> <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="isReachable"/> <arg type="s" name="URI" direction="in" /> <arg type="b" name="isReachable" direction="out" /> </method> <method name="GetLatency"> <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="getLatency"/> <arg type="s" name="URI" direction="in" /> <arg type="d" name="seconds" direction="out" /> </method> <method name="GetDataRate"> <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="getDataRate"/> <arg type="s" name="URI" direction="in" /> <arg type="d" name="KiloBytesPerSecond" direction="out" /> </method> <method name="GetMediaType"> <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="getMediaType"/> <arg type="s" name="mediaType" direction="out" /> </method> <method name="GetLinkSpeed"> <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="getLinkSpeed"/> <arg type="d" name="kpbs" direction="out" /> </method> <signal name="Connected" /> <signal name="Disconnected" /> </interface> </node>[/xhtml]
In this introspection XML file, an interface called org.moblin.Platform.Connection defines six methods:
IsConnected, IsReachable, GetLatency, GetDatRate, GetMediaType and GetLinkSpeed. The annotation XML tags tell dbus-binding-tool what the name of the implementing C function should be. For example, IsConnected is the name of the method that will be visible to users over D-Bus. Internally, we need to implement a C function called isConnected. Method parameters and return values are both specified using the arg XML tag. The direction attribute for a parameter is "in"; for return values, the direction attribute is "out". Note that parameters and return values both have name attributes.

The type attribute of the arg XML tag indicates the data type of the parameter or return value. For example, the value "b" in the IsConnected method return value indicates that the method returns a Boolean. The second method, IsReachable, takes a string parameter representing the target URI (Uniform Resource Identifier) and returns a Boolean value. The next two methods, GetLatency and GetDataRate, also take a string parameter representing the URI and return a double representing seconds and kilobytes per second, respectively. The output of GetMediaType is a string. Finally, the output of the last method, GetLinkSpeed, is a Double data type, representing the number of kilobits per second.

Two signals are defined in the introspection XML file. The signal "Connected" is generated when the system changes state from having no connection to having at least one connection. The "Disconnected" signal is generated when the when the system changes state from having at least one connection to having no connections.

Below are primary steps to implement a D-Bus service. For more details, look here.

  • Run dbus-binding-tool for an introspection XML file to generate dbus-glib binding code
  • Acquire the D-Bus GObject wrapper
  • Implement the instance initialization
  • Implement the methods
  • Register the object on the D-Bus

To call the methods implemented on a remote object, a client needs to follow the following steps:

  • Connect to the bus
  • Create a proxy object
  • Invoke the methods on the proxy object.

The connectivity awareness service below uses D-Bus as a communication channel to send information to clients regarding network connectivity. Clients can thus use D-Bus to get connectivity information by querying the methods provided by the connectivity service. Client applications can also listen for the signals that the connectivity awareness service generates. The methods and events mentioned in the introspection file are implemented by the connectivity awareness service.

Getting Network Connectivity Information


The connectivity awareness service provides the means to query the following connectivity information on the computer where the service is installed.

Detecting the network connection

A networked application obviously requires an established network connection. Before allocating resources for a network transaction, it is useful to know whether or not the system has an established network connection. Therefore, the network connectivity service provides a method called IsConnected, which returns a Boolean value confirming the status of the network. The network connectivity service checks the status of all network devices on the computer. If at least one network device has an established connection, the Boolean value TRUE is returned. If all network devices on the computer are not running, the Boolean value FALSE is returned. To query the connectivity, the application first must bind to D-Bus service as shown in the following example

#include <dbus/dbus.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <dbus/dbus-glib-bindings.h> 

#define PLATFORM_SERVICE          "org.moblin.Platform"
#define PLATFORM_PATH                    "/org/moblin/Platform"
#define PLATFORM_CONNECTION_IF    "org.moblin.Platform.Connection"

int main(int argc, char** argv)
{
       DBusGConnection* conn;
       GError *error;
       DBusGProxy *platformProxy;

       g_type_init();
       error = NULL;
       conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);

       // Create the proxy Platform
       platformProxy = dbus_g_proxy_new_for_name(conn,
              PLATFORM_SERVICE,          // target for the method call
               PLATFORM_PATH,                   // object to call on
               PLATFORM_CONNECTION_IF);  // interface to call on

      gboolean result;

      if (!dbus_g_proxy_call(platformProxy, "IsConnected", &error, 
                            G_TYPE_INVALID,
                            G_TYPE_BOOLEAN, &result,
                            G_TYPE_INVALID))
      {
              g_printerr("Failed to call remotely: %sn",
                                error->message);
              g_error_free(error);
      }
      else
              printf ("Remote call successes with returned value: %sn", result? "TRUE" : "FALSE");

       . . . . . . . . . . . . . . . . . . . . . .
       . . . . . . . . . . . . . . . . . . . . . .
       . . . . . . . . . . . . . . . . . . . . . .

       return 0;
}


Note that the application invokes the method IsConnected implemented in the connectivity awareness service: no input is required, and the method returns TRUE if the computer is connected to a network.

The back-end implementation of this method basically computes the list of network adapters available, and confirms the connection status if there is at least one network adapter running. The function detecting whether or not a network adapter is running, is shown below

[c-sharp]netdev_ret netdev_get_running(netdev_dev *dev, // in
netdev_running *running) // out
{
struct ifreq ifr;
if(_get_ifreq(dev->ifname, SIOCGIFFLAGS, &ifr) != NETDEV_SUCCESS)
{
running = RUNNING_UNKNOWN;
return NETDEV_ERROR;
}

short flags = ifr.ifr_flags;
if(flags & IFF_RUNNING)
{
*running = RUNNING_RUN;
}
else
{
*running = RUNNING_STOP;
}

return NETDEV_SUCCESS;
}
[/c-sharp]

Detecting HTTP reachability


The second method queries the reachability of a specified website. When getting this request, the connectivity service forms a HTTP GET message[5] and sends it to the specified address (web address) at port 80 (the typical port for HTTP protocol). The socket timeout is set to 3 seconds in this implementation: the service will wait for a HTTP response with status 200[5] before disconnecting the socket. This method, IsReachable, requires a string parameter as the target address (web address) and returns a Boolean value confirming whether or not the web address can be reachable within the socket timeout. The format of the valid entry takes the protocol (http in this case) follows it with the URI. Examples of valid inputs are shown below

"http://www.intel.com/", "http://www.google.com/", "http://www.yahoo.com/", etc ...

The third method, GetLatency, computes the time delay between sending a HTTP GET message and receiving the reply. This method takes an input parameter which is the URI of the website and returns a value of type double, representing the time in seconds. If the socket timeout expires, this method returns a null value.

The fourth method, GetDataRate, calculates the data rate when sending a HTTP GET message and receiving the reply. This method takes an input parameter which is the URI of the website and returns a value of type double representing kilobyte per second (kBps). If the socket timeout expires, this method returns a null value. Data rate is computed using the following formula:

(length of HTTP GET sent + length of HTTP reply) / latency

Code referring to these D-Bus methods is shown below.

              int result1;

      if (!dbus_g_proxy_call(platformProxy, "IsReachable", &error,
                            G_TYPE_STRING, "http://www.intel.com/",
                            G_TYPE_INVALID,
                            G_TYPE_BOOLEAN, &result1,
                            G_TYPE_INVALID))
      {
              g_printerr("Failed to call remotely: %sn",
                     error->message);
              g_error_free(error);
      }
       else
                printf ("Remote call successes with returned value: %sn", result1? "TRUE" : "FALSE");

      gdouble result2;

      if (!dbus_g_proxy_call(platformProxy, "GetLatency", &error,
                            G_TYPE_STRING, "http://www.intel.com/",
                            G_TYPE_INVALID,
                            G_TYPE_DOUBLE, &result2,
                            G_TYPE_INVALID))
      {
              g_printerr("Failed to call remotely: %sn",
                     error->message);
              g_error_free(error);
      }
      else
              printf ("Remote call successes with returned value: %6.3f secondsn", result2);


      if (!dbus_g_proxy_call(platformProxy, "GetDataRate", &error,
                            G_TYPE_STRING, "http://www.intel.com/",
                            G_TYPE_INVALID,
                            G_TYPE_DOUBLE, &result2,
                            G_TYPE_INVALID))
      {
              g_printerr("Failed to call remotely: %sn",
                     error->message);
              g_error_free(error);
      }
              else
                     printf ("Remote call successes with returned value: %8.2f KB/secn", result2);



The back-end implementation of the above three methods shares a common function: this function parses the input URI and then searches for the URI reachability using HTTP protocol. The parsing function validates the URI and retrieves the target IP address. The reachability function forms a HTTP GET message and sends to the target IP address at port 80. The reachability is confirmed if a HTTP reply message is received within a timeout limit. Readers are encouraged to refer to the source code of the MID Platform Awareness project for the implementation. For illustration, only the reachability function is shown below

bool HTTPIsReachable(
              unsigned int *pTime,       // in msec
              double *pRate,             // in KB/sec
              PURI_INFO pUri)
{
              bool bRet = false;
              unsigned long StartTime, StopTime;
              int dwStatusCode;
              int conn;
              int Len = 0;

              if (pTime)
                     *pTime = 0;

              if ( pUri->Port == 0 )
                     pUri->Port = HTTPPORT;

              StartTime = getTime();
              if (pUri->DestPath)
                        conn = (int)socketClient( pUri->pHostIP, pUri->Port, 0);

                if (conn < 0)
              {
                     printf("conn<0n");
                       goto GracefulExit;
              }

              char req[1024];
              sprintf( req, "GET /%s HTTP/1.0rnHost: %srn", pUri->FileName, pUri->DestPath);
               strcat( req, "User-Agent: hget/"  LIBHTTP_VERSION "rn");
                     strcat( req, "Pragma: no-cachern" );
              strcat( req, "Accept: */*rnrn" );
              dwStatusCode = HttpRequest(conn, &Len, req);    // in Bytes
              close(conn);

              StopTime = getTime();

              printf("return code %dn", dwStatusCode);
              if ( dwStatusCode == 200 || pUri->FileName == 0 )
              {
                     bRet = true;
                     if (pTime)
                     {
                           unsigned int st;
                           st = StopTime - StartTime;                // in msec
                           *pTime = st;
                           *pRate = (double)Len / (double)st;              // in KB/sec
                     }
              }

       GracefulExit:
              return bRet;
}



Detecting the media and speed of the network connection

The last two methods, GetMediaType and GetLinkSpeed, return the network media type and the link speed of the network. In the current implementation, only Ethernet and WiFi are supported. The output of GetMediaType is a string (i.e., "802.03", "802.11g"… etc) and the output of GetLinkSpeed is a value of type double, representing the number of kbps (kilobits per second).

      char *result3;

      if (!dbus_g_proxy_call(platformProxy, "GetMediaType", &error,
                            G_TYPE_INVALID,
                            G_TYPE_STRING, &result3,
                            G_TYPE_INVALID))
      {
              g_printerr("Failed to call remotely: %sn",
                     error->message);
              g_error_free(error);
      }
      else
              printf ("Remote call successes with returned value: %sn", result3);

      double result4;

      if (!dbus_g_proxy_call(platformProxy, "GetLinkSpeed", &error,
                             G_TYPE_INVALID,
                             G_TYPE_DOUBLE, &result4,
                             G_TYPE_INVALID))
      {
              g_printerr("Failed to call remotely: %sn",
                     error->message);
              g_error_free(error);
      }
      else
              printf ("Remote call successes with returned value: %10.1f Kbps (kbit per sec)n", result4);



The back-end implementation of the method GetMediaType is shown below.

char* RetrieveMediaType(void)
{
   netdev_dev netdev_list[MAX_DEVICE];
   int numInterface = 0;

   if(netdev_get_devices(netdev_list, &numInterface) == NETDEV_SUCCESS)
   {
       int i;
       netdev_dev *net_devPtr = netdev_list;

       for (i=0; i= Frequency802_11a1) && 
                                      (freq <= Frequency802_11a2) && 
                                      (rate <= MaxRate802_11a))
                                         return ("802.11a");

                                  if ((freq >= Frequency802_11b1) && 
                                       (freq <= Frequency802_11b2) && 
                                      (rate <= MaxRate802_11b))
                                         return ("802.11b");

                                  if ((freq >= Frequency802_11g1) && 
                                      (freq <= Frequency802_11g2) && 
                                      (rate <= MaxRate802_11g))
                                         return ("802.11g");

                                  return ("Unknown");
                                  break;
                           }

                           default:
                                  return ("Unknown");
                                  break;
                        }

                     }

                     else
                        return ("Unknown");

              }
          }
          net_devPtr++;
       }

   }

   return ("Unknown");
}



The back-end implementation of the method GetLinkSpeed is shown below.

 double RetrieveLinkSpeed(void)   // in bps
{
   netdev_dev netdev_list[MAX_DEVICE];
   int numInterface = 0;

   if(netdev_get_devices(netdev_list, &numInterface) == NETDEV_SUCCESS)
   {
       int i;
       netdev_dev *net_devPtr = netdev_list;

       for (i=0; i



Readers are encouraged to refer to the source code of the MID Platform Awareness project for more details.

Monitoring Network Connectivity Change


An application can query the network connectivity information by invoking the methods above. However, instead of constantly invoking the method to query the network connectivity status, sometimes it is more practical that an application just subscribe to network events, or signals. The application will be notified whenever the network connectivity changes status. Two network events are defined: Connected and Disconnected. The Connected event is emitted by the network connectivity service when the network suddenly becomes available. Similarly, the Disconnected event is emitted by the network connectivity service when the network suddenly becomes unavailable. The following code shows how to use D-Bus signals for monitoring network connectivity.

       DBusMessage* msg;
       DBusConnection* conn;
       DBusError err;

       printf("Listening for signalsn");

       // initialise the errors
       dbus_error_init(&err);

       // connect to the bus and check for errors
       conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
       if (dbus_error_is_set(&err)) 
       { 
              fprintf(stderr, "Connection Error (%s)n", err.message);
             dbus_error_free(&err); 
       }

       if (NULL == conn) 
              exit(1);

       dbus_bus_add_match(conn, "type='signal',interface='org.moblin.Platform.Connection'", &err);
       dbus_connection_flush(conn);

       if (dbus_error_is_set(&err)) 
       { 
              fprintf(stderr, "Match Error (%s)n", err.message);
              exit(1); 
       }

       printf("Match rule sentn");

       // loop listening for signals being emmitted
       while (true) 
       {
              // non blocking read of the next available message
             dbus_connection_read_write(conn, 0);
              msg = dbus_connection_pop_message(conn);

             // loop again if we haven't read a message
              if (NULL == msg) 
              { 
                     sleep(1);
                     continue;
              }

              if (dbus_message_is_signal(msg, PLATFORM_CONNECTION_IF, CONN_CONNECTED_SIGNAL)) 
                     printf("Received signal %sn", CONN_CONNECTED_SIGNAL);

              if (dbus_message_is_signal(msg, PLATFORM_CONNECTION_IF, CONN_DISCONNECTED_SIGNAL)) 
                     printf("Received signal %sn", CONN_DISCONNECTED_SIGNAL);

              // free the message
              dbus_message_unref(msg);
       }



Conclusion


D-Bus is a lightweight Remote Procedure Call (RPC) which is suitable for many applications running on a desktop. Because D-Bus is simple and relative small, yet powerful, we can adapt this technology for Mobile Internet Devices (MID). In this paper, we introduced a platform service for MID using D-Bus technology. We briefly described the six connectivity-related methods and two signals provided by the MIDPlatformSvc.

Although this implementation has been tested on the Moblin stack, this technology should also work on a Windows platform as well: therefore, with minimum change, this application should work on other form factors like UMPC, which run Windows* instead.

References


[1] D-Bus, http://www.freedesktop.org/wiki/Software/dbus
[2] D-Bus Tutorial, http://dbus.freedesktop.org/doc/dbus-tutorial.html
[3] http://www.moblin.org/toolkits/basicDevGuides/mobLinux/toolkits_DevGds_mobLinux_createDBUS.php
[4] Mobile and Internet Linux Project, http://moblin.org/
[5] RFC 1945 "Hypertext Transfer Protocol – HTTP/1.0"

For more complete information about compiler optimizations, see our Optimization Notice.
Categories: