Sunday, October 31, 2010

C2DM: Using ClientLogin to Obtain an Authentication Token

As explained in a previous entry, Google's Cloud to Device Messaging (C2DM) framework allows our Android applications to make use of the connection that Google services establishes with Android phones, meaning we can implement push notifications more easily and make our applications near real-time.  But where do we start?  Well, there's two sides that are needed...there's the Android device side, and there's the server side.  This blog entry is going to cover what is needed on the server side so that we can then start sending messages to the Android devices.

Setting The Stage
The first step is to create a new Google email account.  In order to send notifications, the web server needs to be logged in to a valid Google account, and so we simply set a new one up that is specifically for the server application's use only.

Once that has been set up, we then need to request access to the service itself.  At the time of writing, C2DM is in Google Labs, meaning it's necessary to signup to use it; this is easily done via this simple form.  You'll need to provide the package name of the Android app that will be receiving the messages, and you'll need to specify the Role Account Email as the new email account that we just set up.  From my experience, the registration process is quick and painless, with a confirmation email arriving within minutes.

Authenticating
With those prerequisites complete, our server will need to authenticate itself by using an authentication token, and we get one of those from ClientLogin.  Authentication tokens are used as it would be a bad idea to store the actual account credentials on the server, but if it's a bad idea to store the credentials, how does the app log in in the first place to obtain this authentication token?  Well, what we can do is create a very simple servlet that allows us to enter the email and password manually...this then gets sent to ClientLogin for validation, and if the credentials are good, then we should receive back an authentication token that is good for about a day (don't worry, the C2DM framework will provide us with a new token when the current one is about to expire).

To authenticate, we first need a connection object to https://www.google.com/accounts/ClientLogin:

HttpURLConnection connection = null;
try {
    URL url = null;
    try {
        url = new URL("https://www.google.com/accounts/ClientLogin");
    } catch (MalformedURLException e) {
        throw new ServletException(e.getCause());
    }
    connection = (HttpURLConnection) url.openConnection();
    connection.setDoOutput(true);
    connection.setUseCaches(false);
    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
} catch (Exception e) {
    // Exception handling
}

The more observant may have noticed the use of HttpURLConnection instead of a HttpsURLConnection object.  Well, that's because I'm using Google App Engine to host my app, and HttpsURLConnection isn't a permitted class (although from my understanding, under the covers App Engine uses HttpsURLConnection for secure connections).
 
Now that we have our connection object, it's time to pass in the credentials that we wish to authenticate with:

StringBuilder sb = new StringBuilder(); 

addEncodedParameter(sb, "accountType", "GOOGLE");
addEncodedParameter(sb, "Email", request.getParameter("account"));
addEncodedParameter(sb, "Passwd", request.getParameter("password"));
addEncodedParameter(sb, "service", "ac2dm");
addEncodedParameter(sb, "source", "myCompany-demoApp-1.0.0");
String data = sb.toString();

DataOutputStream stream = new DataOutputStream(connection.getOutputStream());
stream.writeBytes(data);
stream.flush();
stream.close();

As you can see, this is where we're passing the account type, the email address, and the password.  The service parameter must be the Android C2DM service ("ac2dm"), and the source parameter should follow the format of "companyName-applicationName-versionID".  Oh, and the addEncodedParameter() method that I'm using?  That's just a simple helper method as shown below. :)

public void addEncodedParameter(StringBuilder sb, String name, String value) {

    if (sb.length() > 0) {
        sb.append("&");
    }

    try {
        sb.append(URLEncoder.encode(name, "UTF-8"));
        sb.append("=");
        sb.append(URLEncoder.encode(value, "UTF-8"));
    } catch (UnsupportedEncodingException e) {
    }
}
After we've sent the credentials, the next step is to process the response.  Basically, we're looking for a line in the response that starts with "Auth=", as this contains our token.  We're also going to capture any errors that we may have received:

BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); 

String line;
String tokenIdentifier = "Auth=";
String errorIdentifier = "Error=";
String token = null;
StringBuilder errors = new StringBuilder();
while ((line = reader.readLine()) != null) {
    if (line.startsWith(tokenIdentifier)) {
        token = line.substring(tokenIdentifier.length());
    } else if (line.startsWith(errorIdentifier)) {
        String error = line.substring(errorIdentifier.length());
        errors.append(error + System.getProperty("line.separator"));
    }
}
reader.close(); 
And that's about it.  Of course, this is just the bare minimum that is required to get an authentication token, as the code doesn't even attempt to handle CAPTCHA challenge responses.  In this event, our servlet must then display the CAPTCHA image url (http://www.google.com/accounts/ + CaptchaUrl) and prompt for the additional user input.  It can then respond to the challenge using another call and providing the additional headers logintoken and logincaptcha.

But for now, this should be enough to get a valid authentication token, and with the token in hand, we can start to send messages to our Android devices. :)

Friday, October 29, 2010

Android C2DM: Push It!

Let's imagine an email application for a cell phone; it obviously needs to notify the user whenever a new email has arrived...but how does it actually know when new emails are available?  Well, it could simply ask the server continously if there are any new emails (poll), or we could have the server notify us when any new emails have arrived (push).  But which one should we be using?

As a developer, polling is the easier solution to implement.  We simply have the client wake-up every now and again, and have it call home to the server to check if there's anything new to report...much akin to the child that keeps asking "Are we there yet?".  However, for cell phone applications this approach introduces the issue of polling frequency; poll too frequently, and we're wasting valuable system resources (battery and bandwidth) when for the most part, the answer will be the same.  But poll too infrequently, and we risk the user receiving stale data.  We can skirt around this issue by simply handing the decision to the user via a preference setting...or we could use the push model instead.

When it comes to mobile devices, the push model is, to me at least, a much better solution; it follows the Hollywood Principle of don't call us, we'll call you, meaning that our application only needs to do something when the server actually tells us that new information is available.  This saves those precious system resources, but it still comes at a cost...namely that of increased complexity.  Thankfully, Google has already addressed this with their C2DM service, and being the good fellows that they are, they're opening the service up so that other Android applications can take full advantage of near real-time notifications.

So how does it work?

It works via an existing Google services connection, meaning the user needs to be signed in to a valid Google account on their Android device.  The application can then register itself to receive notifications via this connection, and the C2DM service will respond with a unique key for the application to use.  The application then has to send this key to any third-party server that it wants to receive notifications from, and viola...any time the server needs to notify that particular application on that particular device of something, it simply asks the C2DM service to send out a message.  When the device receives the message, Android knows where to route the message to...even waking up the application if necessary, and as there's no required UI for the messages, the app is free to do whatever it likes.  It may display a message to the user, or it may perform some silent task in the background, like synchronizing data.  How neat is that?

But what of the caveats?
  • The service is only available as of Android 2.2.
  • The service requires the user to be signed in to a valid Google account.
  • Messages can be no more than 1024 bytes in size.
Requiring at least Android 2.2 will eventually become a non-issue as time goes by.  And restricting the message size to 1024 bytes ensures that servers are simply saying "Hey, data is available...come and get it", rather than  trying to send the actual data itself.  But what about requiring a Google account?  Well, that's an extremely small price to pay in my mind...if the user doesn't have (nor want) a Google account, even if it's just for the sake of receiving push notifications, then they'll just have to go to their preference settings and decide for themselves what a good polling frequency should be. :)

Wednesday, October 27, 2010

Hello, World!

Recently I've been working on a personal Java project of mine, and I've been having a lot of fun slowly watching the pieces come together. The real fun, if I'm completely honest, has been mainly due to the fact that a lot of the frameworks that I've used are new to me, meaning there's been a lot to learn, and also there's a lot to improve upon when I no doubt refactor everything later.  Harnessing the power of bullet points, here's what the project has involved so far:
As you can see from the above list, it's an Android project, and my first one at that.  Armed with a book, some simple Googling skills, and a little head-scratching, I've so far managed to concoct the following:
  • Android device can register itself with Google's C2DM service
  • Android device can call the web service (using Restlet) to request certain functionality, and pass the C2DM registration key to it so that the device can receive near real-time notifications once tasks have been completed
  • Web service can persist the information using Google's BigTable, and can service other requests
  • Web service can send notifications to registered Android apps using C2DM
Of course, I say that all of these pieces are working, but I'll hold my hand up and readily admit that I've only tested things so far in Eclipse using the Android emulator. I don't really intend to test fully using my own device (the rather fantastic Droid X) until I have everything working for sure in the emulator first...don't ask why, it's just how I feel like doing things. :)

So what do I have left to do?  At this point, there's still a lot to be done on the device side of things...mainly UI, and of course I still have to deploy the web service to Google App Engine, but all of this is to come.

So that's pretty much what I'm going to be writing about, and probably starting with C2DM. As all of this is a new playing area for me, I'm pretty sure that what I say will be subject to subsequent corrections; but that's kinda the point...we all learn from our mistakes, and if I can write about something, and if that something actually makes some sense to others, then I may just have achieved a basic understanding of that subject. :)