Google Cloud Messaging (GCM) is a service that allows messages to be sent directly to devices that have your application installed. New features were introduced at Google I/O 2013 including: upstream messaging (device-to-cloud), seamless multi-device messaging via User Notifications, and GCM Cloud Connection Server (CCS). This post will detail the setup and use of GCM via CCS, demonstrating the new features. The source code for the demo can be found at: https://github.com/antoinecampbell/GCMCCSDemo.

Watch Francesco Nerieri's Google I/O 2013 presentation on GCM for an understanding of the basics. Note that in Google's nomenclature, your CCS application is referred to as the ì3rd-party application serverî.

Previously, GCM could only be used to send downstream messages (cloud-to-device) via HTTP. A GCM message sent with the HTTP API can be sent up to 1000 users, requiring multiple messages for large numbers of users. The new API offers an alternate method using XMPP, the GCM Cloud Connection Server. The CCS allows for a constant connection to Google's GCM servers and messages to be sent at the max wire speed. Google throttles this speed to be 4,000 messages per second at maximum, per connection; with a 10-connection maximum. When using all 10 connections you can send 40,000 messages per second. A key difference is that messages sent using Google's CCS is one message per device, rather than messages with multiple recipients.

Uses

GCM messages can be used in many different ways to enhance an application. The example used during the Google I/O presentation was a surfing app that informs users about good wave conditions by sending a downstream message to the device and showing a notification on the device. This feature removes the necessity for routine polling for certain data, which has a significant impact on battery life. Another use could be to inform all of your users about a new app release, possibly one that requires new permissions that would not update automatically.

Applications such as Facebook and Google+ use GCM to inform users about friends that have checked in nearby; the user received a message; or information about a post related their profile. GCM allows these applications to minimize data usage by removing the need for background polling. The most common result of an incoming message is to display a notification about the received information, but it's really up to you to decide what to do with the incoming message. Some applications use the incoming message to cause the app to sync in the background (e.g. Gmail).

GCM Setup

Before GCM can be used within an application there are some steps you must take to enable the service.

1. Create a project in your Google API Console.

  • If you already have a project you can simply enable GCM for your existing project. You will need the
ProjectID later so be sure to hold on to it. It is part of the url when viewing your project page e.g. https://code.google.com/apis/console/#project:4815162342 your ProjectID would be: 4815162342

2. Enable the GCM service. Click on the ëServices' menu item and find ëGoogle Cloud Messaging for Android' and enable it.


3. Now you will need to create a Server Key to send GCM messages from your server. Click ëAPI Access' then create a new Server Key if you do not already have one. You'll also have the option here to whitelist specific IP addresses or leave blank to allow use from any address.

  • Now you have what you need to connect to GCM using CCS. The
ProjectID will be used as a sender ID when sending upstream messages from a device and as a username when connecting to Google's CCS. The Server Key will only be used by your server application as a password when connecting to Google's CCS.

NOTE: After you have created your GCM enabled project in the API Console you must fill out this form and become a trial partner to use upstream messaging and user notifications via CCS. Access is limited to those that fill out the form. You will receive an email from Google informing you that you now have access; Google will also send you the address of an echo server that you can use to bounce messages back to your application.

Android Demo Application Setup

Lets begin by setting up Android application. You'll need to add your ProjectID to Globals.java:

public static final String GCM_SENDER_ID = "Your ProjectID";

Once you add the ProjectID the sample application will be ready to send and receive messages with the demo Android application.

CCS Demo Application Setup

Similar to the Android application, the CCS application will need your ProjectID as well as your ServerKey entered before you can begin to use it. Add them to SmackCcsClient.java:

public static final String GCM_SENDER_ID = "Your ProjectID";
public static final String GCM_SERVER_KEY = "Your ServerKey";

Now that the ProjectID and ServerKey has been added you can begin using the CCS application. It will communicate with Google's GCM servers to send downstream messages to devices and receive upstream messages sent by devices.

Android Application Overview

The demo Android application allows the user to send and receive several types of GCM messages. The Android application requires: a few unusual permissions, a BroadcastReceiver to receive messages, and it's recommended to use an IntentService. The IntentService allows your application to do any sort of time-consuming tasks should they be required, such as downloading data. A BroadcastReceiver is ìaggressively killedî by the operating system once the onReceive() method returns, so any time-consuming task should be passed along to a Service, such as an IntentService. In the demo application received messages simply show or dismiss status bar notifications, but still utilize an IntentService to perform the task, as it is a best practice.

Permissions

Notice that GCM requires that the application define a new permission for use, com.antoinecampbell.gcmdemo.permission.C2D_MESSAGE. This permission prevents other applications from receiving the GCM message for your application.

<> android:name="android.permission.GET_ACCOUNTS"/>
<> android:name="android.permission.INTERNET"/>
<> android:name="android.permission.USE_CREDENTIALS"/>
<> android:name="com.google.android.c2dm.permission.RECEIVE"/>
<> android:name="android.permission.WAKE_LOCK"/>
 
<> 
 android:name="com.antoinecampbell.gcmdemo.permission.C2D_MESSAGE" 
 android:protectionLevel="signature"/>
<> android:name="com.antoinecampbell.gcmdemo.permission.C2D_MESSAGE"/>

Components

The SEND permission ensures only the GCM Framework can send messages to the BroadcastReceiver. The IntentService in the demo application does the actual handling of the message it was passed by the BroadcastReceiver.

<> 
 android:name=".GcmBroadcastReceiver" 
 android:permission="com.google.android.c2dm.permission.SEND">
 <>>
 <> android:name="com.google.android.c2dm.intent.RECEIVE"/>
 <> android:name="com.google.android.c2dm.intent.REGISTRATION"/>
 <> android:name="com.antoinecampbell.gcmdemo"/>
 >
>
<> android:name=".GcmIntentService">>

Device Registration

Before any device can be used with GCM it must first register with the GCM servers. This can be done using the GoogleCloudMessaging class within the Google Play Services library. This is a blocking call so it should be performed in the background using an AsyncTask. A registration ID will be returned that uniquely identifies the device for the ProjectID used. Registration should occur in the onCreate() method of your activity, or using a dialog if you would like to give the user a choice about receiving GCM messages. This is generally something you do only once. The demo shows how to unregister as well, though it is not a common action. The only time you may want to unregister a device is if you allow the user to opt-out of receiving GCM messages within your application.

Once the registration ID is retrieved, it should be sent to your server to be saved in a database and stored locally on the device. The demo application stores the registration ID in SharedPreferences and sends the ID off to the CCS application using a GCM message. It is recommended that this be done via HTTP so you have an immediate answer on its success or failure. The CCS application handles the same functions an HTTP server would by storing the usernames and device registration IDs.

Android Application Code
gcm = GoogleCloudMessaging.getInstance(context);
regid = gcm.register(Globals.GCM_SENDER_ID);
// You should send the registration ID to your server via HTTP
sendRegistrationIdToBackend();
// Persist the regID
storeRegistrationId(context, regid);
CCS Application Code
public void handleIncomingDataMessage(MapString, Object> jsonObject) {
...
 String from = jsonObject.get("from").toString();
 MapString, String> payload = 
 MapString, String>) jsonObject.get("data");
 String action = payload.get("action");
 if(action.equalsIgnoreCase("com.antoinecampbell.gcmdemo.REGISTER")) {
 String name = payload.get("name").toString();
 // Store username and registration ID in DB
 addUser(name, from);
 // Send a REGISTER response back
 payload.put("message", "Registration successful");
 String echo = createJsonMessage(
 from, getRandomMessageId(), payload, null, null, false);
 send(echo);
 }
...
}

Sending Messages

Anatomy of a Message

A GCM message is essentially a JSON object containing some required and optional fields that enable different message delivery behaviors. More information about message fields can be found in the GCM documentation.

  • to: ex. AP9vj2372321314fsd...
  • message_id: unique message id
  • data: custom JSON object for your application-specific data
  • time_to_live: (optional) seconds message is valid, default 2 weeks
  • delay_while_idle: (optional) boolean whether to deliver message to an idle device, default false
{
"to":"DEVICE_REGISTRATION_ID",
"message_id":"m-1366082849205",
"data":
{
 "message":"hello world",
 "action":"com.antoinecampbell.gcmdemo.ECHO",
}
"time_to_live":"600",
"delay_while_idle": false
}

Echo Message

The Echo message in the demo app demonstrates a device sending an upstream message to the GCM servers and the CCS application sending the same message downstream back to the device. This type of message shows the basic usage of upstream and downstream messaging. On the device-side the GCM framework uses an existing connection to Google Play services to send the message upstream. The GCM framework also caches the message locally on the device and on the GCM servers until it is sent, which protects against temporary connectivity issues.

Android Application Code
...
Bundle data = new Bundle();
data.putString("message", params[0]);
data.putString("action", "com.antoinecampbell.gcmdemo.ECHO");
String id = Integer.toString(msgId.incrementAndGet());
gcm.send(Globals.GCM_SENDER_ID + "@gcm.googleapis.com", 
 id, Globals.GCM_TIME_TO_LIVE, data);
...
CCS Application Code
public void handleIncomingDataMessage(MapString, Object> jsonObject) {
...
 String from = jsonObject.get("from").toString();
 MapString, String> payload = 
 MapString, String>) jsonObject.get("data");
 String action = payload.get("action");
 if(action.equalsIgnoreCase("com.antoinecampbell.gcmdemo.ECHO")) {
 // Send an ECHO response back
 String echo = createJsonMessage(
 from, getRandomMessageId(), payload, null, null, false);
 send(echo);
 }
...
}

Broadcast Message

The demo Android application can also send a Broadcast message. This is very similar to the Echo message, however this message is sent to all users including the sender. This type of message could be used to inform all users about an app update. This message just leaves the device with a different action value in the GCM message.

Android Application Code
data.putString("action", "com.antoinecampbell.gcmdemo.BROADCAST");
CCS Application Code
public void handleIncomingDataMessage(MapString, Object> jsonObject) {
...
 if(action.equalsIgnoreCase("com.antoinecampbell.gcmdemo.BROADCAST")) {
 // Send a Broadcast response back
 broadcastMessage(payload);
 }
...
}
/**
* Send GCM message to all registered devices
*/
public void broadcastMessage(MapString, String> payload) {
 try {
 ListUser> users = userDao.queryForAll();
 for(User user : users) {
 String broadcast = createJsonMessage(
 user.getGcm_id(), getRandomMessageId(), 
 payload, null, null, false);
 send(broadcast);
 }
 }
 catch (SQLException e) {
 e.printStackTrace();
 }
}

User Notification Message

Last but certainly not least is the User Notification message. This type of GCM message allows a user's devices to be grouped together so that a single message can be sent to all their devices at once. The unique aspect of this type of message is that it is necessary to create a user notification key when a device is registered that can be used to group all related devices together. Similar to a device's registration ID, the user notification key is used as an address to send messages. In the demo application the user's email address is used to distinguish devices of the same user. You will need to use an alias of the user that is unique across installs such as an email address or username. To create a user notification key for the user, a JSON request must be sent to Google's GCM Notification endpoint. More details about its creation can be found in the GCM documentation.

The main benefit of a User Notification message is to allow a GCM message to be sent to all of a user's devices but also allow the dismissal of notifications on other devices. For example, in the demo application when 2 or more devices are registered under the same email address, ìNotificationî messages are delivered to all those devices. If the notification is clicked from one of the device's status bar, it will trigger a GCM message to be sent upstream to the other devices dismissing the notification, as it has already been handled on a device. The great part about this is that the CCS application does not need to be involved when a device sends a message to a user notification key. The message is delivered to Google's CCS and delivered to the user's other devices. It is really up to the developer what to do with the message, but in the demo application they're used to dismiss the notifications from the status bar of other devices.

In the demo application, a NOTIFICATION message is sent up to the CCS application much like other messages, however the action field is set to com.antoinecampbell.gcmdemo.NOTIFICATION letting the server know to send a message downstream to the user's notification key. The notification key for a user's device is only stored on the server (CCS application). This particular downstream message includes the notification key in the message. This allows any device that receives the message to utilize the notification key, sending a message to the user's other devices, to dismiss the status bar notifications.

Android Application Code
/**
* If this activity was started or brought to the front using an intent 
* from a notification type GCM message inform other devices the message
* was handled
* @param extras Extras bundle from incoming intent
*/
private void handleNotification(Bundle extras) {
 if(extras != null && extras.containsKey("action") 
 && extras.containsKey("notification_key")
 && "com.antoinecampbell.gcmdemo.NOTIFICATION".
 equalsIgnoreCase(extras.getString("action"))) {
 // Send a notification clear message upstream to clear on other devices
 sendClearMessage(extras.getString("notification_key"));
 }
}

Conclusion

The new CCS API for GCM messages has made sending messages faster than the HTTP API, but with the added complexity of an asynchronous process. Its advantages are quickly apparent when using the increased message cap to send a broadcast to a large user base or in cases where messages need to be sent to a user's other devices. Though some complexity has been added to GCM with the CCS, it also adds the ability to create a better user experience.

All source code is available on GitHub.