Home | History | Annotate | Download | only in gcm
      1 page.title=GCM Cloud Connection Server (XMPP)
      2 @jd:body
      3 
      4 <div id="qv-wrapper">
      5 <div id="qv">
      6 
      7 
      8 <h2>In this document</h2>
      9 
     10 <ol class="toc">
     11   <li><a href="#connecting">Establishing a Connection</a>
     12     <ol class="toc">
     13       <li><a href="#auth">Authentication</a></li>
     14       </ol>
     15       </li>
     16     <li><a href="#format">Message Format</a>
     17       <ol class="toc">
     18         <li><a href="#request">Request format</a></li>
     19         <li><a href="#response">Response format</a></li>
     20       </ol>
     21       </li>
     22   <li><a href="#upstream">Upstream Messages</a>
     23     <ol>
     24       <li><a href="#receipts">Receive delivery receipts</a></li>
     25     </ol>
     26   </li>
     27   <li><a href="#flow">Flow Control</a> </li>
     28   <li><a href="#implement">Implementing an XMPP-based App Server</a>
     29     <ol class="toc">
     30       <li><a href="#smack">Java sample using the Smack library</a></li>
     31       <li><a href="#python">Python sample</a></li>
     32     </ol>
     33   </li>
     34 </ol>
     35 
     36 <h2>See Also</h2>
     37 
     38 <ol class="toc">
     39 <li><a href="server-ref.html">Server Reference</a></li>
     40 <li><a href="{@docRoot}google/gcm/http.html">HTTP</a></li>
     41 <li><a href="{@docRoot}google/gcm/gs.html">Getting Started</a></li>
     42 <li><a href="{@docRoot}google/gcm/server.html">Implementing GCM Server</a></li>
     43 <li><a href="{@docRoot}google/gcm/client.html">Implementing GCM Client</a></li>
     44 </ol>
     45 
     46 </div>
     47 </div>
     48 
     49 <p>The Google Cloud Messaging (GCM) Cloud Connection Server (CCS) is an XMPP endpoint that provides a
     50 persistent, asynchronous, bidirectional connection to Google servers. The
     51 connection can be used to send and receive messages between your server and
     52 your users' GCM-connected devices.</p>
     53 
     54 <p class="note"><strong>Note:</strong> The content in this document
     55 applies to <a href="http://developer.chrome.com/apps/cloudMessaging">
     56 GCM with Chrome apps</a> as well as Android.
     57 
     58 <p>You can continue to use the HTTP request mechanism to send messages to GCM
     59 servers, side-by-side with CCS which uses XMPP. Some of the benefits of CCS include:</p>
     60 
     61 <ul>
     62   <li>The asynchronous nature of XMPP allows you to send more messages with fewer
     63 resources.</li>
     64   <li>Communication is bidirectional&mdash;not only can your server send messages
     65 to the device, but the device can send messages back to your server.</li>
     66   <li>The device can send messages back using the same connection used for receiving,
     67 thereby improving battery life.</li>
     68 </ul>
     69 
     70 <p>The upstream messaging (device-to-cloud) feature of CCS is part of the Google
     71 Play services platform. Upstream messaging is available through the
     72 <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">
     73 {@code GoogleCloudMessaging}</a>
     74 APIs. For examples, see
     75 <a href="#implement">Implementing an XMPP-based App Server</a>.</p>
     76 
     77 <p class="note"><strong>Note:</strong> See the
     78 <a href="server-ref.html">Server Reference</a> for a list of all the message
     79 parameters and which connection server(s) supports them.</p>
     80 
     81 <h2 id="connecting">Establishing a Connection</h2>
     82 
     83 <p>CCS just uses XMPP as an authenticated transport layer, so you can use most
     84 XMPP libraries to manage the connection. For an example, see <a href="#smack">
     85 Java sample using the Smack library</a>.</p>
     86 
     87 <p>The CCS XMPP endpoint runs at {@code gcm.googleapis.com:5235}. When testing
     88 functionality (with non-production users), you should instead connect to
     89 {@code gcm-preprod.googleapis.com:5236} (note the different port). Regular
     90 testing on preprod (a smaller environment where the latest CCS builds run) is
     91 beneficial both for isolating real users from test code, as well as for early
     92 detection of unexpected behavior changes. Note that a connection receives upstream
     93 messages destined for its GCM sender ID, regardless of which environment (gcm or
     94 gcm-preprod) it is connected to. Therefore, test code connecting to
     95 {@code gcm-preprod.googleapis.com:5236} should use a different GCM sender ID to
     96 avoid upstream messages from production traffic being sent over test connections.</p>
     97 
     98 <p>The connection has two important requirements:</p>
     99 
    100 <ul>
    101   <li>You must initiate a Transport Layer Security (TLS) connection. Note that
    102   CCS doesn't currently support the <a href="http://xmpp.org/rfcs/rfc3920.html"
    103   class="external-link" target="_android">STARTTLS extension</a>.</li>
    104   <li>CCS requires a SASL PLAIN authentication mechanism using
    105   {@code &lt;your_GCM_Sender_Id&gt;&#64;gcm.googleapis.com} (GCM sender ID)
    106   and the API key as the password, where the sender ID and API key are the same
    107   as described in <a href="gs.html">Getting Started</a>.</li>
    108 </ul>
    109 
    110 <p>If at any point the connection fails, you should immediately reconnect.
    111 There is no need to back off after a disconnect that happens after
    112 authentication.</p>
    113 
    114 <h3 id="auth">Authentication</h3>
    115 
    116 <p>The following snippets illustrate how to perform authentication in CCS.</p>
    117 <h4>Client</h4>
    118 <pre>&lt;stream:stream to=&quot;gcm.googleapis.com&quot; 
    119         version=&quot;1.0&quot; xmlns=&quot;jabber:client&quot; 
    120         xmlns:stream=&quot;http://etherx.jabber.org/streams"/>;
    121 </pre>
    122 <h4>Server</h4>
    123 <pre>&lt;str:features xmlns:str=&quot;http://etherx.jabber.org/streams">;
    124  &lt;mechanisms xmlns=&quot;urn:ietf:params:xml:ns:xmpp-sasl&quot;&gt;
    125    &lt;mechanism&gt;X-OAUTH2&lt;/mechanism&gt;
    126    &lt;mechanism&gt;X-GOOGLE-TOKEN&lt;/mechanism&gt;
    127    &lt;mechanism&gt;PLAIN&lt;/mechanism&gt;
    128  &lt;/mechanisms&gt;
    129 &lt;/str:features&gt;
    130 </pre>
    131 
    132 <h4>Client</h4>
    133 <pre>&lt;auth mechanism=&quot;PLAIN&quot;
    134 xmlns=&quot;urn:ietf:params:xml:ns:xmpp-sasl&quot;&gt;MTI2MjAwMzQ3OTMzQHByb2plY3RzLmdjbS5hb
    135 mFTeUIzcmNaTmtmbnFLZEZiOW1oekNCaVlwT1JEQTJKV1d0dw==&lt;/auth&gt;
    136 </pre>
    137 
    138 <h4>Server</h4>
    139 <pre>&lt;success xmlns=&quot;urn:ietf:params:xml:ns:xmpp-sasl&quot;/&gt;</pre>
    140 
    141 <h2 id="format">Message Format</h2>
    142 <p>Once the XMPP connection is established, CCS and your server use normal XMPP
    143 <code>&lt;message&gt;</code> stanzas to send JSON-encoded messages back and
    144 forth. The body of the <code>&lt;message&gt;</code> must be:</p>
    145 <pre>
    146 &lt;gcm xmlns:google:mobile:data&gt;
    147     <em>JSON payload</em>
    148 &lt;/gcm&gt;
    149 </pre>
    150 
    151 <p>The JSON payload for regular GCM messages is similar to
    152 <a href="http.html#request">what the GCM http endpoint uses</a>, with these
    153 exceptions:</p>
    154 <ul>
    155   <li>There is no support for multiple recipients.</li>
    156   <li>{@code to} is used instead of {@code registration_ids}.</li>
    157   <li>CCS adds the field {@code message_id}, which is required. This ID uniquely
    158 identifies the message in an XMPP connection. The ACK or NACK from CCS uses the
    159 {@code message_id} to identify a message sent from 3rd-party app servers to CCS.
    160 Therefore, it's important that this {@code message_id} not only be unique (per
    161 sender ID), but always present.</li>
    162 </ul>
    163 
    164 <p>In addition to regular GCM messages, control messages are sent, indicated by
    165 the {@code message_type} field in the JSON object. The value can be either
    166 'ack' or 'nack', or 'control' (see formats below). Any GCM message with an
    167 unknown {@code message_type} can be ignored by your server.</p>
    168 
    169 <p>For each device message your app server receives from CCS, it needs to send
    170 an ACK message.
    171 It never needs to send a NACK message. If you don't send an ACK for a message,
    172 CCS will just resend it.
    173 </p>
    174 <p>CCS also sends an ACK or NACK for each server-to-device message. If you do not
    175 receive either, it means that the TCP connection was closed in the middle of the
    176 operation and your server needs to resend the messages. See
    177 <a href="#flow">Flow Control</a> for details.
    178 </p>
    179 
    180 <p class="note"><strong>Note:</strong> See the
    181 <a href="server-ref.html">Server Reference</a> for a list of all the message
    182 parameters and which connection server(s) supports them.</p>
    183 
    184 <h3 id="request">Request format</h3>
    185 
    186 <p>Here is an XMPP stanza containing the JSON message from a 3rd-party app server to CCS:
    187 
    188 </p>
    189 <pre>&lt;message id=&quot;&quot;&gt;
    190   &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
    191   {
    192       &quot;to&quot;:&quot;REGISTRATION_ID&quot;,  // &quot;to&quot; replaces &quot;registration_ids&quot;
    193       &quot;message_id&quot;:&quot;m-1366082849205&quot; // new required field
    194       &quot;data&quot;:
    195       {
    196           &quot;hello&quot;:&quot;world&quot;,
    197       }
    198       &quot;time_to_live&quot;:&quot;600&quot;,
    199       &quot;delay_while_idle&quot;: true/false,
    200       &quot;delivery_receipt_requested&quot;: true/false
    201   }
    202   &lt;/gcm&gt;
    203 &lt;/message&gt;
    204 </pre>
    205 
    206 <h3 id="response">Response format</h3>
    207 
    208 <p>A CCS response can have 3 possible forms. The first one is a regular 'ack'
    209 message. But when the response contains an error, there are 2
    210 different forms the message can take, described below.</p>
    211 
    212 <h4 id="ack">ACK message</h4>
    213 
    214 <p>Here is an XMPP stanza containing the ACK/NACK message from CCS to 3rd-party app server:
    215 </p>
    216 <pre>&lt;message id=&quot;&quot;&gt;
    217   &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
    218   {
    219       &quot;from&quot;:&quot;REGID&quot;,
    220       &quot;message_id&quot;:&quot;m-1366082849205&quot;
    221       &quot;message_type&quot;:&quot;ack&quot;
    222   }
    223   &lt;/gcm&gt;
    224 &lt;/message&gt;
    225 </pre>
    226 
    227 <h4 id="nack">NACK message</h4>
    228 
    229 <p>A NACK error is a regular XMPP message in which the {@code message_type} status
    230 message is &quot;nack&quot;. A NACK message contains:</p>
    231 <ul>
    232 <li>Nack error code.</li>
    233 <li>Nack error description.</li>
    234 </ul>
    235 
    236 <p>Below are some examples.</p>
    237 
    238 <p>Bad registration:</p>
    239 
    240 <pre>&lt;message&gt;
    241   &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
    242   {
    243     &quot;message_type&quot;:&quot;nack&quot;,
    244     &quot;message_id&quot;:&quot;msgId1&quot;,
    245     &quot;from&quot;:&quot;SomeInvalidRegistrationId&quot;,
    246     &quot;error&quot;:&quot;BAD_REGISTRATION&quot;,
    247     &quot;error_description&quot;:&quot;Invalid token on 'to' field: SomeInvalidRegistrationId&quot;
    248   }
    249   &lt;/gcm&gt;
    250 &lt;/message&gt;</pre>
    251 
    252 <p>Invalid JSON:</p>
    253 
    254 <pre>&lt;message&gt;
    255  &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
    256  {
    257    &quot;message_type&quot;:&quot;nack&quot;,
    258    &quot;message_id&quot;:&quot;msgId1&quot;,
    259    &quot;from&quot;:&quot;APA91bHFOtaQGSwupt5l1og&quot;,
    260    &quot;error&quot;:&quot;INVALID_JSON&quot;,
    261    &quot;error_description&quot;:&quot;InvalidJson: JSON_TYPE_ERROR : Field \&quot;time_to_live\&quot; must be a JSON java.lang.Number: abc&quot;
    262  }
    263  &lt;/gcm&gt;
    264 &lt;/message&gt;
    265 </pre>
    266 
    267 <p>Device Message Rate Exceeded:</p>
    268 
    269 <pre>&lt;message id=&quot;...&quot;&gt;
    270   &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
    271   {
    272     &quot;message_type&quot;:&quot;nack&quot;,
    273     &quot;message_id&quot;:&quot;msgId1&quot;,
    274     &quot;from&quot;:&quot;REGID&quot;,
    275     &quot;error&quot;:&quot;DEVICE_MESSAGE_RATE_EXCEEDED&quot;,
    276     &quot;error_description&quot;:&quot;Downstream message rate exceeded for this registration id&quot;
    277   }
    278   &lt;/gcm&gt;
    279 &lt;/message&gt;
    280 </pre>
    281 
    282 <p>See the <a href="server-ref.html#table11">Server Reference</a> for a complete list of the
    283 NACK error codes. Unless otherwise
    284 indicated, a NACKed message should not be retried. Unexpected NACK error codes
    285 should be treated the same as {@code INTERNAL_SERVER_ERROR}.</p>
    286 
    287 <h4 id="stanza">Stanza error</h4>
    288 
    289 <p>You can also get a stanza error in certain cases.
    290 A stanza error contains:</p>
    291 <ul>
    292 <li>Stanza error code.</li>
    293 <li>Stanza error description (free text).</li>
    294 </ul>
    295 <p>For example:</p>
    296 
    297 <pre>&lt;message id=&quot;3&quot; type=&quot;error&quot; to=&quot;123456789 (a] gcm.googleapis.com/ABC&quot;&gt;
    298   &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
    299      {&quot;random&quot;: &quot;text&quot;}
    300   &lt;/gcm&gt;
    301   &lt;error code=&quot;400&quot; type=&quot;modify&quot;&gt;
    302     &lt;bad-request xmlns=&quot;urn:ietf:params:xml:ns:xmpp-stanzas&quot;/&gt;
    303     &lt;text xmlns=&quot;urn:ietf:params:xml:ns:xmpp-stanzas&quot;&gt;
    304       InvalidJson: JSON_PARSING_ERROR : Missing Required Field: message_id\n
    305     &lt;/text&gt;
    306   &lt;/error&gt;
    307 &lt;/message&gt;
    308 </pre>
    309 
    310 <h4 id="control">Control messages</h4>
    311 
    312 <p>Periodically, CCS needs to close down a connection to perform load balancing. Before it
    313 closes the connection, CCS sends a {@code CONNECTION_DRAINING} message to indicate that the connection is being drained
    314 and will be closed soon. "Draining" refers to shutting off the flow of messages coming into a
    315 connection, but allowing whatever is already in the pipeline to continue. When you receive
    316 a {@code CONNECTION_DRAINING} message, you should immediately begin sending messages to another CCS
    317 connection, opening a new connection if necessary. You should, however, keep the original
    318 connection open and continue receiving messages that may come over the connection (and
    319 ACKing them)&mdash;CCS will handle initiating a connection close when it is ready.</p>
    320 
    321 <p>The {@code CONNECTION_DRAINING} message looks like this:</p>
    322 <pre>&lt;message&gt;
    323   &lt;data:gcm xmlns:data=&quot;google:mobile:data&quot;&gt;
    324   {
    325     &quot;message_type&quot;:&quot;control&quot;
    326     &quot;control_type&quot;:&quot;CONNECTION_DRAINING&quot;
    327   }
    328   &lt;/data:gcm&gt;
    329 &lt;/message&gt;</pre>
    330 
    331 <p>{@code CONNECTION_DRAINING} is currently the only {@code control_type} supported.</p>
    332 
    333 <h2 id="upstream">Upstream Messages</h2>
    334 
    335 <p>Using CCS and the
    336 <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">
    337 {@code GoogleCloudMessaging}</a>
    338 API, you can send messages from a user's device to the cloud.</p>
    339 
    340 <p>Here is how you send an upstream message using the
    341 <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">
    342 {@code GoogleCloudMessaging}</a>
    343 API. For a complete example, see <a href="client.html">Implementing GCM Client</a>:</p>
    344 
    345 <pre>GoogleCloudMessaging gcm = GoogleCloudMessaging.get(context);
    346 String GCM_SENDER_ID = "Your-Sender-ID";
    347 AtomicInteger msgId = new AtomicInteger();
    348 String id = Integer.toString(msgId.incrementAndGet());
    349 Bundle data = new Bundle();
    350 // Bundle data consists of a key-value pair
    351 data.putString("hello", "world");
    352 // "time to live" parameter
    353 // This is optional. It specifies a value in seconds up to 24 hours.
    354 int ttl = [0 seconds, 24 hours]
    355 
    356 gcm.send(GCM_SENDER_ID + "&#64;gcm.googleapis.com", id, ttl, data);
    357 </pre>
    358 
    359 <p>This call generates the necessary XMPP stanza for sending the upstream message.
    360 The message goes from the app on the device to CCS to the 3rd-party app server.
    361 The stanza has the following format:</p>
    362 
    363 <pre>&lt;message id=&quot;&quot;&gt;
    364   &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
    365   {
    366       &quot;category&quot;:&quot;com.example.yourapp&quot;, // to know which app sent it
    367       &quot;data&quot;:
    368       {
    369           &quot;hello&quot;:&quot;world&quot;,
    370       },
    371       &quot;message_id&quot;:&quot;m-123&quot;,
    372       &quot;from&quot;:&quot;REGID&quot;
    373   }
    374   &lt;/gcm&gt;
    375 &lt;/message&gt;</pre>
    376 
    377 <p>Here is the format of the ACK expected by CCS from 3rd-party app servers in
    378 response to the above message:</p>
    379 
    380 <pre>&lt;message id=&quot;&quot;&gt;
    381   &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
    382   {
    383       &quot;to&quot;:&quot;REGID&quot;,
    384       &quot;message_id&quot;:&quot;m-123&quot;
    385       &quot;message_type&quot;:&quot;ack&quot;
    386   }
    387   &lt;/gcm&gt;
    388 &lt;/message&gt;</pre>
    389 
    390 <h3 id="receipts">Receive delivery receipts</h3>
    391 
    392 <p>You can use upstream messaging to get delivery receipts (sent from CCS to
    393 your 3rd party app server) when
    394 a device confirms that it received a message sent by CCS.</p>
    395 
    396 <p>To enable this feature, the message your 3rd-party app server sends to CCS must include
    397 a field called <code>&quot;delivery_receipt_requested&quot;</code>. When this field is set to
    398 <code>true</code>, CCS sends a delivery receipt when a device confirms that it received a particular message.</p>
    399 
    400 <p>Here is an XMPP stanza containing a JSON
    401 message with <code>&quot;delivery_receipt_requested&quot;</code> set to <code>true</code>:</p>
    402 
    403 <pre>&lt;message id=&quot;&quot;&gt;
    404   &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
    405   {
    406       &quot;to&quot;:&quot;REGISTRATION_ID&quot;,
    407       &quot;message_id&quot;:&quot;m-1366082849205&quot;
    408       &quot;data&quot;:
    409       {
    410           &quot;hello&quot;:&quot;world&quot;,
    411       }
    412       &quot;time_to_live&quot;:&quot;600&quot;,
    413       &quot;delay_while_idle&quot;: true,
    414       <strong>&quot;delivery_receipt_requested&quot;: true</strong>
    415   }
    416   &lt;/gcm&gt;
    417 &lt;/message&gt;
    418 </pre>
    419 
    420 
    421 
    422 <p>Here is an example of the delivery receipt that CCS sends to tell your 3rd-party
    423 app server that a device received a message that CCS sent it:</p>
    424 
    425 </p>
    426 <pre>&lt;message id=&quot;&quot;&gt;
    427   &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
    428   {
    429       &quot;category&quot;:&quot;com.example.yourapp&quot;, // to know which app sent it
    430       &quot;data&quot;:
    431       {
    432          &#x201c;message_status&quot;:&quot;MESSAGE_SENT_TO_DEVICE&quot;,
    433          &#x201c;original_message_id&#x201d;:&#x201d;m-1366082849205&#x201d;
    434          &#x201c;device_registration_id&#x201d;: &#x201c;REGISTRATION_ID&#x201d;
    435       },
    436       &quot;message_id&quot;:&quot;dr2:m-1366082849205&quot;,
    437       &quot;message_type&quot;:&quot;receipt&quot;,
    438       &quot;from&quot;:&quot;gcm.googleapis.com&quot;
    439   }
    440   &lt;/gcm&gt;
    441 &lt;/message&gt;</pre>
    442 
    443 <p>Note the following:</p>
    444 
    445 <ul>
    446   <li>The {@code &quot;message_type&quot;} is set to {@code &quot;receipt&quot;}.
    447   <li>The {@code &quot;message_status&quot;} is set to {@code &quot;MESSAGE_SENT_TO_DEVICE&quot;},
    448   indicating that the device received the message. Notice that in this case,
    449 {@code &quot;message_status&quot;} is not a field but rather part of the data payload.</li>
    450   <li>The receipt message ID consists of the original message ID, but with a
    451 <code>dr2:</code> prefix. Your 3rd-party app server must send an ACK back with this ID,
    452 which in this example is {@code dr2:m-1366082849205}.</li>
    453   <li>The original message ID, the device registration ID, and the status are inside the
    454 {@code &quot;data&quot;} field.</li>
    455 </ul>
    456 
    457 <h2 id="flow">Flow Control</h2>
    458 
    459 <p>Every message sent to CCS receives either an ACK or a NACK response. Messages
    460 that haven't received one of these responses are considered pending. If the pending
    461 message count reaches 100, the 3rd-party app server should stop sending new messages
    462 and wait for CCS to acknowledge some of the existing pending messages as illustrated in
    463 figure 1:</p>
    464 
    465 <img src="{@docRoot}images/gcm/CCS-ack.png">
    466 
    467 <p class="img-caption">
    468   <strong>Figure 1.</strong> Message/ack flow.
    469 </p>
    470 
    471 <p>Conversely, to avoid overloading the 3rd-party app server, CCS will stop sending
    472 if there are too many unacknowledged messages. Therefore, the 3rd-party app server
    473 should "ACK" upstream messages, received from the client application via CCS, as soon as possible
    474 to maintain a constant flow of incoming messages. The aforementioned pending message limit doesn't
    475 apply to these ACKs. Even if the pending message count reaches 100, the 3rd-party app server
    476 should continue sending ACKs for messages received from CCS to avoid blocking delivery of new
    477 upstream messages.</p>
    478 
    479 <p>ACKs are only valid within the context of one connection. If the connection is
    480 closed before a message can be ACKed, the 3rd-party app server should wait for CCS
    481 to resend the upstream message before ACKing it again. Similarly, all pending messages for which an
    482 ACK/NACK was not received from CCS before the connection was closed should be sent again.
    483 </p>
    484 
    485 <h2 id="implement">Implementing an XMPP-based App Server</h2>
    486 
    487 <p>This section gives examples of implementing an app server that works with CCS.
    488 Note that a full GCM implementation requires a client-side implementation, in
    489 addition to the server. For more information, see <a href="client.html">
    490 Implementing GCM Client</a>.</a>
    491 
    492 <h3 id="smack">Java sample using the Smack library</h3>
    493 
    494 <p>Here is a sample app server written in Java, using the
    495 <a href="http://www.igniterealtime.org/projects/smack/">Smack</a> library.</p>
    496 
    497 <pre>import org.jivesoftware.smack.ConnectionConfiguration;
    498 import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
    499 import org.jivesoftware.smack.ConnectionListener;
    500 import org.jivesoftware.smack.PacketInterceptor;
    501 import org.jivesoftware.smack.PacketListener;
    502 import org.jivesoftware.smack.SmackException;
    503 import org.jivesoftware.smack.SmackException.NotConnectedException;
    504 import org.jivesoftware.smack.XMPPConnection;
    505 import org.jivesoftware.smack.XMPPException;
    506 import org.jivesoftware.smack.filter.PacketTypeFilter;
    507 import org.jivesoftware.smack.packet.DefaultPacketExtension;
    508 import org.jivesoftware.smack.packet.Message;
    509 import org.jivesoftware.smack.packet.Packet;
    510 import org.jivesoftware.smack.packet.PacketExtension;
    511 import org.jivesoftware.smack.provider.PacketExtensionProvider;
    512 import org.jivesoftware.smack.provider.ProviderManager;
    513 import org.jivesoftware.smack.tcp.XMPPTCPConnection;
    514 import org.jivesoftware.smack.util.StringUtils;
    515 import org.json.simple.JSONValue;
    516 import org.json.simple.parser.ParseException;
    517 import org.xmlpull.v1.XmlPullParser;
    518 
    519 import java.io.IOException;
    520 import java.util.HashMap;
    521 import java.util.Map;
    522 import java.util.UUID;
    523 import java.util.logging.Level;
    524 import java.util.logging.Logger;
    525 
    526 import javax.net.ssl.SSLSocketFactory;
    527 
    528 /**
    529  * Sample Smack implementation of a client for GCM Cloud Connection Server. This
    530  * code can be run as a standalone CCS client.
    531  *
    532  * &lt;p&gt;For illustration purposes only.
    533  */
    534 public class SmackCcsClient {
    535 
    536     private static final Logger logger = Logger.getLogger(&quot;SmackCcsClient&quot;);
    537 
    538     private static final String GCM_SERVER = &quot;gcm.googleapis.com&quot;;
    539     private static final int GCM_PORT = 5235;
    540 
    541     private static final String GCM_ELEMENT_NAME = &quot;gcm&quot;;
    542     private static final String GCM_NAMESPACE = &quot;google:mobile:data&quot;;
    543 
    544     static {
    545 
    546         ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE,
    547             new PacketExtensionProvider() {
    548                 &#64;Override
    549                 public PacketExtension parseExtension(XmlPullParser parser) throws
    550                         Exception {
    551                     String json = parser.nextText();
    552                     return new GcmPacketExtension(json);
    553                 }
    554             });
    555     }
    556 
    557     private XMPPConnection connection;
    558 
    559     /**
    560      * Indicates whether the connection is in draining state, which means that it
    561      * will not accept any new downstream messages.
    562      */
    563     protected volatile boolean connectionDraining = false;
    564 
    565     /**
    566      * Sends a downstream message to GCM.
    567      *
    568      * &#64;return true if the message has been successfully sent.
    569      */
    570     public boolean sendDownstreamMessage(String jsonRequest) throws
    571             NotConnectedException {
    572         if (!connectionDraining) {
    573             send(jsonRequest);
    574             return true;
    575         }
    576         logger.info(&quot;Dropping downstream message since the connection is draining&quot;);
    577         return false;
    578     }
    579 
    580     /**
    581      * Returns a random message id to uniquely identify a message.
    582      *
    583      * &lt;p&gt;Note: This is generated by a pseudo random number generator for
    584      * illustration purpose, and is not guaranteed to be unique.
    585      */
    586     public String nextMessageId() {
    587         return &quot;m-&quot; + UUID.randomUUID().toString();
    588     }
    589 
    590     /**
    591      * Sends a packet with contents provided.
    592      */
    593     protected void send(String jsonRequest) throws NotConnectedException {
    594         Packet request = new GcmPacketExtension(jsonRequest).toPacket();
    595         connection.sendPacket(request);
    596     }
    597 
    598     /**
    599      * Handles an upstream data message from a device application.
    600      *
    601      * &lt;p&gt;This sample echo server sends an echo message back to the device.
    602      * Subclasses should override this method to properly process upstream messages.
    603      */
    604     protected void handleUpstreamMessage(Map&lt;String, Object&gt; jsonObject) {
    605         // PackageName of the application that sent this message.
    606         String category = (String) jsonObject.get(&quot;category&quot;);
    607         String from = (String) jsonObject.get(&quot;from&quot;);
    608         &#64;SuppressWarnings(&quot;unchecked&quot;)
    609         Map&lt;String, String&gt; payload = (Map&lt;String, String&gt;) jsonObject.get(&quot;data&quot;);
    610         payload.put(&quot;ECHO&quot;, &quot;Application: &quot; + category);
    611 
    612         // Send an ECHO response back
    613         String echo = createJsonMessage(from, nextMessageId(), payload,
    614                 &quot;echo:CollapseKey&quot;, null, false);
    615 
    616         try {
    617             sendDownstreamMessage(echo);
    618         } catch (NotConnectedException e) {
    619             logger.log(Level.WARNING, &quot;Not connected anymore, echo message is
    620                     not sent&quot;, e);
    621         }
    622     }
    623 
    624     /**
    625      * Handles an ACK.
    626      *
    627      * &lt;p&gt;Logs a {@code INFO} message, but subclasses could override it to
    628      * properly handle ACKs.
    629      */
    630     protected void handleAckReceipt(Map&lt;String, Object&gt; jsonObject) {
    631         String messageId = (String) jsonObject.get(&quot;message_id&quot;);
    632         String from = (String) jsonObject.get(&quot;from&quot;);
    633         logger.log(Level.INFO, &quot;handleAckReceipt() from: &quot; + from + &quot;,
    634                 messageId: &quot; + messageId);
    635     }
    636 
    637     /**
    638      * Handles a NACK.
    639      *
    640      * &lt;p&gt;Logs a {@code INFO} message, but subclasses could override it to
    641      * properly handle NACKs.
    642      */
    643     protected void handleNackReceipt(Map&lt;String, Object&gt; jsonObject) {
    644         String messageId = (String) jsonObject.get(&quot;message_id&quot;);
    645         String from = (String) jsonObject.get(&quot;from&quot;);
    646         logger.log(Level.INFO, &quot;handleNackReceipt() from: &quot; + from + &quot;,
    647                 messageId: &quot; + messageId);
    648     }
    649 
    650     protected void handleControlMessage(Map&lt;String, Object&gt; jsonObject) {
    651         logger.log(Level.INFO, &quot;handleControlMessage(): &quot; + jsonObject);
    652         String controlType = (String) jsonObject.get(&quot;control_type&quot;);
    653         if (&quot;CONNECTION_DRAINING&quot;.equals(controlType)) {
    654             connectionDraining = true;
    655         } else {
    656             logger.log(Level.INFO, &quot;Unrecognized control type: %s. This could
    657                     happen if new features are &quot; + &quot;added to the CCS protocol.&quot;,
    658                     controlType);
    659         }
    660     }
    661 
    662     /**
    663      * Creates a JSON encoded GCM message.
    664      *
    665      * &#64;param to RegistrationId of the target device (Required).
    666      * &#64;param messageId Unique messageId for which CCS will send an
    667      *         &quot;ack/nack&quot; (Required).
    668      * &#64;param payload Message content intended for the application. (Optional).
    669      * &#64;param collapseKey GCM collapse_key parameter (Optional).
    670      * &#64;param timeToLive GCM time_to_live parameter (Optional).
    671      * &#64;param delayWhileIdle GCM delay_while_idle parameter (Optional).
    672      * &#64;return JSON encoded GCM message.
    673      */
    674     public static String createJsonMessage(String to, String messageId,
    675             Map&lt;String, String&gt; payload, String collapseKey, Long timeToLive,
    676             Boolean delayWhileIdle) {
    677         Map&lt;String, Object&gt; message = new HashMap&lt;String, Object&gt;();
    678         message.put(&quot;to&quot;, to);
    679         if (collapseKey != null) {
    680             message.put(&quot;collapse_key&quot;, collapseKey);
    681         }
    682         if (timeToLive != null) {
    683             message.put(&quot;time_to_live&quot;, timeToLive);
    684         }
    685         if (delayWhileIdle != null &amp;&amp; delayWhileIdle) {
    686             message.put(&quot;delay_while_idle&quot;, true);
    687         }
    688       message.put(&quot;message_id&quot;, messageId);
    689       message.put(&quot;data&quot;, payload);
    690       return JSONValue.toJSONString(message);
    691     }
    692 
    693     /**
    694      * Creates a JSON encoded ACK message for an upstream message received
    695      * from an application.
    696      *
    697      * &#64;param to RegistrationId of the device who sent the upstream message.
    698      * &#64;param messageId messageId of the upstream message to be acknowledged to CCS.
    699      * &#64;return JSON encoded ack.
    700      */
    701         protected static String createJsonAck(String to, String messageId) {
    702         Map&lt;String, Object&gt; message = new HashMap&lt;String, Object&gt;();
    703         message.put(&quot;message_type&quot;, &quot;ack&quot;);
    704         message.put(&quot;to&quot;, to);
    705         message.put(&quot;message_id&quot;, messageId);
    706         return JSONValue.toJSONString(message);
    707     }
    708 
    709     /**
    710      * Connects to GCM Cloud Connection Server using the supplied credentials.
    711      *
    712      * &#64;param senderId Your GCM project number
    713      * &#64;param apiKey API Key of your project
    714      */
    715     public void connect(long senderId, String apiKey)
    716             throws XMPPException, IOException, SmackException {
    717         ConnectionConfiguration config =
    718                 new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
    719         config.setSecurityMode(SecurityMode.enabled);
    720         config.setReconnectionAllowed(true);
    721         config.setRosterLoadedAtLogin(false);
    722         config.setSendPresence(false);
    723         config.setSocketFactory(SSLSocketFactory.getDefault());
    724 
    725         connection = new XMPPTCPConnection(config);
    726         connection.connect();
    727 
    728         connection.addConnectionListener(new LoggingConnectionListener());
    729 
    730         // Handle incoming packets
    731         connection.addPacketListener(new PacketListener() {
    732 
    733             &#64;Override
    734             public void processPacket(Packet packet) {
    735                 logger.log(Level.INFO, &quot;Received: &quot; + packet.toXML());
    736                 Message incomingMessage = (Message) packet;
    737                 GcmPacketExtension gcmPacket =
    738                         (GcmPacketExtension) incomingMessage.
    739                         getExtension(GCM_NAMESPACE);
    740                 String json = gcmPacket.getJson();
    741                 try {
    742                     &#64;SuppressWarnings(&quot;unchecked&quot;)
    743                     Map&lt;String, Object&gt; jsonObject =
    744                             (Map&lt;String, Object&gt;) JSONValue.
    745                             parseWithException(json);
    746 
    747                     // present for &quot;ack&quot;/&quot;nack&quot;, null otherwise
    748                     Object messageType = jsonObject.get(&quot;message_type&quot;);
    749 
    750                     if (messageType == null) {
    751                         // Normal upstream data message
    752                         handleUpstreamMessage(jsonObject);
    753 
    754                         // Send ACK to CCS
    755                         String messageId = (String) jsonObject.get(&quot;message_id&quot;);
    756                         String from = (String) jsonObject.get(&quot;from&quot;);
    757                         String ack = createJsonAck(from, messageId);
    758                         send(ack);
    759                     } else if (&quot;ack&quot;.equals(messageType.toString())) {
    760                           // Process Ack
    761                           handleAckReceipt(jsonObject);
    762                     } else if (&quot;nack&quot;.equals(messageType.toString())) {
    763                           // Process Nack
    764                           handleNackReceipt(jsonObject);
    765                     } else if (&quot;control&quot;.equals(messageType.toString())) {
    766                           // Process control message
    767                           handleControlMessage(jsonObject);
    768                     } else {
    769                           logger.log(Level.WARNING,
    770                                   &quot;Unrecognized message type (%s)&quot;,
    771                                   messageType.toString());
    772                     }
    773                 } catch (ParseException e) {
    774                     logger.log(Level.SEVERE, &quot;Error parsing JSON &quot; + json, e);
    775                 } catch (Exception e) {
    776                     logger.log(Level.SEVERE, &quot;Failed to process packet&quot;, e);
    777                 }
    778             }
    779         }, new PacketTypeFilter(Message.class));
    780 
    781         // Log all outgoing packets
    782         connection.addPacketInterceptor(new PacketInterceptor() {
    783             &#64;Override
    784                 public void interceptPacket(Packet packet) {
    785                     logger.log(Level.INFO, &quot;Sent: {0}&quot;, packet.toXML());
    786                 }
    787             }, new PacketTypeFilter(Message.class));
    788 
    789         connection.login(senderId + &quot;&#64;gcm.googleapis.com&quot;, apiKey);
    790     }
    791 
    792     public static void main(String[] args) throws Exception {
    793         final long senderId = 1234567890L; // your GCM sender id
    794         final String password = &quot;Your API key&quot;;
    795 
    796         SmackCcsClient ccsClient = new SmackCcsClient();
    797 
    798         ccsClient.connect(senderId, password);
    799 
    800         // Send a sample hello downstream message to a device.
    801         String toRegId = &quot;RegistrationIdOfTheTargetDevice&quot;;
    802         String messageId = ccsClient.nextMessageId();
    803         Map&lt;String, String&gt; payload = new HashMap&lt;String, String&gt;();
    804         payload.put(&quot;Hello&quot;, &quot;World&quot;);
    805         payload.put(&quot;CCS&quot;, &quot;Dummy Message&quot;);
    806         payload.put(&quot;EmbeddedMessageId&quot;, messageId);
    807         String collapseKey = &quot;sample&quot;;
    808         Long timeToLive = 10000L;
    809         String message = createJsonMessage(toRegId, messageId, payload,
    810                 collapseKey, timeToLive, true);
    811 
    812         ccsClient.sendDownstreamMessage(message);
    813     }
    814 
    815     /**
    816      * XMPP Packet Extension for GCM Cloud Connection Server.
    817      */
    818     private static final class GcmPacketExtension extends DefaultPacketExtension {
    819 
    820         private final String json;
    821 
    822         public GcmPacketExtension(String json) {
    823             super(GCM_ELEMENT_NAME, GCM_NAMESPACE);
    824             this.json = json;
    825         }
    826 
    827         public String getJson() {
    828             return json;
    829         }
    830 
    831         &#64;Override
    832         public String toXML() {
    833             return String.format(&quot;&lt;%s xmlns=\&quot;%s\&quot;&gt;%s&lt;/%s&gt;&quot;,
    834                     GCM_ELEMENT_NAME, GCM_NAMESPACE,
    835                     StringUtils.escapeForXML(json), GCM_ELEMENT_NAME);
    836         }
    837 
    838         public Packet toPacket() {
    839             Message message = new Message();
    840             message.addExtension(this);
    841             return message;
    842         }
    843     }
    844 
    845     private static final class LoggingConnectionListener
    846             implements ConnectionListener {
    847 
    848         &#64;Override
    849         public void connected(XMPPConnection xmppConnection) {
    850             logger.info(&quot;Connected.&quot;);
    851         }
    852 
    853         &#64;Override
    854         public void authenticated(XMPPConnection xmppConnection) {
    855             logger.info(&quot;Authenticated.&quot;);
    856         }
    857 
    858         &#64;Override
    859         public void reconnectionSuccessful() {
    860             logger.info(&quot;Reconnecting..&quot;);
    861         }
    862 
    863         &#64;Override
    864         public void reconnectionFailed(Exception e) {
    865             logger.log(Level.INFO, &quot;Reconnection failed.. &quot;, e);
    866         }
    867 
    868         &#64;Override
    869         public void reconnectingIn(int seconds) {
    870             logger.log(Level.INFO, &quot;Reconnecting in %d secs&quot;, seconds);
    871         }
    872 
    873         &#64;Override
    874         public void connectionClosedOnError(Exception e) {
    875             logger.info(&quot;Connection closed on error.&quot;);
    876         }
    877 
    878         &#64;Override
    879         public void connectionClosed() {
    880             logger.info(&quot;Connection closed.&quot;);
    881         }
    882     }
    883 }</pre>
    884 
    885 <h3 id="python">Python sample</h3>
    886 
    887 <p>Here is an example of a CCS app server written in Python. This sample echo
    888 server sends an initial message, and for every upstream message received, it sends
    889 a dummy response back to the application that sent the upstream message. This
    890 example illustrates how to connect, send, and receive GCM messages using XMPP. It
    891 shouldn't be used as-is on a production deployment.</p>
    892 
    893 <pre>
    894 #!/usr/bin/python
    895 import sys, json, xmpp, random, string
    896 
    897 SERVER = 'gcm.googleapis.com'
    898 PORT = 5235
    899 USERNAME = "Your GCM Sender Id"
    900 PASSWORD = "API Key"
    901 REGISTRATION_ID = "Registration Id of the target device"
    902 
    903 unacked_messages_quota = 100
    904 send_queue = []
    905 
    906 # Return a random alphanumerical id
    907 def random_id():
    908   rid = ''
    909   for x in range(8): rid += random.choice(string.ascii_letters + string.digits)
    910   return rid
    911 
    912 def message_callback(session, message):
    913   global unacked_messages_quota
    914   gcm = message.getTags('gcm')
    915   if gcm:
    916     gcm_json = gcm[0].getData()
    917     msg = json.loads(gcm_json)
    918     if not msg.has_key('message_type'):
    919       # Acknowledge the incoming message immediately.
    920       send({'to': msg['from'],
    921             'message_type': 'ack',
    922             'message_id': msg['message_id']})
    923       # Queue a response back to the server.
    924       if msg.has_key('from'):
    925         # Send a dummy echo response back to the app that sent the upstream message.
    926         send_queue.append({'to': msg['from'],
    927                            'message_id': random_id(),
    928                            'data': {'pong': 1}})
    929     elif msg['message_type'] == 'ack' or msg['message_type'] == 'nack':
    930       unacked_messages_quota += 1
    931 
    932 def send(json_dict):
    933   template = (&quot;&lt;message&gt;&lt;gcm xmlns='google:mobile:data'&gt;{1}&lt;/gcm&gt;&lt;/message&gt;&quot;)
    934   client.send(xmpp.protocol.Message(
    935       node=template.format(client.Bind.bound[0], json.dumps(json_dict))))
    936 
    937 def flush_queued_messages():
    938   global unacked_messages_quota
    939   while len(send_queue) and unacked_messages_quota &gt; 0:
    940     send(send_queue.pop(0))
    941     unacked_messages_quota -= 1
    942 
    943 client = xmpp.Client('gcm.googleapis.com', debug=['socket'])
    944 client.connect(server=(SERVER,PORT), secure=1, use_srv=False)
    945 auth = client.auth(USERNAME, PASSWORD)
    946 if not auth:
    947   print 'Authentication failed!'
    948   sys.exit(1)
    949 
    950 client.RegisterHandler('message', message_callback)
    951 
    952 send_queue.append({'to': REGISTRATION_ID,
    953                    'message_id': 'reg_id',
    954                    'data': {'message_destination': 'RegId',
    955                             'message_id': random_id()}})
    956 
    957 while True:
    958   client.Process(1)
    959   flush_queued_messages()</pre>
    960