Android 2.2's most important improvement is without doubt the significantly faster virtual machine. The API, however, got some new features too. For me, the most intriguing new feature is the access to Google's experimental push services.

I have already posted about push feature in relation to iBus//Mobile asynchronous communication package so push is not new to Android. There are also open source alternatives like MQTT, Deacon or Urban Airship. It has also been present in certain Google applications from the beginning. Whenever Android Gmail application sends a notification when a new mail arrives on the server, you see push in operation. It is pretty fast, according to my experiences, even though time to time it takes longer time for the notification to arrive. 2.2 opened that already existing mechanism to general-purpose applications, even though the push service is still in early beta.

In order to do push, either one has proper push bearer (a network mechanism able to deliver unsolicited messages to the device) or such mechanism is simulated. Currently only two real push bearers are deployed widely on mobile networks, SMS and Blackberry's proprietary push solution. SMS is costly and the Blackberry solution is available only for Blackberry so the push bearer has to be simulated. The most common simulation method relies on the device to open a TCP connection to the push server. As the TCP stream is full-duplex, the server can send the push message to the device, provided that the stream is still alive. And that's the tricky bit whose complexity should not be underestimated. TCP streams time out if there is no communication on them and the other side does not necessarily notice it. A constant ping-pong traffic needs to be generated to prevent this. Frequency, however, is critical. Too frequent ping-pongs and the data cost associated to ping-ponging will be unacceptable and the battery is drawn down quickly. Too rare ping-pongs and the device or the server will not notice that the TCP stream was closed, only after a long timeout. For the user, it means that he did not get his urgent message immediately, only after, say, an hour.

There is no perfect solution to this problem, therefore it is a good feeling that Google made its best to create a simulated push bearer, along with the server that is able to keep that many TCP sockets open and shared that infrastructure with us. The Google push architecture has the following elements.

The device that authenticates and registers with the push server (Cloud to Device Messaging in Google lingo) and provides authentication ticket to the 3rd Party Application Server.

3rd Party Application Server that uses the authentication ticket generated by the device to send push requests to the push server.

Push server provided by Google that authenticates push requests from 3rd Party Application Servers and delivers the messages to the devices.

A Google account that you register with Google for push. It is better not to use your real e-mail address because you have to insert the password for this account into the server script. Use aexp.push as package name for the application in the registration form.

Do the following.

Unpack the download package. Start the Android emulator. Create a Google account if no such account exists (Settings/Accounts & sync). This account does not have to be the same that you registered for push, it can be any Google account that you know the password for.

Enter the "client" directory in the download package, update client/src/aexp/push/Config.java with the Google account you registered for push (C2DM_SENDER)

Open server/pushserver/pushserver.py and update it with your push Google account (lines 165 and 167). Unfortunately here you have to insert the password for your push Google account into the server script.

Start the Push application on the emulator, select the account using the Menu and wait for the "Registered" message to appear. Now the server application is ready to deliver push messages.

Go to http://localhost:8080 with your browser and type a message. Select the account from the drop-down list that you configured on the device and click the submit button. The message should appear on the emulator screen.

If you want to do the same trick from a real phone, you need a server with fix IP address or a fix DNS name and run the App Engine SDK there. Or better, deploy it on the real thing, on Google infrastructure. In any case, make sure that you update the server address in client/src/aexp/push/Config.java (and delete client/bin/classes directory as there are occassionally troubles with javac's dependency resolution).

About the application. The application reuses c2dm utility library from the JumpNote sample application. When the account is selected, the application registers with the push server using the phone user's credentials and the application ID that you registered with Google. Then comes the interesting bit. After the application registers with the push server, it sends the registration ID to the application server. The application server will use the registration ID to talk to the push server when it wants to do push. The server uses the Google account that you selected for push for authentication and therefore it needs the password for this account.

The implementation in the example application server program is a not efficient as it always requests an authentication token before it sends the push message. In an efficient implementation the token can be requested only once and can be used to authenticate many messages. Eventually the token expires and only then should new authentication token requested.

The client application also sends the client account name to the server but it is not used for authentication, it is only needed so that you can select the push target by account name in the web application's drop-down menu.

62 comments:

I forgot about the push notifications! Thanks for reminding us all to check this out again and providing a code snippet! Maybe you'd like to start an account on github with all your valuable code snippets. I'm interested in how Google's technical implementation will differ from Urban Airship's: http://github.com/urbanairship/android-push-sample

Anonymous, use the link in the post to download the Windows version of the Google App Engine SDK. Then tweak server/ae_pushserver.sh. Currently it is a Unix shell script but is only one line long. Turning it into a .bat file should be easy. I have never tried the App Engine SDK on Windows but it should work then without any further changes.

I added Urban Airship's link to the post, when I mention alternatives. Doing a comparison of C2DM to these would be an interesting project, maybe I do it once I get out of this pretty busy period. One thing to note: the server part of the solution that uses TCP stream to simulate push may look easy but the scalability problems should not be underestimated. Very quickly you can end up with a server that needs to handle 10s of thousands of 100s of thousands TCP connections - mostly idle connections though.

As far as I know, direct UDP to devices is blocked by operators at various levels.

Thanks the tutorial it is very helpful. I cannot however get the server side code to work. When i submit the message i get the following error : Message sent, status: UnauthorizedError 401I think im not configuring the pushserver.py you posted properly. The emulator seems to register fine as i see no error in the DDMS when i register. Any Help would be great. Thanks

OK, one more. Do you know exactly which part is unauthorized? The Android client may receive this error message when it talks to the Google App Engine SDK. Or the server part may be unauthorized when it tries to connect to Google's push server.

I tried running the server part through the SDK as proposed. The previous error seems to be gone and a new error appears, this one is a bit more descriptive when i select "Send Message" I get the following; Message Sent, status: Error=NotRegistered but I believe the simulator is registered as my simulator email is appearing in the dropdown menu.I am going to try running it on Google's server and see what happens.

Can you see the "onRegistered() done" message in the Android log? (adb logcat). There is a chance that the server side stored the registration with an earlier, successful registration attempt (this simplistic server never deletes a registration).

That's not necessary. That particular IP address in Config.java is the localhost of the machine running the emulator. It is an Android emulator convention. The reason is that localhost or 127.0.0.1 on the emulator refers to the Linux running the Android framework, the phone itself. In order to get to the real localhost (the PC running the emulator), they came up with that special address.

I have never tried this trick in Windows but it should work there too.

OK, I managed to reproduce it. When you start the Android client application and select the account, have you noticed the small black triangle in the upper left corner of the phone screen, in the notification tray? This warns you that you have to grant first right for this application to access ac2dm token. Also, for good measure, I added --clear_datastore to the script starting the server but I don't think it was the cause.

I got it all working !! :) Phew...Well I found out that you really don't need to send anything else than the registration id from the device to the server.

The server should use the authToken not from the device ID but the one which is acquired by using the ROLE Account (your gmail id in your app case). This authToken you can acquire by any means (say curl) by using your password.

So I got into their whitelist using my email id and then got the authToken using the curl command. Used this while positing the message to the server.

I get this when I push a message. Did anyone get this error?? I checked out the link http://code.google.com/appengine/kb/general.html#rpcssl but it gives me a patch to python for the ssl package but I have python 2.6 and the patch doesnt work on it.

When I open http://localhost:8080 in web browser i can't see my registered username on the list. I have already registered my account to google c2dm. Also when i try to send some message i get an error as "No registration for '-'" . How do I resolve this error?Thank You.

Thank you Gabor, it worked!!.Im just having some issues with the registration process,i´ve changed config.java in client, pushserver.py, ae_pushserver.sh (wich has changed to bat) in server side.Started the push app in the emulator and selected the account but i dont get the "Register" message and also it doesn´t apear in the drop down list in my browser.

The most common simulation method relies on the device to open a TCP connection to the push server. As the TCP stream is full-duplex, the server can send the push message to the device, provided that the stream is still alive

About the blog

This blog is a personal diary about my adventures with the Google Android platform. I write it in the hope that others may find my experiences useful but please, beware. The blog is created as I gain experience about the platform myself so errors, omissions, etc. may be found in the entries.