Creating Location-Aware Applications for Windows Mobile Devices
4/7/2010
Maarten Struys, Alten-PTS
June 2009
Summary
Adding location awareness to your own application and calling out to Web services in your current position opens a lot of possibilities, not just for line-of-business applications, but also for many different kinds of consumer applications. You can develop applications that are location aware by using the techniques described in this article to obtain your current position either through GPS Lookup or through Cell Tower Lookup.
This article addresses the technical details of adding location awareness to a Windows Mobile® application. The paper covers related issues, such as the effect of location determination on battery life, and provides relevant code examples and a sample.
Sample Download
Applies To
Windows Mobile 5.0
Windows Mobile 5.0 SDK
Windows Mobile 6 Professional
Windows Mobile 6 SDK
Introduction
Retrieving Location Information Through GPS
Using the Managed Wrappers Around GPSID
Retrieving Location Information through Cell Tower Lookup
Accessing Cell Tower Information on your Windows Mobile Device
Making Windows Mobile Device Location Awareness Independent of Location Retrieval
Using a Class Factory to Retrieve your Current Location
Conclusion
Introduction
Location awareness requires you to obtain the current location of your Windows Mobile device. Then you must make your application aware of the location. The application design should be independent of how location information is retrieved. This white paper shows you how to retrieve your location by using GPS Lookup and Cell Tower Lookup. You also learn how to use location information inside a Windows Mobile application.
Retrieving Location Information Through GPS
There are several ways to retrieve location information inside a Windows Mobile device, each with different advantages. Both Windows Mobile 5.0 and Windows Mobile 6 devices contain the GPS Intermediate Driver (GPSID). Even though the name implies that this is a device driver, it is not a real device driver. GPSID is an abstraction layer between the device driver that controls the GPS receiver and applications that want to use GPS information. Having GPSID positioned in between applications and the GPS hardware gives Windows Mobile 5.0 and Mobile 6 devices a big advantage over previous Windows Mobile devices. With GPSID, multiple applications can use the same physical GPS hardware, something that was not possible with devices earlier than Windows Mobile 5.0. In those earlier Windows Mobile devices, only one application at a time could use GPS hardware. For another application to retrieve location information through the GPS receiver, the original application that used the GPS hardware had to be closed, or at least, that application had to stop using the GPS hardware. GPSID resolves this problem, because it acts as the only application that uses GPS hardware from a low-level point-of-view. At the same time, GPSID acts as the GPS location provider for multiple applications.
GPSID exposes several native APIs to retrieve location information. However, this white paper focuses on managed application development. Developers of managed applications can access a series of managed wrappers around the native GPSID APIs through sample code that is included in both the Windows Mobile 5.0 and Windows Mobile 6 SDKs. You can find these managed wrappers on your development workstation, assuming you installed the Windows Mobile 6 SDK in the default folder, at the following path:
C:\Program Files\Windows Mobile 6 SDK\Samples\PocketPC\CS\GPS
The path differs for the Windows Mobile 5.0 SDK.
Using the Managed Wrappers Around GPSID
When you consider the GPS sample inside the Windows Mobile 5.0 or Windows Mobile 6 SDK, you see a project that includes complete source code. To use the managed wrappers around GPSID, you have to build the sample project first. Because the Windows Mobile SDKs were released before the release of Microsoft® Visual Studio® 2008, the sample project has to be converted before you can build it in Visual Studio 2008. Visual Studio 2008 handles converting your project. After the sample project is built, you see that it created both an executable and a type library. The type library is called Microsoft.WindowsMobile.Samples.Location.dll. It contains managed wrappers around GPSID. To use these wrappers, you have to add a reference to this particular type library inside your own project. The following illustration shows the classes that the wrapper around GPSID contains.
If you want to use GPSID inside your managed application, after you add a reference to the managed wrappers around GPSID, you first have to create an instance of type Gps. Then you have to call the Open method on that object so that you can retrieve location information. You can retrieve location information synchronously or asynchronously. In the latter case, you retrieve continued updates when there is any change in the data that the GPS radio receives. You have to be aware that the GPS radio, being an additional radio, drains the battery. Therefore, if you no longer have to retrieve location information inside your application, make sure to call the Close method on the Gps object. Omitting to call the Close method keeps the Gps radio fully powered, even if you no longer retrieve location information.
The following screen shows a simple application that can retrieve GPS-based location information, both synchronously and asynchronously. The location is retrieved after using the synchronous method GetPosition and is retrieved continuously by subscribing to the LocationChanged event.
To retrieve a location synchronously, you only have to call one additional method after having called the Open method of the Gps object, as the following code example shows. In this code example, a new instance of type Gps is created in the MainForm constructor and the Open method is called immediate to enable the GPS hardware. When the user chooses the menu My Location, the code in the menuLocation_Click event handler is executed. In this event handler, you can see that the current position is retrieved through a call to the GetPosition method. This method blocks until a position reading is returned from GPSID and with that from the underlying hardware. After location information is available, the Latitude, Longitude, and Heading readings are validated and displayed in a Label control. For simplicity, no action is taken when readings are invalid. The method GetPosition is available in two flavors. The one used in the first code example below just waits for a new reading from the GPS hardware; In other words, it waits until location data is received from one or more satellites. There is also an overloaded method available that takes a TimeSpan object as parameter. With it, you can retrieve locations more efficiently by returning a cached location reading as long as it is not older then the time specified in the TimeSpan object. This version of the GetPosition method is useful for initial location readings when you know that the location of the device has not changed after the last location reading.
The following code example shows how the location is retrieved synchronously through GPSID.
public partial class MainForm : Form
{
private Gps gps;
public MainForm()
{
InitializeComponent();
gps = new Gps();
gps.Open();
}
private void menuExit_Click(object sender, EventArgs e)
{
gps.Close();
Close();
}
private void menuLocation_Click(object sender, EventArgs e)
{
GpsPosition position = gps.GetPosition();
LatLong location = new LatLong();
if (position.LatitudeValid)
location.Latitude = position.Latitude;
if (position.LongitudeValid)
location.Longitude = position.Longitude;
if (position.HeadingValid)
location.Heading = position.Heading;
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.Append("Latitude = ");
sb.AppendLine(location.Latitude.ToString());
sb.Append("Longitude = ");
sb.AppendLine(location.Longitude.ToString());
sb.Append("Heading = ");
sb.AppendLine(location.Heading.ToString());
label1.Text = sb.ToString();
}
}
To retrieve a location asynchronously, you must subscribe to the LocationChanged event, as is shown in the next code example. Initializing the Gps object is omitted in this code example, because it is identical to what you already saw in the first code example. Every time the user chooses the Location Updates menu, (see the screen GPS Lookup), the application either subscribes or unsubscribes from the LocationChanged event. When the application is subscribed to this event, continuous location updates are received in the gps_LocationChanged event handler. Identical to the synchronous way of retrieving location information, this information is available in a GpsPosition object. However, asynchronous updates are received on a separate thread, even though this is transparent for users of GPSID. Because it is not allowed to update User Interface controls on other threads than their creator, updating User Interface controls requires some additional work. Controls have to be updated through the Control.Invoke method as you can see in the gps_LocationChanged event handler in the following code example. The code example shows how to retrieve the location asynchronously through GPSID.
private void menuLocationUpdates_Click(object sender, EventArgs e)
{
if (menuLocationUpdates.Checked)
{
menuLocationUpdates.Checked = false;
gps.LocationChanged -= gps_LocationChanged;
}
else
{
menuLocationUpdates.Checked = true;
gps.LocationChanged += new
LocationChangedEventHandler(gps_LocationChanged);
}
}
private delegate void UpdateLocationInfo();
void gps_LocationChanged(object sender, LocationChangedEventArgs args)
{
GpsPosition position = args.Position;
LatLong location = new LatLong();
if (position.LatitudeValid)
location.Latitude = position.Latitude;
if (position.LongitudeValid)
location.Longitude = position.Longitude;
if (position.HeadingValid)
location.Heading = position.Heading;
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.Append("Latitude = ");
sb.AppendLine(location.Latitude.ToString());
sb.Append("Longitude = ");
sb.AppendLine(location.Longitude.ToString());
sb.Append("Heading = ");
sb.AppendLine(location.Heading.ToString());
label2.Invoke((UpdateLocationInfo)delegate()
{
label2.Text = sb.ToString();
});
}
}
GPSID and the Battery
In the second code example above, you can see that location information is only updated and presented to the user. In scenarios where it only makes sense to update location information in the foreground (showing updated location information for example inside a map), it makes sense to add code to stop continuous location updates when the application goes to the background. Limiting the processing for an application that executes in the background saves battery life. You can even save more battery life in such scenarios by closing the connection to GPSID when the application goes to the background and reopening it again when the applications becomes the foreground application again. The following code example shows how you can optimize the application to preserve battery life. The application subscribes to the LocationChanged event every time that the application is in the foreground, by reacting to the Activated event that is raised by the MainForm object. It unsubscribes from the LocationChanged event when the application is moved to the background by reacting to the Deactivate event that is raised by the MainForm object. The following code example shows how to preserve battery power when the application goes to the background.
private void MainForm_Activated(object sender, EventArgs e)
{
if (menuLocationUpdates.Checked)
{
gps.LocationChanged += new
LocationChangedEventHandler(gps_LocationChanged);
}
}
private void MainForm_Deactivate(object sender, EventArgs e)
{
if (menuLocationUpdates.Checked)
{
gps.LocationChanged -= gps_LocationChanged;
}
}
Even though you can retrieve your current location easily by using GPSID and even though the location that the satellites return is very accurate, using GPSID might not be the most efficient way to retrieve location information. To start, it can take a long time until the first location reading is transmitted to the device. Depending on physical device hardware, your current location, visibility of satellites, and the last position read through GPSID, it can take several minutes to retrieve an initial location. If you do not require the accuracy of a GPS-transmitted position but, at the same time, require fast location information retrieval, consider the following alternative.
Retrieving Location Information through Cell Tower Lookup
Most Windows Mobile devices are, of course, phones. Depending on the Mobile Operator you are using, your phone has the capability to retrieve Cell Tower information programmatically. Included in this information are a cell tower code, an area code, and an international dialing code. Given this information, you can retrieve the location of the cell tower your phone is currently connected to, providing you with more or less accurate information about your current location. The advantage of using Cell Tower Lookup is that your current location can be retrieved very fast without using additional battery power. The disadvantage is the varying accuracy, assuming you are not using cell tower triangulation. Another disadvantage is that you have to create additional code to retrieve your location. Getting cell tower information can be achieved through the Radio Interface Layer (RIL) on the Windows Mobile device. However, just an identification of a cell tower and country and mobile operator information is not enough. All this information must be converted to a location. You can pass the retrieved cell tower information to a third-party Web service.
Accessing Cell Tower Information on your Windows Mobile Device
To retrieve cell tower information inside your own application, you must access the Radio Interface Layer (RIL) of the device. The recommendations for OEM RIL implementation suggest disallowing untrusted applications to access RIL functionality. Therefore, you either have to code sign your application with an appropriate certificate or limit yourself to make use of RIL functionality inside your application on unlocked devices. To retrieve cell tower information, you basically must call one single RIL function. However, you must call additional functions for a handle to RIL and to clean up that handle after you have finished using the RIL functionality.
You might be unfamiliar with accessing low-level functions on Windows Mobile devices to retrieve cell tower information. This is not much of a problem, however, because the Radio Interface Layer is implemented as a device driver that is divided into two separate layers and a proxy to access information from inside applications. The MDD (Model Device Driver) layer is independent of the radio stack. It contains code that communicates with the RIL Proxy and code that implements any radio stack independent features in the RIL driver. Code published in the MDD layer is safe to use, well documented, and gives you high-level access to all kinds of radio functionality. On the other hand, the low-lever layer of RIL, the PDD (Platform Dependent Driver) layer is radio-stack dependent. The implementation differs per OEM.
RIL publishes several APIs, all identified by the prefix "RIL_". All these APIs are written in native code, so to use them from a managed application, you must use P/Invoke. You must use the following APIs to retrieve cell tower information:
- RIL_Initialize. Initializes the Radio Interface Layer for use by an application.
- RIL_Deinitialize. Correctly closes and cleans up resources after an application is finished using RIL.
- RIL_GetCellTowerInfo. Retrieves information about the cell tower that the Windows Mobile device uses.
Accessing these APIs from managed code is fairly simple, with one exception, as you can see in the following code examples. The following code example shows the P/Invoke declaration to access RIL.
public delegate void RILRESULTCALLBACK(uint dwCode,
IntPtr hrCmdID, IntPtr lpData, uint cbData, uint dwParam);
public delegate void RILNOTIFYCALLBACK(uint dwCode,
IntPtr lpData, uint cbData, uint dwParam);
[DllImport("ril.dll")]
public static extern IntPtr RIL_Initialize(uint dwIndex,
RILRESULTCALLBACK pfnResult,
RILNOTIFYCALLBACK pfnNotify,
uint dwNotificationClasses,
uint dwParam,
out IntPtr lphRil);
[DllImport("ril.dll", EntryPoint = "RIL_GetCellTowerInfo")]
public static extern IntPtr RIL_GetCellTowerInfo(IntPtr hRil);
[DllImport("ril.dll")]
public static extern IntPtr RIL_Deinitialize(IntPtr hRil);
You can use RIL_Initialize to either return a result through the callback function pfnResult, or you can use it to asynchronously return state changes through the callback function pfnNotify for all the states the calling application registered for through dwNotificationClasses. The example code in this white paper uses only synchronous operations. Every time the application calls RIL_GetCellTowerInfo, the execution result of this API is immediately returned. However, the actual data is returned through the callback function pfnResult that was passed to RIL_Initialize. The callback function receives a structure that must to be marshaled before it can be used in managed code. The following code example shows the structure, returned by RIL, after calling RIL_GetCellTowerInfo.
[StructLayout(LayoutKind.Explicit)]
internal class RILCELLTOWERINFO
{
[FieldOffset(0)]
uint dwSize;
[FieldOffset(4)]
uint dwParams;
[FieldOffset(8)]
public uint dwMobileCountryCode;
[FieldOffset(12)]
public uint dwMobileNetworkCode;
[FieldOffset(16)]
public uint dwLocationAreaCode;
[FieldOffset(20)]
public uint dwCellID;
}
In this code example, you can see how to retrieve the most important structure members to pass to a Web service to retrieve the location of a cell tower. There are more interesting fields in the RILCELLTOWERINFO structure, for example, the signal strength of the tower you are connected to. By using additional members of this structure you might calculate a more accurate current location for yourself. However, to keep things fairly simple, focus on the tower you are connected to and its location. Inside managed code, you can now retrieve information about the cell tower you are currently connected to and store that information in a class.
The following code example shows how the managed type stores cell tower information.
public class CellTower
{
public int CellId { get; set; }
public int LAC { get; set; }
public int MCC { get; set; }
public int MNC { get; set; }
}
To call the necessary RIL APIs from within managed code, you can use the following code example. The interesting part is how cell tower information is returned to the caller of RIL_GetCellTowerInfo. The information is not returned immediately, but instead is returned through a callback function. Because a location-aware application cannot do anything useful until the current location of the Windows Mobile device is known, the asynchronous behavior of RIL_GetCellTowerInfo is changed into synchronous behavior by using a managed event that is set when cell tower information is available.
The following code example shows how the cell tower information is retrieved in managed code.
public class CellTowerInfo
{
private static AutoResetEvent dataReceived = new AutoResetEvent(false);
private static RIL.RILCELLTOWERINFO towerInfo;
public static CellTower GetCellTowerInfo()
{
IntPtr hRIL = IntPtr.Zero;
IntPtr hResult = IntPtr.Zero;
hResult = RIL.RIL_Initialize(1,
new RIL.RILRESULTCALLBACK(CellTowerData),
null, 0, 0, out hRIL);
if (hResult != IntPtr.Zero)
return null;
hResult = RIL.RIL_GetCellTowerInfo(hRIL);
dataReceived.WaitOne();
RIL.RIL_Deinitialize(hRIL);
CellTower ct = new CellTower();
ct.LAC = (int)towerInfo.dwLocationAreaCode;
ct.MCC = (int)towerInfo.dwMobileCountryCode;
ct.MNC = (int)towerInfo.dwMobileNetworkCode;
ct.CellId = (int)towerInfo.dwCellID;
return ct;
}
private static void CellTowerData(uint dwCode,
IntPtr hrCmdID, IntPtr lpData, uint cbData, uint dwParam)
{
towerInfo = new RIL.RILCELLTOWERINFO();
Marshal.PtrToStructure(lpData, (object)towerInfo);
dataReceived.Set();
}
}
To retrieve the location of the cell tower that you are connected to, you require additional functionality, for example a database or a Web service to look up the Cell-ID with coordinates. There are several commercial and free Web services available to retrieve cell tower location information. This article uses two Web services hosted by Yahoo to obtain latitude/longitude information about a cell tower. Yahoo has a fairly easy-to-use Web service available to retrieve the postal code for a cell tower. Having the postal code of a cell tower, you can then pass the postal code to another Yahoo Web service to convert a given postal code into a latitude / longitude reading. To look up a cell tower location, see the Yahoo Web site. When you have passed the international dialing code (dwMobileCountryCode), network code (dwMobileNetworkCode), location area code (dwLocationAreaCode) and cell Id (dwCellID) that you retrieved from RIL_GetCellTowerInfo, you get a postal code that contains the location of the cell tower. You can pass it to another Yahoo Web service to give you a latitude/longitude reading of the cell tower you are currently connected to. In order to use these Web services, you have to sign up and obtain an application ID. Getting an application ID is really easy. Just read the description of the mentioned Web services. The following screen shows the cell tower location through different Web services.
The Yahoo Web services used in this white paper are REST services, meaning that it is easy to construct request URLs that work both in a browser and from within an application. For example, to retrieve a cell tower location, you can construct a URL similar to the following URL.
The following code example shows the XML response that the Yahoo Cell Lookup Web service returns.
<?xml version="1.0" encoding="utf-8" ?>
<<rsp stat="ok">
< <Location>
< <Country cell="current" source="user">Netherlands</Country>
< </Location>
< <Location>
< <Country cell="" source="GPS">The Netherlands</Country>
< <State cell="" source="GPS">Array</State>
< <City cell="" source="GPS">Alphen aan Den Rijn</City>
< <Zipcode cell="" source="GPS">2408</Zipcode>
</Location>
</rsp>
There are also other Web services available, including one, hosted by Google that gives reasonably accurate location information for cell towers worldwide. It is beyond the scope of this article to describe how to use this undocumented Web service that is used inside Google Maps. If you want to read more about how to access this particular Web service, passing in cell tower information, you can find an interesting article on CodeProject.
Making Windows Mobile Device Location-Awareness Independent of Location Retrieval
Inside your application, it should be irrelevant how you are retrieving your current location. As long as you have a good latitude/longitude reading of your location, retrieved either by GPSID or by Cell Tower Lookup, you can use that location information inside a location-aware application for various tasks, such as for the following tasks:
- Publish location-aware "Tweets" on Twitter.
- Publish pictures on Face Book and automatically insert the location where the picture is taken.
- Create location-aware blog entries.
- Execute localized searches by using Live Search.
- Display a map around my current location by using Virtual Earth.
To keep your Windows Mobile application clean and to retrieve your location information independent of underlying location retrieval technologies, consider creating a class factory to retrieve location information. If you are developing an application that targets Windows Mobile devices, you first might want to retrieve location information through Cell Tower Lookup. This does not drain the battery of your device, assuming that you are already connected to a cell tower through a mobile operator. If you know your exact location, or if you are not sure about the accuracy of the location retrieved through Cell Tower Lookup, you can use GPS functionality to get a better determination of your location, and even update the cell tower location information that is available through several Web services to become more accurate. However, again, all this functionality should be transparent for your users. Using a combination of Cell Tower Lookup and getting more detailed information through GPS if you do not trust the Cell Tower Lookup readings is a software-only solution known as Assisted GPS (A-GPS), although it is absolutely free. It does not require additional hardware on your device.
Several mobile operators do not let your application access cell tower location information at all, although most mobile operators do. In the end, all mobile operators will probably make this information available, because developers and users can greatly benefit from location awareness and use the mobile operator’s network to retrieve location-aware information, such as the location of my friends, searching for nearby places of interest, and so on.
Using a Class Factory to Retrieve your Current Location
To make the functionality for location retrieval transparent to the application, you can create an interface that defines several methods to retrieve locations and an event to which an application can subscribe to be informed about location changes.
The following code example shows how to retrieve location information independent of the used location provider, GPS or Cell Tower Lookup.
public class LocationEventArgs : EventArgs
{
public LocationEventArgs(LatLong l)
{
Location = l;
}
public LatLong Location { get; set; }
}
public delegate void LocationChangedEvent(ILocationProvider sender,
LocationEventArgs e);
public interface ILocationProvider : IDisposable
{
LatLong CurrentLocation { get; }
void EnableLocationProvider(bool enable);
void LiveUpdates(int interval);
void StopUpdates();
event EventHandler<LocationEventArgs> LocationChanged;
}
If each location provider implements this interface, an application can retrieve location information through ILocationProvider without having to care about the physical way in which location information is retrieved on the device. In the downloadable sample code for this article, two location providers are implemented, one that retrieves location information through Cell Tower Lookup and one that retrieves location information through GPS.
Inside an application, location information can now be retrieved in two ways. In the sample application that is provided as part of this article, the user can select either Cell Tower or GPS Lookup. Of course this is also something that can be automated, letting the application start reading Cell Tower information until GPS is available.
The following screens show that the sample application can retrieve location information in different ways.
Retrieving location information inside the application itself is very simple as the following code example shows.
private LatLong location;
ILocationProvider locationProvider;
private void MainForm_Load(object sender, EventArgs e)
{
if (locationProvider == null && SystemState.PhoneRadioPresent)
{
locationProvider = LocationProvider.GetLocationProvider(
LocationProvider.LocationType.CellTower);
location = locationProvider.CurrentLocation;
ShowLocation(location.Latitude, location.Longitude);
}
}
private void menuLocation_Click(object sender, EventArgs e)
{
location = locationProvider.CurrentLocation;
ShowLocation(location.Latitude, location.Longitude);
}
private void menuSettings_Click(object sender, EventArgs e)
{
if (Settings.GetInstance().ShowDialog() == DialogResult.OK)
{
locationProvider = LocationProvider.GetLocationProvider(
Settings.UseGPS ? LocationProvider.LocationType.GPS :
LocationProvider.LocationType.CellTower);
location = locationProvider.CurrentLocation;
}
}
It is beyond the scope of this article to show how to call out to several Web services to use your current location. However, when you have location information available on your Windows Mobile device, you can retrieve location-aware data in multiple ways, and publish your location on all kinds of social networks. Of course you can also use a Microsoft SQL Server® Compact Edition database to continuously store your location information for various purposes.
Conclusion
Retrieving location information on a Windows Mobile device is fairly easy. This article discussed how to retrieve your location by using two different methods:
- Cell Tower Lookup, which is fast, does not consume much battery power, but is not entirely accurate.
- GPS Lookup, which might take some initialization time (sometimes up to minutes), drains additional battery power, but is very accurate.
Adding location awareness to your own applications and calling out to Web services that have your current position opens lots of possibilities, not only for line-of-business applications but also for many kinds of consumer applications. Using the techniques described in this article, consider how to develop some location-aware applications. One promising technique (fetching location through IP lookup) is not described in this article. It is up to the reader to extend the current location retrieval methods with IP Lookup that can return accurate results, especially in densely populated areas where many free Wi-Fi networks are available.
About the sample code
This article includes complete sample code. However, the sample code only functions well if you are running it on a Windows Mobile 6 Professional device with phone capabilities. In order to experiment with GPS Location retrieval, the device should either have built-in or external GPS hardware or a running instance of FakeGPS. To be able to use the Cell Tower Lookup Web service, you have to obtain a Yahoo Application ID and store it in the string table that is part of the resource file of the solution under the name YAHOO_AppID.
Author Bio
Maarten Struys is an experienced software developer. He has been working with all Microsoft Windows Operating Systems for over 20 years both developing native applications, and, after the turn of the century, developing managed applications. He is a well known speaker at international conferences like Tech·Ed, MEDC, and Mobile Connections. Maarten created a large number of How-Do-I videos for MSDN around device development. In the past, he frequently presented MSDN Webcasts for developing Windows Mobile devices. They are all available for on-demand viewing. Recently, Maarten created the RampUp program for Windows Mobile Developers. Maarten mainly focuses on how to create well performing managed applications for smart devices, getting the most out of devices with limited resources, and using as little battery power as possible. He travels around the world frequently, talking about Windows Mobile and Windows Embedded CE development. For information about how to use .NET in the embedded world, see Maarten's Web site at https://www.dotmetfordevices.com.