July 31, 2009

How to reliably establish a network connection on any BlackBerry device

Share

Establishing a network connection within a BlackBerry application is a challenge which tends to surprise developers new to the BlackBerry platform. This is especially true for developers coming from another platform and expecting the ability to simply call Connector.open(url) and get back a connection to that url. This article explains why connecting to the internet on a BlackBerry device can be tricky and presents some of the popular ways of doing it along with the source code for our own solution.

The Problem

On most platforms an application simply asks for a connection to an Internet resource and the OS handles the specifics of determining which route to take to get there. On a BlackBerry however, there are six possible methods of creating an HTTP or socket connection, each of which serve different ends and may or may not be available on a given device. The developer chooses a connection type by appending a connection string which identifies the type to the end of the desired URL. It is the responsibility of the developer to determine which method to use and append the correct string. As a result, the developer is faced with the problem of writing code which figures this out in a way which will work on every device their app is targeting.

The Options As Described by RIM

These are the six connection options, per the RIM KB article, and their associated concerns:

  1. BlackBerry Enterprise Server using BlackBerry Mobile Data System (MDS) - This is the default which means that if no connection string is appended to the URL this is how the device will connect. For this connection to work, the device needs to be configured to connect through a server which supports MDS. This is true for many corporate controlled devices and very few consumer devices.
  2. BlackBerry Internet Service – This method requires that the application be approved by RIM. This is how apps such as the Google Maps application which tend to work regardless of the device configuration often work.Direct TCP – This method relies on the carrier’ s TCP stack and requires that device be configured with the carrier’s APN information. Alternatively the application can specify the correct values for the application.
  3. Wireless (WiFi) – For devices with wifi capabilities it is possible to connect directly to a site via an available wireless network, or connect to the Blackberry Enterprise Server or Internet Service. This solution is complicated by the fact that wireless networks are not always available.
  4. WAP 1.x - The WAP protocol provides access from a mobile device to many Internet services. However the connection requires passing carrier specific values describing the WAP server to use. There is also the added problem that many carriers do not allow devices to connect through their WAP gateways.
  5. WAP 2.0 – If supported by the carrier, this tends to be a reliable transport mechanism. WAP 2.0 is a complete redesign of the protocol and does supports end to end communication so no carrier specific information is required.

The Undocumented 7th Method

Mike Nelson at AccelGolf gives us this post about the magic string ‘mds-public’. This is an apparently undocumented setting which causes the device to connect through the BlackBerry Internet Browser Service. The end result is an HTTP connection over IPPP via a public MDS server. It is unclear why RIM does not provide any information about this setting, but many applications use this and from our testing it is supported on almost all carriers.

Common Ways This Problem Is Solved

These are some of the ways applications in App World solve this problem. For each method we list the reasons why they can be suboptimal.

  • Some applications take the approach of compiling a list of all the known carrier APN settings and always using the carrier TCP stack. This method is nice because it almost foolproof for the carriers covered by the list but it requires maintaining this list, pushing updates for any new carriers or changes, and likely will not cover every carrier on the planet.
  • Other apps use a chain of failure model in which the application first tries one connection method, then another, and keeps going until a successful connection is established. This is a very robust approach but has the potential drawback of popping up a lot of “Allow connection to X.Y.com” dialogs on devices such as older models or aggressively configured corporate devices.
  • There are plenty of applications which simply query the user. This offloads the problem of discovering the correct path but many users don’t which method their applications use and will likely try them all until one works, making this similar to the option above.

The Solution Used By Localytics

Instead of the above options we adopted the method outlined in the AccelGolf post. We query the device to try and guess which connection method will work and establish a connection based on what we find.

Our discovery hierarchy:

  • Test for simulator. If so use simulator connection.
  • Test for Wifi. If available, use it.
  • Test for carrier coverage. If available, check for access to a BIBS server, otherwise use the settings on the device
  • Test for enterprise server coverage, if available use it.

Notes About This Solution

There are a few extra considerations that have to be taken into account for this methodology to work:

  • There are two ways to connect in the simulator. Either setting “;deviceside=true” and having everything work, or setting “;deviceside=false” and running the BlackBerry MDS Simulator. The BlackBerry browser is configured to use MDS, so the MDS Simulator is necessary to bring up web pages in the simulator and as a result there is a common misconception that it is required in order to use networking within the simulator.
  • The way to test for Wifi varies between OS versions. In version 4.3.0 and above a convenient API getWLANState is available but on previous versions it is necessary to poll the ServiceBook to determine the WiFi state. See this post on the BlackBerry forum for information on how to do that.
  • The constant that describes access to a Carrier TCP resource changed in version 4.5.0 from COVERAGE_CARRIER to COVERAGE_DIRECT
  • The test for a BIBS server is OS dependent. Prior to version 4.6 the only way to check was to query the servicebooks but in 4.6 a flag was added to check for BIBS access.

Our Code

Here is the relevant snippet of our network connection code to use as a demonstration of this method. This code uses the 4.3.0 Wifi check and the 4.5.0 network constants. See the notes above for information on the two changes necessary to use this downlevel, or simply download our open source client libraries directly from the Localytics Wiki

    /**
     * Determines what connection type to use and returns the necessary string to use it.
     * @return A string with the connection info
     */
    private static String getConnectionString()
    {
        // This code is based on the connection code developed by Mike Nelson of AccelGolf.
        // http://blog.accelgolf.com/2009/05/22/blackberry-cross-carrier-and-cross-network-http-connection
        String connectionString = null;

        // Simulator behavior is controlled by the USE_MDS_IN_SIMULATOR variable.
        if(DeviceInfo.isSimulator())
        {
                if(UploaderThread.USE_MDS_IN_SIMULATOR)
                {
                        logMessage("Device is a simulator and USE_MDS_IN_SIMULATOR is true");
                        connectionString = ";deviceside=false";
                }
                else
                {
                        logMessage("Device is a simulator and USE_MDS_IN_SIMULATOR is false");
                        connectionString = ";deviceside=true";
                }
        }

        // Wifi is the preferred transmission method
        else if(WLANInfo.getWLANState() == WLANInfo.WLAN_STATE_CONNECTED)
        {
            logMessage("Device is connected via Wifi.");
            connectionString = ";interface=wifi";
        }

        // Is the carrier network the only way to connect?
        else if((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_DIRECT) == CoverageInfo.COVERAGE_DIRECT)
        {
            logMessage("Carrier coverage.");

            String carrierUid = getCarrierBIBSUid();
            if(carrierUid == null)
            {
                // Has carrier coverage, but not BIBS.  So use the carrier's TCP network
                logMessage("No Uid");
                connectionString = ";deviceside=true";
            }
            else
            {
                // otherwise, use the Uid to construct a valid carrier BIBS request
                logMessage("uid is: " + carrierUid);
                connectionString = ";deviceside=false;connectionUID="+carrierUid + ";ConnectionType=mds-public";
            }
        }

        // Check for an MDS connection instead (BlackBerry Enterprise Server)
        else if((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_MDS) == CoverageInfo.COVERAGE_MDS)
        {
            logMessage("MDS coverage found");
            connectionString = ";deviceside=false";
        }

        // If there is no connection available abort to avoid bugging the user unnecssarily.
        else if(CoverageInfo.getCoverageStatus() == CoverageInfo.COVERAGE_NONE)
        {
            logMessage("There is no available connection.");
        }

        // In theory, all bases are covered so this shouldn't be reachable.
        else
        {
            logMessage("no other options found, assuming device.");
            connectionString = ";deviceside=true";
        }

        return connectionString;
    }

    /**
     * Looks through the phone's service book for a carrier provided BIBS network
     * @return The uid used to connect to that network.
     */
    private static String getCarrierBIBSUid()
    {
        ServiceRecord[] records = ServiceBook.getSB().getRecords();
        int currentRecord;

        for(currentRecord = 0; currentRecord < records.length; currentRecord++)         {             if(records[currentRecord].getCid().toLowerCase().equals("ippp"))             {                 if(records[currentRecord].getName().toLowerCase().indexOf("bibs") >= 0)
                {
                    return records[currentRecord].getUid();
                }
            }
        }

        return null;
    }

Feel free to download the source code for the Localytics client library for BlackBerry OS 4.5 and above if you wish to see the rest our networking code.

Hopefully this helps to clear up some of the confusion around this problem. Between the number of options available, the changing methods for querying the device, and the undocumented strings there is a lot to figure out in order to create robust networking code. Feel free to use this code in your own projects and let us know how it works out for you!

Share this
Share