Home | History | Annotate | Download | only in gcm
      1 page.title=GCM Advanced Topics
      2 @jd:body
      3 
      4 <div id="qv-wrapper">
      5 <div id="qv">
      6 
      7 <h2>Quickview</h2>
      8 
      9 <ul>
     10 <li>Learn more about GCM advanced features.</li>
     11 </ul>
     12 
     13 
     14 <h2>In this document</h2>
     15 
     16 <ol>
     17 <li><a href="#lifetime">Lifetime of a Message</a></li>
     18 <li><a href="#throttling">Throttling</a></li>
     19 <li><a href="#reg-state">Keeping the Registration State in Sync</a>
     20   <ol>
     21     <li><a href="#canonical">Canonical IDs</a></li>
     22   </ol>
     23 </li>
     24 <li><a href="#retry">Automatic Retry Using Exponential Back-Off</a></li>
     25 <li><a href="#unreg">How Unregistration Works</a></li>
     26 <li><a href="#collapsible">Send-to-Sync vs. Messages with Payload</a>
     27   <ol>
     28     <li><a href="#s2s">Send-to-sync messages</a></li>
     29     <li><a href="#payload">Messages with payload</a></li>
     30 <li><a href="#which">Which should I use?</a></li>
     31     </ol>
     32 </li>
     33 <li><a href="#ttl">Setting an Expiration Date for a Message</a> </li>
     34 <li><a href="#throttling"></a><a href="#multi-senders">Receiving Messages from Multiple Senders</a></li>
     35 </ol>
     36 
     37 </div>
     38 </div>
     39 <p>This document covers advanced topics for GCM.</p>
     40 
     41 
     42 
     43 
     44 <h2 id="msg-lifetime">Lifetime of a Message</h2>
     45 <p>When a 3rd-party server posts a message to GCM and receives a message ID back, it does not mean that the message was already delivered to the device. Rather, it means that it was accepted for delivery. What happens to the message after it is accepted depends on many factors.</p>
     46 <p>In the best-case scenario, if the device is connected to GCM, the screen is on, and there are no throttling restrictions (see <a href="#throttling">Throttling</a>), the message will be delivered right away.</p>
     47 <p>If the device is connected but idle, the message will still be
     48 delivered right away unless the <code>delay_while_idle</code> flag is set to true. Otherwise, it will be stored in the GCM servers until the device is awake. And that's where the <code>collapse_key</code> flag plays a role: if there is already a message with the same collapse key (and registration ID) stored and waiting for delivery, the old message will be discarded and the new message will take its place (that is, the old message will be collapsed by the new one). However, if the collapse key is not set, both the new and old messages are stored for future delivery.</p>
     49 
     50 <p class="note"><strong>Note:</strong> There is a limit on how many messages can be stored without collapsing. That limit is currently 100. If the limit is reached, all stored messages are discarded. Then when the device is back online, it receives a special message indicating that the limit was reached. The application can then handle the situation properly, typically by requesting a full sync.</p>
     51 
     52 <p>If the device is not connected to GCM, the message will be stored until a connection is established (again respecting the collapse key rules). When a connection is established, GCM will deliver all pending messages to the device, regardless of the <code>delay_while_idle</code> flag. If the device never gets connected again (for instance, if it was factory reset), the message will eventually time out and be discarded from GCM storage. The default timeout is 4 weeks, unless the <code>time_to_live</code> flag is set.</p>
     53 
     54 <p>Finally, when GCM attempts to deliver a message to the device and the application was uninstalled, GCM will discard that message right away and invalidate the registration ID. Future attempts to send a message to that device will get a <code>NotRegistered</code> error. See <a href="#unreg">How Unregistration Works</a> for more information.</p>
     55 <p>Although is not possible to track the status of each individual message, the Google APIs Console stats are broken down by messages sent to device, messages collapsed, and messages waiting for delivery.</p>
     56 
     57 <h2 id="throttling">Throttling</h2>
     58 <p>To prevent abuse (such as sending a flood of messages to a device) and
     59 to optimize for the overall network efficiency and battery life of
     60 devices, GCM implements throttling of messages using a token bucket
     61 scheme. Messages are throttled on a per application and per <a href="#collapsible">collapse
     62 key</a> basis (including non-collapsible messages). Each application
     63 collapse key is granted some initial tokens, and new tokens are granted
     64 periodically therefter. Each token is valid for a single message sent to
     65 the device. If an application collapse key exhausts its supply of
     66 available tokens, new messages are buffered in a pending queue until
     67 new tokens become available at the time of the periodic grant. Thus
     68 throttling in between periodic grant intervals may add to the latency
     69 of message delivery for an application collapse key that sends a large
     70 number of messages within a short period of time. Messages in the pending
     71 queue of an application collapse key may be delivered before the time
     72 of the next periodic grant, if they are piggybacked with messages
     73 belonging to a non-throttled category by GCM for network and battery
     74 efficiency reasons.</p>
     75 
     76 <h2 id="reg-state">Keeping the Registration State in Sync</h2>
     77 <p>Whenever the application receives a <code>com.google.android.c2dm.intent.REGISTRATION</code> intent with a <code>registration_id</code> extra, it should save the ID for future use, pass it to the 3rd-party server to complete the registration, and keep track of whether the server completed the registration. If the server fails to complete the registration, it should try again or unregister from GCM.</p>
     78 <p>There are also two other scenarios that require special care:</p>
     79 <ul>
     80   <li>Application update</li>
     81   <li>Backup and restore
     82   </li>
     83 </ul>
     84 <p>When an application is updated, it should invalidate its existing registration ID, as it is not guaranteed to work with the new version.  Because there is no lifecycle method called when the application is updated, the best way to achieve this validation is by storing the current application version when a registration ID is stored. Then when the application is started, compare the stored value with the current application version. If they do not match, invalidate the stored data and start the registration process again.</p>
     85 
     86 <p>Similarly, you should not save the registration ID when an application is backed up. This is because the registration ID could become invalid by the time the application is restored, which would put the application in an invalid state  (that is, the application thinks it is registered, but the server and GCM do not store that registration ID anymore&mdash;thus the application will not get more messages).</p>
     87 <h3 id="canonical">Canonical IDs</h3>
     88 <p>On the server side, as long as the application is behaving well, everything should work normally. However, if a bug in the application triggers multiple registrations for the same device, it can be hard to reconcile state and you might end up with duplicate messages.</p>
     89 <p>GCM provides a facility called &quot;canonical registration IDs&quot; to easily recover from these situations. A canonical registration ID is defined to be the ID of the last registration requested by your application. This is the ID that the server should use when sending messages to the device.</p>
     90 <p>If later on you try to send a message using a different registration ID, GCM will process the request as usual, but it will include the canonical registration ID in the <code>registration_id</code> field of the response. Make sure to replace the registration ID stored in your server with this canonical ID, as eventually the ID you're using will stop working.</p>
     91 
     92 <h2 id="retry">Automatic Retry Using Exponential Back-Off</h2>
     93 
     94 <p>When the application receives a <code>com.google.android.c2dm.intent.REGISTRATION</code> intent with the <code>error</code> extra set as <code>SERVICE_NOT_AVAILABLE</code>, it should retry the failed operation (register or unregister).</p>
     95 <p>In the simplest case, if your application just calls <code>register</code> and GCM is not a fundamental part of the application, the application could simply ignore the error and try to register again the next time it starts. Otherwise, it should retry the previous operation using exponential back-off. In exponential back-off, each time there is a failure, it should wait twice the previous amount of time before trying again. If the register (or unregister) operation was synchronous, it could be retried in a simple loop. However, since it is asynchronous, the best approach is to schedule a pending intent to retry the operation. The following steps describe how to implement this in the <code>MyIntentService</code> example used above:</p>
     96 <ol>
     97   <li> Create a random token to verify the origin of the retry intent:
     98 
     99 <pre class="prettyprint pretty-java">private static final String TOKEN =
    100         Long.toBinaryString(new Random().nextLong());
    101 </pre>
    102 
    103   <li> Change the <code>handleRegistration()</code> method so it creates the pending intent when appropriate:</li>
    104 
    105 <pre class="prettyprint pretty-java">...
    106 if (error != null) {
    107  if ("SERVICE_NOT_AVAILABLE".equals(error)) {
    108    long backoffTimeMs = // get back-off time from shared preferences
    109    long nextAttempt = SystemClock.elapsedRealtime() + backoffTimeMs;
    110    Intent retryIntent = new Intent("com.example.gcm.intent.RETRY");
    111    retryIntent.putExtra("token", TOKEN);
    112    PendingIntent retryPendingIntent =
    113        PendingIntent.getBroadcast(context, 0, retryIntent, 0);
    114    AlarmManager am = (AlarmManager)   
    115        context.getSystemService(Context.ALARM_SERVICE);
    116    am.set(AlarmManager.ELAPSED_REALTIME, nextAttempt, retryPendingIntent);
    117    backoffTimeMs *= 2; // Next retry should wait longer.
    118    // update back-off time on shared preferences
    119  } else {
    120    // Unrecoverable error, log it
    121    Log.i(TAG, "Received error: " + error);
    122 }
    123 ...</pre>
    124 <p> The back-off time is stored in a shared preference. This ensures that it is persistent across multiple activity launches. The name of the intent does not matter, as long as the same intent is used in the following steps.</p></li>
    125 
    126   <li> Change the <code>onHandleIntent()</code> method adding an <code>else if</code> case for the retry intent:</li>
    127 
    128 <pre class="prettyprint pretty-java">...
    129 } else if (action.equals("com.example.gcm.intent.RETRY")) {
    130     String token = intent.getStringExtra("token");
    131     // make sure intent was generated by this class, not by a malicious app
    132     if (TOKEN.equals(token)) {
    133         String registrationId = // get from shared properties
    134         if (registrationId != null) {
    135         // last operation was attempt to unregister; send UNREGISTER intent again
    136     } else {
    137         // last operation was attempt to register; send REGISTER intent again
    138     }
    139 }
    140 ...</pre>
    141 
    142   <li> Create a new instance of <code>MyReceiver</code> in your activity:</li>
    143 
    144 <pre class="prettyprint pretty-java">private final MyBroadcastReceiver mRetryReceiver = new MyBroadcastReceiver();
    145 </pre>
    146 
    147   <li>In the activity's <code>onCreate()</code> method, register the new instance to receive the <code>com.example.gcm.intent.RETRY</code> intent:
    148     <pre class="prettyprint pretty-java">...
    149 IntentFilter filter = new IntentFilter(&quot;com.example.gcm.intent.RETRY&quot;);
    150 filter.addCategory(getPackageName());
    151 registerReceiver(mRetryReceiver, filter);
    152 ...</pre>
    153 
    154 <p class="note"><strong>Note:</strong> You must dynamically create a new instance of the broadcast receiver since the one defined by the manifest can only receive intents with the <code>com.google.android.c2dm.permission.SEND</code> permission. The permission <code>com.google.android.c2dm.permission.SEND</code> is a system permission and as such it cannot be granted to a regular application.</p>
    155 
    156 </li>
    157 
    158   <li>In the activity's <code>onDestroy()</code> method, unregister the broadcast receiver:</li>
    159 
    160 <pre class="prettyprint pretty-java">unregisterReceiver(mRetryReceiver);</pre>
    161 </ol>
    162 <h2 id="unreg">How Unregistration Works</h2>
    163 <p>There are two ways to unregister a device from GCM: manually and automatically.</p>
    164 <p>An Android application can manually unregister itself by issuing a <code>com.google.android.c2dm.intent.UNREGISTER</code> intent, which is useful when the application offers a logoff feature (so it can unregister on logoff and register again on logon). See the <a href="gcm.html#unregistering">Architectural Overview</a> for more discussion of this topic. This is the sequence of events when an application unregisters itself:</p>
    165 <ol>
    166   <li> The application issues a <code>com.google.android.c2dm.intent.UNREGISTER</code> intent, passing the package name as an extra.</li>
    167   <li>When the GCM server is done with the unregistration, it sends a <code>com.google.android.c2dm.intent.REGISTRATION</code> intent with the <code>unregistered</code> extra set.</li>
    168   <li>The application then must contact the 3rd-party server so it can remove the registration ID.</li>
    169   <li>The application should also clear its registration ID.
    170   </li>
    171 </ol>
    172 <p>An application can be automatically unregistered after it is uninstalled from the device. However, this process does not happens right away, as Android does not provide an uninstall callback. What happens in this scenario is as follows:</p>
    173 <ol>
    174   <li>The end user uninstalls the application.</li>
    175   <li>The 3rd-party server sends a message to GCM server.</li>
    176   <li>The GCM server sends the message to the device.</li>
    177   <li>The GCM client receives the message and queries Package Manager about whether there are broadcast receivers configured to receive it, which returns <code>false</code>. 
    178 </li>
    179   <li>The GCM client informs the GCM server that the application was uninstalled.</li>
    180   <li>The GCM server marks the registration ID for deletion.</li>
    181   <li>The 3rd-party server sends a message to  GCM.</li>
    182   <li>The GCM returns a <code>NotRegistered</code> error message to the 3rd-party server.</li>
    183   <li>The 3rd-party deletes the registration ID.
    184   </li>
    185 </ol>
    186 
    187 <p class ="note"><strong>Note:</strong> The GCM client is the Google Cloud Messaging framework present on the device.</p>
    188 
    189 <p>Note that it might take a while for the registration ID be completely removed from GCM. Thus it is possible that messages sent during step 7 above gets a valid message ID as response, even though the message will not be delivered to the device. Eventually, the registration ID will be removed and the server will get a <code>NotRegistered</code> error, without any further action being required from the 3rd-party server (this scenario happens frequently while an application is being developed and tested).</p>
    190 
    191 <h2 id="collapsible">Send-to-Sync  vs. Messages with Payload</h2>
    192 
    193 <p>Every message sent in GCM has the following characteristics:</p>
    194 <ul>
    195   <li>It has a payload limit of 4096 bytes.</li>
    196   <li>By default, it is stored by GCM for 4 weeks.</li>
    197 </ul>
    198 
    199 <p>But despite these similarities, messages can behave very differently depending on their particular settings. One major distinction between messages is whether they are collapsed (where each new message replaces the preceding message) or not collapsed (where each individual message is delivered). Every message sent in GCM is either a &quot;send-to-sync&quot; (collapsible) message or a &quot;message with payload&quot; (non-collapsible message). These concepts are described in more detail in the following sections.</p>
    200 
    201 <h3 id="s2s"><strong>Send-to-sync messages</strong></h3>
    202 
    203 <p>A send-to-sync (collapsible) message is often a &quot;tickle&quot; that tells a mobile application to sync data from the server. For example, suppose you have an email application. When a user receives new email on the server, the server pings the mobile application with a &quot;New mail&quot; message. This tells the application to sync to the server to pick up the new email. The server might send this message multiple times as new mail continues to accumulate, before the application has had a chance to sync. But if the user has received 25 new emails, there's no need to preserve every &quot;New mail&quot; message. One is sufficient. Another example would be a sports application that updates users with the latest score. Only the most recent message is relevant, so it makes sense to have each new message replace the preceding message. </p>
    204 
    205 <p>The email and sports applications are cases where you would probably use the GCM <code>collapse_key</code> parameter. A <em>collapse key</em> is an arbitrary string that is used to collapse a group of like messages when the device is offline, so that only the most recent message gets sent to the client. For example, &quot;New mail,&quot; &quot;Updates available,&quot; and so on</p>
    206 <p>GCM allows a maximum of 4 different collapse keys to be used by the GCM server at any given time. In other words, the GCM server can simultaneously store 4 different send-to-sync messages, each with a different collapse key. If you exceed this number GCM will only keep 4 collapse keys, with no guarantees about which ones they will be.</p>
    207 
    208 <h3 id="payload">Messages with payload</h3>
    209 <p>Unlike a send-to-sync message, every &quot;message with payload&quot; (non-collapsible message) is delivered. The payload the message contains can be up to 4kb. For example, here is a JSON-formatted message in an IM application in which spectators are discussing a sporting event:</p>
    210 
    211 <pre class="prettyprint pretty-json">{
    212   "registration_id" : "APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx...",
    213   "data" : {
    214     "Nick" : "Mario",
    215     "Text" : "great match!",
    216     "Room" : "PortugalVSDenmark",
    217   },
    218 }</pre>
    219 
    220 <p>A &quot;message with payload&quot; is not simply a &quot;ping&quot; to the mobile application to contact the server to fetch data. In the aforementioned IM application, for example, you would want to deliver every message, because every message has different content. To specify a non-collapsible message, you simply omit the <code>collapse_key</code> parameter. Thus GCM will send each message individually. Note that the order of delivery is not guaranteed.</p>
    221 <p>GCM will store up to 100 non-collapsible messages. After that, all messages are discarded from GCM, and a new message is created that tells the client how far behind it is. The message is delivered through a regular <code>com.google.android.c2dm.intent.RECEIVE</code> intent, with the following extras:</p>
    222 <ul>
    223   <li> <code>message_type</code>&mdash;The value is always the string &quot;deleted_messages&quot;.</li>
    224   <li><code>total_deleted</code>&mdash;The value  is a string with the number of deleted messages.</li>
    225 </ul>
    226 <p>The application should respond by syncing with the server to recover the discarded messages. </p>
    227 
    228 <h3 id="which">Which should I use?</h3>
    229   <p>If your application does not need to use non-collapsible messages, collapsible messages are a better choice from a performance standpoint, because they put less of a burden on the device battery.</p>
    230 
    231 <h2 dir="ltr" id="ttl">Setting an Expiration Date for a Message</h2>
    232 <p>The Time to Live (TTL) feature lets  the sender  specify the maximum lifespan of a message using the <code>time_to_live</code> parameter in the send request. The value of this parameter must be a duration from 0 to 2,419,200 seconds, and it corresponds to the maximum period of time for which GCM will store and try to deliver the message. Requests that don't contain this field default to the maximum period of 4 weeks.</p>
    233 <p>Here are some possible uses for this feature:</p>
    234 <ul>
    235   <li>Video chat incoming calls</li>
    236   <li>Expiring invitation events</li>
    237   <li>Calendar events</li>
    238 </ul>
    239 <h3 id="bg">Background </h3>
    240 <p>GCM will usually deliver messages immediately after they are sent. However, this might not always be possible. For example, the device could be turned off, offline, or otherwise unavailable. In other cases, the sender itself might request that messages not be delivered until the device becomes active by using the <code>delay_while_idle</code> flag. Finally, GCM might intentionally delay messages to prevent an application from consuming excessive resources and negatively impacting battery life.</p>
    241 <p>When this happens, GCM will store the message and deliver it as soon as it's feasible. While this is fine in most cases, there are some applications for which a late message might as well never be delivered. For example, if the message is an incoming call or video chat notification, it will only be meaningful for a small period of time before the call is terminated. Or if the message is an invitation to an event, it will be useless if received after the event has ended.</p>
    242 <p>Another advantage of specifying the expiration date for a message is that GCM will never throttle messages with a <code>time_to_live</code> value of 0 seconds. In other words, GCM will guarantee best effort for messages that must be delivered &quot;now or never.&quot; Keep in mind that a <code>time_to_live</code> value of 0 means messages that can't be delivered immediately will be discarded. However, because such messages are never stored, this provides the best latency for sending notifications.</p>
    243 <p>Here is an example of a JSON-formatted request that includes TTL:</p>
    244 <pre class="prettyprint pretty-json">
    245 {
    246   "collapse_key" : "demo",
    247   "delay_while_idle" : true,
    248   "registration_ids" : ["xyz"],
    249   "data" : {
    250     "key1" : "value1",
    251     "key2" : "value2",
    252   },
    253   "time_to_live" : 3
    254 },
    255 </pre>
    256 
    257 
    258 <h2 id="multi-senders">Receiving Messages from Multiple Senders</h2>
    259 <p>GCM allows multiple parties to send messages to the same application. For example, suppose your application is an articles aggregator with multiple contributors, and you want each of them to be able to send a message when they publish a new article. This message might contain a URL so that the application can download the article. Instead of having to centralize all sending activity in one location, GCM gives you the ability to let each of these contributors send its own messages.</p>
    260 <p>To make this possible, all you need to do is have each sender generate its own project number. Then include those IDs in the sender field, separated by commas, when requesting a registration. Finally, share the registration ID with your partners, and they'll be able to send messages to your application using their own authentication keys.</p>
    261 <p>This code snippet illustrates this feature. Senders are passed as an intent extra in a comma-separated list:</p>
    262 <pre class="prettyprint pretty-java">Intent intent = new Intent(GCMConstants.INTENT_TO_GCM_REGISTRATION);
    263 intent.setPackage(GSF_PACKAGE);
    264 intent.putExtra(GCMConstants.EXTRA_APPLICATION_PENDING_INTENT,
    265         PendingIntent.getBroadcast(context, 0, new Intent(), 0));
    266 String senderIds = &quot;968350041068,652183961211&quot;;
    267 intent.putExtra(GCMConstants.EXTRA_SENDER, senderIds);
    268 ontext.startService(intent);
    269  </pre>
    270 
    271 <p>Note that there is limit of 100 multiple senders.</p>
    272  
    273