ICEmobile Locations with Google Latitude

Location, Location, Location

If you are a smartphone user or developing software for mobile devices, being able to figure out the current location of a device is an important feature. Locating a restaurant, getting driving directions, checking in at a pub, finding your friends – these all rely on some way of calculating where you and your device currently are on the planet.

Determining the location of a device can be done in a few different ways. The obvious one is GPS which is built in to most smartphones these days. But cell phone towers and WiFi access points can also be used to estimate the location of device with varying degrees of accuracy.

Using location information to provide contextually relevant information as the device moves between different virtual geographical areas (geofencing) can also be beneficial for many things (or scary if it’s abused).

I’ve been looking at some different methods of getting a device’s location for use in server-side applications, and recently took a stab at coding up a small, quick prototype using Google Latitude. Google provides Latitude as a service that you can use on your mobile device to do some of the things I mentioned before – keep tabs on friends and family, check in to various locations, etc. It’s free, fairly east to use, and works on a wide range of devices.

Google Latitude is also useful to developers. You can access it either through Google’s custom APIs or as a RESTful service. I waded through Google’s developer documentation for Latitude and developed a simple servlet that would query Google Latitude for the current location of my own device. The following is a brief summary of that journey.

Research

I started with a lot of reading. Some of it useful and some not so much. The recommended place to begin is Google’s “Getting Started” page. You’ll need a Google account before doing anything so go and sign up/sign in first. Google also recommends playing around with Latitude a bit to understand what it provides. You can do this on your desktop or on your device. Depending on which device you have you’ll want to download Google Maps for your mobile device (which includes Latitude support) or, if you have an Apple device, download the Google Latitude app. I was lazy and initially skipped this part but later found that if I didn’t log in and use Latitude at least once, I ran into some problems.

Prep

To begin with, I figured I could get something going in a single Java servlet. The first thing the application needs to do is get authorized. This is probably the trickiest part of the whole adventure. People’s privacy is important and most of us don’t want to be tracked without our explicit permission. So before Google will provide you with any location information, you’ll need to get that permission. This requires the following general steps as outlined in the “Using OAuth 2.0 to Access Google APIs” page:

  • Create and register your application with Google via the APIs Console. This creates security values the web application can use to authorize itself with Google and then call the Latitude API.
  • Switch on the Latitude API service for the application you’ve created.

latitude-service-on

  • When you are done, the Overview panel should look something like this:

google-api-console-overview

Code

Now let’s roll up our sleeves and code. It’s a multi-step process to get permission to access a device’s location and it depends on how it’s being done. In our case, we are doing it from the back-end of a web application so the steps are:

  • Request permission from the user to allow us access to the location information. Success here provides us with an authorization code.
  • Using the authorization code, request an access token.
  • Using the access token, call the Latitude API.

Request Permission

To request permission from the user, I constructed a simple web page with a link that includes the follow parameters:

   <a href="https://accounts.google.com/o/oauth2/auth?
   scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Flatitude.current.best&
   state=icenotify.google.auth&
   redirect_uri=http%3A%2F%2Fhost%3Aport%2Flocate%2Flatitude&
   response_type=code&
   client_id=your-client-id&
   approval_prompt=force">Click to begin authorization for Google Latitude location tracking.</a>

The “scope” in this case indicates that type of information I’m requesting. The “state” is a value I can set myself and use for my own purposes. The “redirect_uri” is where the response will be sent and this should include the authorization code. The “client-id” can be found in the APIs Console for the application you created and will look like ############.apps.googleusercontent.com where the number is the project number.

When a user clicks on that link, Google will respond with a request asking them if it’s okay with them if the application requests their location. This should work in any modern desktop browser that supports the Geolocation API.

Request Access Token

Once the user agrees, Google will then send an authorization code to the location provided in the “redirect-uri” parameter. Here we provide some code in our prototype servlet for handling this incoming request and taking the next steps. First we can check the incoming “state” parameter to see if it matches what we sent:

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ...
    String state = req.getParameter("state");
    if( state == null || !state.equals("icenotify.google.auth")){
    ...

Assuming all is good and we want to continue, we extract the authorization code from the request:

    String code = req.getParameter("code");
    String accessToken = getAccessToken(code);

Now we use that code to make our own request back to Google to acquire an access token. Google provides their own libraries and APIs to help simplify this step but I didn’t want to depend on those so I just rolled my own HTTP connection. Some names have been changed to protect the innocent and please keep in mind that the code is intended more for learning (mine and yours) than production:

private String getAccessToken(String authCode){

    HttpsURLConnection httpConn = null;
    try {
        //This is the URL used for obtaining access tokens using an authorization code
        URL smsURL = new URL("https://accounts.google.com/o/oauth2/token");

        httpConn = (HttpsURLConnection) smsURL.openConnection();
        httpConn.setRequestMethod("POST");
        httpConn.setDoOutput(true);
        httpConn.setReadTimeout(5000);
        httpConn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");

        //Build a string of parameters to POST...
        StringBuilder params = new StringBuilder();

        //The authorization code you received from Google is required to get a valid token
        params.append("code=").append(URLEncoder.encode(authCode, "UTF-8")).append("&");

        //You need to include your own client_id available from the Google API console for your application
        params.append("client_id=").append(URLEncoder.encode("############.apps.googleusercontent.com", "UTF-8")).append("&");

        //The client_secret value is also available in the Google API console
        params.append("client_secret=").append(URLEncoder.encode("yourclientsecretshouldgohere", "UTF-8")).append("&");

        //This should point to where you want the access token sent. I just use the same servlet
        params.append("redirect_uri=").append(URLEncoder.encode("http://host:8080/locate/latitude", "UTF-8")).append("&");

        //Tells Google what we are sending it to get the token
        params.append("grant_type=").append(URLEncoder.encode("authorization_code", "UTF-8"));

        log.log(Level.INFO, "params: " + params.toString());

        httpConn.connect();

        DataOutputStream wr = new DataOutputStream(httpConn.getOutputStream());
        wr.writeBytes(params.toString());
        wr.flush();
        wr.close();

        //Google returns the response as JSON so we just convert that to a map for convenience using the
        //jackson-mapper-asl JSON library
        BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getInputStream()));
        ObjectMapper mapper = new ObjectMapper();
        Map<String,String> responseMap = mapper.readValue(reader, Map.class);
        log.log(Level.INFO, "response:\n" + responseMap);

        //The resulting map should contain a key value pair for the access_token
        return responseMap.get("access_token");

    } catch (Exception e) {
        log.log(Level.SEVERE, "could not get access token", e);
        e.printStackTrace();
    } finally {
        if (httpConn != null) {
            httpConn.disconnect();
        }
    }

    return null;
}

Request Location

Once you have the access token, you can now make a RESTful request to the Latitude API. It’s a GET rather than a POST and, again, I just rolled my own rather than use the Google APIs:

private String getLocation(String accessToken){

    HttpsURLConnection httpConn = null;
    try {

        //The RESTful URL used to get the current location
        StringBuilder rawURL = new StringBuilder("https://www.googleapis.com/latitude/v1/currentLocation");

        //Include the access token as a parameter
        rawURL.append("?access_token=").append(URLEncoder.encode(accessToken, "UTF-8"));
        log.log(Level.INFO, "raw URL: " + rawURL.toString());
        URL smsURL = new URL(rawURL.toString());

        httpConn = (HttpsURLConnection) smsURL.openConnection();
        httpConn.connect();

        InputStream respStream = httpConn.getErrorStream();
        if( respStream == null ){
            respStream = httpConn.getInputStream();
        }

        //Google returns the response as JSON so we just convert that to a map for convenience. The format is:
        // {data={kind=latitude#location, timestampMs=1364485778555, latitude=53.7266683, longitude=-127.6476206}}

        BufferedReader reader = new BufferedReader(new InputStreamReader(respStream));
        ObjectMapper mapper = new ObjectMapper();
        Map<String,Map> responseMap = mapper.readValue(reader, Map.class);
        log.log(Level.INFO, "response:\n" + responseMap);

        Map<String, Object> dataMap = responseMap.get("data");
        String location = dataMap.get("latitude") + ", " + dataMap.get("longitude");
        return location;

    } catch (Exception e) {
        log.log(Level.SEVERE, "could not get location", e);
        e.printStackTrace();
    } finally {
        if (httpConn != null) {
            httpConn.disconnect();
        }
    }

    return null;
}

At this point I had some difficulty getting a valid response to the location request even though I had a valid token. I would consistently get 403 Forbidden. I couldn’t find any definitive cause but it seems that it was necessary to actually visit (and play with) Latitude on your device or in your browser before it’s officially “activated”. After I did that, it all worked fine.

Leave a Reply

Your email address will not be published. Required fields are marked *

one × 3 =