2 * Copyright 2011 Google Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.google.ipc.invalidation.ticl.android2.channel;
18 import com.google.android.gcm.GCMRegistrar;
19 import com.google.ipc.invalidation.external.client.SystemResources.Logger;
20 import com.google.ipc.invalidation.external.client.android.service.AndroidLogger;
21 import com.google.ipc.invalidation.external.client.contrib.MultiplexingGcmListener;
22 import com.google.ipc.invalidation.ticl.android2.AndroidTiclManifest;
23 import com.google.ipc.invalidation.ticl.android2.ProtocolIntents;
24 import com.google.ipc.invalidation.ticl.android2.channel.AndroidChannelConstants.C2dmConstants;
25 import com.google.ipc.invalidation.ticl.proto.AndroidChannel.AddressedAndroidMessage;
26 import com.google.ipc.invalidation.util.ProtoWrapper.ValidationException;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.util.Base64;
34 * Service that receives messages from using GCM.
37 public class AndroidMessageReceiverService extends MultiplexingGcmListener.AbstractListener {
39 * This class is public so that it can be instantiated by the Android runtime. All of the
40 * {@code onYYY} methods are called holding a wakelock that will be automatically released when
41 * they return, since this is a subclass of {@code AbstractListener}.
45 * Receiver for broadcasts by the multiplexed GCM service. It forwards them to
46 * AndroidMessageReceiverService.
48 public static class Receiver extends MultiplexingGcmListener.AbstractListener.Receiver {
49 /* This class is public so that it can be instantiated by the Android runtime. */
51 protected Class<?> getServiceClass() {
52 return AndroidMessageReceiverService.class;
56 private final Logger logger = AndroidLogger.forTag("MsgRcvrSvc");
58 public AndroidMessageReceiverService() {
59 super("AndroidMessageReceiverService");
63 protected void onMessage(Intent intent) {
64 // Forward the message to the Ticl service.
65 if (intent.hasExtra(C2dmConstants.CONTENT_PARAM)) {
66 String content = intent.getStringExtra(C2dmConstants.CONTENT_PARAM);
67 byte[] msgBytes = Base64.decode(content, Base64.URL_SAFE);
69 // Look up the name of the Ticl service class from the manifest.
70 String serviceClass = new AndroidTiclManifest(this).getTiclServiceClass();
71 AddressedAndroidMessage addrMessage = AddressedAndroidMessage.parseFrom(msgBytes);
73 ProtocolIntents.InternalDowncalls.newServerMessageIntent(addrMessage.getMessage());
74 msgIntent.setClassName(this, serviceClass);
75 startService(msgIntent);
76 } catch (ValidationException exception) {
77 logger.warning("Failed parsing inbound message: %s", exception);
80 logger.fine("GCM Intent has no message content: %s", intent);
83 // Store the echo token.
84 String echoToken = intent.getStringExtra(C2dmConstants.ECHO_PARAM);
85 if (echoToken != null) {
86 AndroidChannelPreferences.setEchoToken(this, echoToken);
91 protected void onRegistered(String registrationId) {
92 // Inform the sender service that the registration id has changed. If the sender service
93 // had buffered a message because no registration id was previously available, this intent
94 // will cause it to send that message.
95 Intent sendBuffered = new Intent();
96 final String ignoredData = "";
97 sendBuffered.putExtra(AndroidChannelConstants.MESSAGE_SENDER_SVC_GCM_REGID_CHANGE, ignoredData);
98 sendBuffered.setClass(this, AndroidMessageSenderService.class);
99 startService(sendBuffered);
101 // Inform the Ticl service that the registration id has changed. This will cause it to send
102 // a message to the data center and update the GCM registration id stored at the data center.
103 Intent updateServer = ProtocolIntents.InternalDowncalls.newNetworkAddrChangeIntent();
104 updateServer.setClassName(this, new AndroidTiclManifest(this).getTiclServiceClass());
105 startService(updateServer);
109 protected void onUnregistered(String registrationId) {
114 protected void onDeletedMessages(int total) {
115 // This method must be implemented if we start using non-collapsable messages with GCM. For
116 // now, there is nothing to do.
120 * Initializes GCM as a convenience method for tests. In production, applications should handle
123 public static void initializeGcmForTest(Context context, Logger logger, String senderId) {
125 GCMRegistrar.checkDevice(context);
126 GCMRegistrar.checkManifest(context);
127 String regId = GCMRegistrar.getRegistrationId(context);
128 if (regId.isEmpty()) {
129 logger.info("Not registered with GCM; registering");
130 GCMRegistrar.register(context, senderId);
132 logger.fine("Already registered with GCM: %s", regId);