page.title=GCM Architectural Overview @jd:body
Google Cloud Messaging for Android (GCM) is a service that helps developers send data from servers to their Android applications on Android devices. This could be a lightweight message telling the Android application that there is new data to be fetched from the server (for instance, a movie uploaded by a friend), or it could be a message containing up to 4kb of payload data (so apps like instant messaging can consume the message directly). The GCM service handles all aspects of queueing of messages and delivery to the target Android application running on the target device.
To jump right into using GCM with your Android applications, see the instructions in Getting Started.
Here are the primary characteristics of Google Cloud Messaging (GCM):
This section gives an overview of how GCM works.
This table summarizes the key terms and concepts involved in GCM. It is divided into these categories:
Components | |
---|---|
Mobile Device | The device that is running an Android application that uses GCM. This must be a 2.2 Android device that has Google Play Store installed, and it must have at least one logged in Google account if the device is running a version lower than Android 4.0.4. Alternatively, for testing you can use an emulator running Android 2.2 with Google APIs. |
3rd-party Application Server | An application server that developers set up as part of implementing GCM in their applications. The 3rd-party application server sends data to an Android application on the device via the GCM server. |
GCM Servers | The Google servers involved in taking messages from the 3rd-party application server and sending them to the device. |
Credentials | |
Sender ID | A project ID you acquire from the API console, as described in Getting Started. The sender ID is used in the registration process to identify an Android application that is permitted to send messages to the device. |
Application ID | The Android application that is registering to receive messages. The Android application is identified by the package name from the manifest. This ensures that the messages are targeted to the correct Android application. |
Registration ID | An ID issued by the GCM servers to the Android application that allows it to receive messages. Once the Android application has the registration ID, it sends it to the 3rd-party application server, which uses it to identify each device that has registered to receive messages for a given Android application. In other words, a registration ID is tied to a particular Android application running on a particular device. |
Google User Account | For GCM to work, the mobile device must include at least one Google account if the device is running a version lower than Android 4.0.4. |
Sender Auth Token | An API key that is saved on the 3rd-party application server that gives the application server authorized access to Google services. The API key is included in the header of POST requests that send messages. |
Here are the primary processes involved in cloud-to-device messaging:
These processes are described in more detail below.
This is the sequence of events that occurs when an Android application running on a mobile device registers to receive messages:
This registration Intent
(com.google.android.c2dm.intent.REGISTER
) includes the sender ID, and the Android application ID.
Note: Because there is no lifecycle method that is called when the application is run for
the first time, the registration intent should be sent on onCreate()
, but only if the application is not registered yet.
com.google.android.c2dm.intent.REGISTRATION
intent which gives the Android application a registration
ID.
The Android application should store this ID for later use (for instance, to check on onCreate()
if it is already registered).
Note that Google may periodically refresh the registration ID, so you should design your Android application
with the understanding that the com.google.android.c2dm.intent.REGISTRATION
intent may be called
multiple times. Your Android application needs to be able to respond
accordingly.
The registration ID lasts until the Android application explicitly unregisters itself, or until Google refreshes the registration ID for your Android application.
Note: When users uninstall an application, it is not automatically unregistered on GCM. It is only unregistered when the GCM server tries to send a message to the device and the device answers that the application is uninstalled. At that point, you server should mark the device as unregistered (the server will receive a NotRegistered
error).
Note that it might take a few minutes for the registration ID to be completed removed from the GCM server. So if the 3rd party server sends a message during this time, it will get a valid message ID, even though the message will not be delivered to the device.
For an application server to send a message to an Android application, the following things must be in place:
Here is the sequence of events that occurs when the application server sends a message:
An Android application can unregister GCM if it no longer wants to receive messages.
This is the sequence of events that occurs when an Android application installed on a mobile device receives a message:
com.google.android.c2dm.intent.RECEIVE
Intent as a set of
extras.com.google.android.c2dm.intent.RECEIVE
Intent by key and processes the data.When mobile device users install Android applications that include GCM, the Google Play Store will inform them that the Android application includes GCM. They must approve the use of this feature to install the Android application.
To write Android applications that use GCM, you must have an application server that can perform the tasks described in Role of the 3rd-party Application Server. This section describes the steps you take to create a client application that uses GCM.
Remember that there is no user interface associated with GCM. However you choose to process messages in your Android application is up to you.
There are two primary steps involved in writing a client Android application:
com.google.android.c2dm.intent.RECEIVE
and com.google.android.c2dm.intent.REGISTRATION
intents.Every Android application must have an AndroidManifest.xml
file (with
precisely that name) in its root directory. The manifest presents essential
information about the Android application to the Android system, information the
system must have before it can run any of the Android application's code (for more
discussion of the manifest file, see the Android Developers Guide. To use the GCM feature, the
manifest must include the following:
com.google.android.c2dm.permission.RECEIVE
permission so the Android application can register and receive messages.android.permission.INTERNET
permission so the Android application can send the registration ID to the 3rd party server.android.permission.GET_ACCOUNTS
permission as GCM requires a Google account (necessary only if if the device is running a version lower than Android 4.0.4)android.permission.WAKE_LOCK
permission so the application can keep the processor from sleeping when a message is received.applicationPackage + ".permission.C2D_MESSAGE
permission to prevent other Android applications from registering and receiving the Android application's
messages. The permission name must exactly match this pattern—otherwise the Android application will not receive the messages.com.google.android.c2dm.intent.RECEIVE
and com.google.android.c2dm.intent.REGISTRATION
, with the category set
as applicationPackage
. The receiver should require the com.google.android.c2dm.SEND
permission, so that only the GCM
Framework can send a message to it. Note that both registration and the receiving
of messages are implemented as Intents.android:minSdkVersion="8"
in the manifest. This
ensures that the Android application cannot be installed in an environment in which it
could not run properly. Here are excerpts from a manifest that supports GCM:
<manifest package="com.example.gcm" ...> <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16"/> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <permission android:name="com.example.gcm.permission.C2D_MESSAGE" android:protectionLevel="signature" /> <uses-permission android:name="com.example.gcm.permission.C2D_MESSAGE" /> <application ...> <receiver android:name=".MyBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" > <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> <category android:name="com.example.gcm" /> </intent-filter> </receiver> <service android:name=".MyIntentService" /> </application> </manifest>
An Android application needs to register with GCM servers before it can receive messages. To register, the application sends an Intent
(com.google.android.c2dm.intent.REGISTER
), with 2 extra parameters:
sender
is the project ID of the account authorized to send messages
to the Android application. app
is the Android application's ID, set with a PendingIntent
to
allow the registration service to extract Android application information. For example:
Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER"); // sets the app name in the intent registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0)); registrationIntent.putExtra("sender", senderID); startService(registrationIntent);
This intent will be asynchronously sent to the GCM server, and the response will be delivered to
the application as a com.google.android.c2dm.intent.REGISTRATION
intent containing
the registration ID assigned to the Android application running on that particular device.
Registration is not complete until the Android application sends the registration ID to the 3rd-party application server, which in turn will use the registration ID to send messages to the application.
To unregister from GCM, do the following:
Intent unregIntent = new Intent("com.google.android.c2dm.intent.UNREGISTER"); unregIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0)); startService(unregIntent);
Similar to the registration request, this intent is sent asynchronously, and the response comes as a com.google.android.c2dm.intent.REGISTRATION
intent.
As discussed in Creating the Manifest, the manifest
defines a broadcast receiver for the com.google.android.c2dm.intent.REGISTRATION
and com.google.android.c2dm.intent.RECEIVE
intents.
These intents are sent by GCM to indicate that a device was registered (or unregistered), or to deliver messages, respectively.
Handling these intents might require I/O operations (such as network calls to the 3rd party server), and
such operations should not be done in the receiver's onReceive()
method.
You may be tempted to spawn a new thread directly, but there are no guarantees that the process will run long enough for the thread to finish the work.
Thus the recommended way to handle the intents is to delegate them to a service, such as an {@link android.app.IntentService}.
For example:
public class MyBroadcastReceiver extends BroadcastReceiver { @Override public final void onReceive(Context context, Intent intent) { MyIntentService.runIntentInService(context, intent); setResult(Activity.RESULT_OK, null, null); } }
Then in MyIntentService
:
public class MyIntentService extends IntentService { private static PowerManager.WakeLock sWakeLock; private static final Object LOCK = MyIntentService.class; static void runIntentInService(Context context, Intent intent) { synchronized(LOCK) { if (sWakeLock == null) { PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); sWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "my_wakelock"); } } sWakeLock.acquire(); intent.setClassName(context, MyIntentService.class.getName()); context.startService(intent); } @Override public final void onHandleIntent(Intent intent) { try { String action = intent.getAction(); if (action.equals("com.google.android.c2dm.intent.REGISTRATION")) { handleRegistration(intent); } else if (action.equals("com.google.android.c2dm.intent.RECEIVE")) { handleMessage(intent); } } finally { synchronized(LOCK) { sWakeLock.release(); } } } }
Note: your application must acquire a wake lock before starting the service—otherwise the device could be put to sleep before the service is started.
When a com.google.android.c2dm.intent.REGISTRATION
intent is received, it could potentially contain 3 extras: registration_id
, error
, and unregistered
.
When a registration succeeds, registration_id
contains the registration ID and the other extras are not set.
The application must ensure that the 3rd-party server receives the registration ID. It may do so by saving the registration ID and sending it to the server.
If the network is down or there are errors, the application should retry sending the registration ID when the network is up again or the next time it starts.
Note: Although the com.google.android.c2dm.intent.REGISTRATION
intent is typically received after a request was made by the application,
Google may periodically refresh the registration ID. So the application must be prepared to handle it at any time.
When an unregistration succeeds, only the unregistered
extra is set, and similar to the registration workflow,
the application must contact the 3rd-party server to remove the registration ID (note that the registration ID is not available in the intent,
but the application should have saved the registration ID when it got it).
If the application request (be it register or unregister) fails, the error
will be set with an error code, and the other extras will not be set.
Here are the possible error codes:
Error Code | Description |
---|---|
SERVICE_NOT_AVAILABLE |
The device can't read the response, or there was a 500/503 from the server that can be retried later. The Android application should use exponential back-off and retry. See Advanced Topics for more information. |
ACCOUNT_MISSING |
There is no Google account on the phone. The Android application should ask the user to open the account manager and add a Google account. Fix on the device side. |
AUTHENTICATION_FAILED |
Bad Google Account password. The Android application should ask the user to enter his/her Google Account password, and let the user retry manually later. Fix on the device side. |
INVALID_SENDER |
The sender account is not recognized. This must be fixed on the Android application side. The developer must fix the application to provide the right sender extra in the com.google.android.c2dm.intent.REGISTER intent. |
PHONE_REGISTRATION_ERROR |
Incorrect phone registration with Google. This phone doesn't currently support GCM. |
INVALID_PARAMETERS |
The request sent by the phone does not contain the expected parameters. This phone doesn't currently support GCM. |
Here's an example on how to handle the registration in the MyIntentService
example:
private void handleRegistration(Intent intent) { String registrationId = intent.getStringExtra("registration_id"); String error = intent.getStringExtra("error"); String unregistered = intent.getStringExtra("unregistered"); // registration succeeded if (registrationId != null) { // store registration ID on shared preferences // notify 3rd-party server about the registered ID } // unregistration succeeded if (unregistered != null) { // get old registration ID from shared preferences // notify 3rd-party server about the unregistered ID } // last operation (registration or unregistration) returned an error; if (error != null) { if ("SERVICE_NOT_AVAILABLE".equals(error)) { // optionally retry using exponential back-off // (see Advanced Topics) } else { // Unrecoverable error, log it Log.i(TAG, "Received error: " + error); } } }
The com.google.android.c2dm.intent.RECEIVE
intent is used by GCM to
deliver the messages sent by the 3rd-party server to the application running in the device.
If the server included key-pair values in the data
parameter, they are available as
extras in this intent, with the keys being the extra names.
Here is an example, again using the MyIntentReceiver
class:
private void handleMessage(Intent intent) { // server sent 2 key-value pairs, score and time String score = intent.getExtra("score"); String time = intent.getExtra("time"); // generates a system notification to display the score and time }
Here are some guidelines for developing and testing an Android application that uses the GCM feature:
android:minSdkVersion="8"
in the manifest. This
ensures that the Android application cannot be installed in an environment in which it
could not run properly. Before you can write client Android applications that use the GCM feature, you must have an application server that meets the following criteria:
This section describes how the 3rd-party application server sends messages to one or more mobile devices. Note the following:
Before the 3rd-party application server can send a message to an Android application, it must have received a registration ID from it.
To send a message, the application server issues a POST request to https://android.googleapis.com/gcm/send
.
A message request is made of 2 parts: HTTP header and HTTP body.
The HTTP header must contain the following headers:
Authorization
: key=YOUR_API_KEYContent-Type
: application/json
for JSON; application/x-www-form-urlencoded;charset=UTF-8
for plain text.
For example:
Content-Type:application/json Authorization:key=AIzaSyB-1uEai2WiUapxCs2Q0GZYzPu7Udno5aA { "registration_id" : "APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx...", "data" : { ... }, }
Note: If Content-Type
is omitted, the format is assumed to be plain text.
The HTTP body content depends on whether you're using JSON or plain text. For JSON, it must contain a string representing a JSON object with the following fields:
Field | Description |
---|---|
registration_ids |
A string array with the list of devices (registration IDs) receiving the message. It must contain at least 1 and at most 1000 registration IDs. To send a multicast message, you must use JSON. For sending a single message to a single device, you could use a JSON object with just 1 registration id, or plain text (see below). Required. |
collapse_key |
An arbitrary string (such as "Updates Available") that is used to collapse a group of like messages
when the device is offline, so that only the last message gets sent to the
client. This is intended to avoid sending too many messages to the phone when it
comes back online. Note that since there is no guarantee of the order in which
messages get sent, the "last" message may not actually be the last
message sent by the application server. See Advanced Topics for more discussion of this topic. Optional, unless you are using the time_to_live parameter—in that case, you must also specify a collapse_key . |
data |
A JSON object whose fields represents the key-value pairs of the message's payload data. If present, the payload data it will be
included in the Intent as application data, with the key being the extra's name. For instance, "data":{"score":"3x1"} would result in an intent extra named score whose value is the string 3x1
There is no limit on the number of key/value pairs, though there is a limit on the total size of the message. Optional. |
delay_while_idle |
If included, indicates that the message should not be sent immediately
if the device is idle. The server will wait for the device to become active, and
then only the last message for each collapse_key value will be
sent. Optional. The default value is false , and must be a JSON boolean. |
time_to_live |
How long (in seconds) the message should be kept on GCM storage if the device is offline. Optional (default time-to-live is 4 weeks, and must be set as a JSON number). If you use this parameter, you must also specify a collapse_key . |
If you are using plain text instead of JSON, the message fields must be set as HTTP parameters sent in the body, and their syntax is slightly different, as described below:
Field | Description |
---|---|
registration_id |
Must contain the registration ID of the single device receiving the message. Required. |
collapse_key |
Same as JSON (see previous table). Optional. |
data.<key> |
Payload data, expressed as parameters prefixed with data. and suffixed as the key. For instance, a parameter of data.score=3x1 would result in an intent extra named score whose value is the string 3x1 . There is no limit on the number of key/value parameters, though there is a limit on the total size of the message. Optional. |
delay_while_idle |
Should be represented as 1 or true for true , anything else for false . Optional. The default value is false . |
time_to_live |
Same as JSON (see previous table). Optional. |
Here is the smallest possible request (a message without any parameters and just one recipient) using JSON:
{ "registration_ids": [ "42" ] }
And here the same example using plain text:
registration_id=42
Here is a message with a payload and 6 recipients:
{ "data": { "score": "5x1", "time": "15:10" }, "registration_ids": ["4", "8", "15", "16", "23", "42"] }
Here is a message with all optional fields set and 6 recipients:
{ "collapse_key": "score_update", "time_to_live": 108, "delay_while_idle": true, "data": { "score": "4x8", "time": "15:16.2342" }, "registration_ids":["4", "8", "15", "16", "23", "42"] }
And here is the same message using plain-text format (but just 1 recipient):
collapse_key=score_update&time_to_live=108&delay_while_idle=1&data.score=4x8&data.time=15:16.2342®istration_id=42
Note: If your organization has a firewall that restricts the traffic to or from the Internet, you'll need to configure it to allow connectivity with GCM. The ports to open are: 5228, 5229, and 5230. GCM typically only uses 5228, but it sometimes uses 5229 and 5230. GCM doesn't provide specific IPs. It changes IPs frequently. We recommend against using ACLs but if you must use them, take a broad approach such as the method suggested in this support link.
There are two possible outcomes when trying to send a message:
When the messge is processed successfully, the HTTP response has a 200 status and the body contains more information about the status of the message (including possible errors). When the request is rejected, the HTTP response contains a non-200 status code (such as 400, 401, or 503).
The following table summarizes the statuses that the HTTP response header might contain. Click the troubleshoot link for advice on how to deal with each type of error.
Response | Description |
---|---|
200 | Message was processed successfully. The response body will contain more details about the message status, but its format will depend whether the request was JSON or plain text. See Interpreting a success response for more details. |
400 | Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields (for instance, passing a string where a number was expected). The exact failure reason is described in the response and the problem should be addressed before the request can be retried. |
401 | There was an error authenticating the sender account. Troubleshoot |
500 | There was an internal error in the GCM server while trying to process the request. Troubleshoot |
503 | Indicates that the server is temporarily unavailable (i.e., because of timeouts, etc ). Sender must retry later, honoring any Retry-After header
included in the response. Application servers must implement exponential back-off. The GCM server took too long to process the request. Troubleshoot |
When a JSON request is successful (HTTP status code 200), the response body contains a JSON object with the following fields:
Field | Description |
---|---|
multicast_id |
Unique ID (number) identifying the multicast message. |
success |
Number of messages that were processed without an error. |
failure |
Number of messages that could not be processed. |
canonical_ids |
Number of results that contain a canonical registration ID. See Advanced Topics for more discussion of this topic. |
results |
Array of objects representing the status of the messages processed. The objects are listed in the same order as the request (i.e., for each registration ID in the request, its result is listed in the same index in the response) and they can have these fields:
|
If the value of failure
and canonical_ids
is 0, it's not necessary to parse the remainder of the response. Otherwise, we recommend that you iterate through the results field and do the following for each object in that list:
message_id
is set, check for registration_id
:
registration_id
is set, replace the original ID with the new value (canonical ID) in your server database. Note that the original ID is not part of the result, so you need to obtain it from the list of registration_ids
passed in the request (using the same index).error
:
Unavailable
, you could retry to send it in another request.NotRegistered
, you should remove the registration ID from your server database because the application was uninstalled from the device.When a plain-text request is successful (HTTP status code 200), the response body contains 1 or 2 lines in the form of key/value pairs.
The first line is always available and its content is either id=ID of sent message
or Error=GCM error code
. The second line, if available,
has the format of registration_id=canonical ID
. The second line is optional, and it can only be sent if the first line is not an error. We recommend handling the plain-text response in a similar way as handling the JSON response:
id
, check second line:
registration_id
, gets its value and replace the registration IDs in your server database.Error
:
NotRegistered
, remove the registration ID from your server database.Unavailable
as the error code, they would have returned a 500 HTTP status instead).Here are the recommendations for handling the different types of error that might occur when trying to send a message to a device:
registration_id
parameter in a plain text message, or in the registration_ids
field in JSON).
MissingRegistration
.com.google.android.c2dm.intent.REGISTRATION
intent and that you're not truncating it or adding additional characters.
InvalidRegistration
.MismatchSenderId
.com.google.android.c2dm.intent.UNREGISTER
intent.NotRegistered
.MessageTooBig
.Authorization
header is the correct API key associated with your project.
Retry-After
header if it's included in the response from the GCM server.error
field of a JSON object in the results
array is Unavailable
.
This section shows a few examples of responses indicating messages that were processed successfully. See Example requests for the requests these responses are based on.
Here is a simple case of a JSON message successfully sent to one recipient without canonical IDs in the response:
{ "multicast_id": 108, "success": 1, "failure": 0, "canonical_ids": 0, "results": [ { "message_id": "1:08" } ] }
Or if the request was in plain-text format:
id=1:08
Here are JSON results for 6 recipients (IDs 4, 8, 15, 16, 23, and 42 respectively) with 3 messages successfully processed, 1 canonical registration ID returned, and 3 errors:
{ "multicast_id": 216, "success": 3, "failure": 3, "canonical_ids": 1, "results": [ { "message_id": "1:0408" }, { "error": "Unavailable" }, { "error": "InvalidRegistration" }, { "message_id": "1:1516" }, { "message_id": "1:2342", "registration_id": "32" }, { "error": "NotRegistered"} ] }
In this example:
Or if just the 4th message above was sent using plain-text format:
Error=InvalidRegistration
If the 5th message above was also sent using plain-text format:
id=1:2342 registration_id=32
To view statistics and any error messages for your GCM applications:
play.google.com/apps/publish
.You will see a page that has a list of all of your apps.
Now you are on the statistics page.
See the GCM Demo Application document.