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.
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.
These are the six connection options, per the RIM KB article, and their associated concerns:
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.
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.
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:
There are a few extra considerations that have to be taken into account for this methodology to work:
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!