/* * Copyright 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.ipc.invalidation.ticl.android2.channel; import com.google.android.gcm.GCMRegistrar; import com.google.ipc.invalidation.external.client.SystemResources.Logger; import com.google.ipc.invalidation.external.client.android.service.AndroidLogger; import com.google.ipc.invalidation.external.client.contrib.MultiplexingGcmListener; import com.google.ipc.invalidation.ticl.android2.AndroidTiclManifest; import com.google.ipc.invalidation.ticl.android2.ProtocolIntents; import com.google.ipc.invalidation.ticl.android2.channel.AndroidChannelConstants.C2dmConstants; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protos.ipc.invalidation.AndroidChannel.AddressedAndroidMessage; import android.content.Context; import android.content.Intent; import android.util.Base64; /** * Service that receives messages from using GCM. * */ public class AndroidMessageReceiverService extends MultiplexingGcmListener.AbstractListener { /* * This class is public so that it can be instantiated by the Android runtime. All of the * {@code onYYY} methods are called holding a wakelock that will be automatically released when * they return, since this is a subclass of {@code AbstractListener}. */ /** * Receiver for broadcasts by the multiplexed GCM service. It forwards them to * AndroidMessageReceiverService. */ public static class Receiver extends MultiplexingGcmListener.AbstractListener.Receiver { /* This class is public so that it can be instantiated by the Android runtime. */ @Override protected Class<?> getServiceClass() { return AndroidMessageReceiverService.class; } } private final Logger logger = AndroidLogger.forTag("MsgRcvrSvc"); public AndroidMessageReceiverService() { super("AndroidMessageReceiverService"); } @Override protected void onMessage(Intent intent) { // Forward the message to the Ticl service. if (intent.hasExtra(C2dmConstants.CONTENT_PARAM)) { String content = intent.getStringExtra(C2dmConstants.CONTENT_PARAM); byte[] msgBytes = Base64.decode(content, Base64.URL_SAFE); try { // Look up the name of the Ticl service class from the manifest. String serviceClass = new AndroidTiclManifest(this).getTiclServiceClass(); AddressedAndroidMessage addrMessage = AddressedAndroidMessage.parseFrom(msgBytes); Intent msgIntent = ProtocolIntents.InternalDowncalls.newServerMessageIntent(addrMessage.getMessage()); msgIntent.setClassName(this, serviceClass); startService(msgIntent); } catch (InvalidProtocolBufferException exception) { logger.warning("Failed parsing inbound message: %s", exception); } } else { logger.fine("GCM Intent has no message content: %s", intent); } // Store the echo token. String echoToken = intent.getStringExtra(C2dmConstants.ECHO_PARAM); if (echoToken != null) { AndroidChannelPreferences.setEchoToken(this, echoToken); } } @Override protected void onRegistered(String registrationId) { // Inform the sender service that the registration id has changed. If the sender service // had buffered a message because no registration id was previously available, this intent // will cause it to send that message. Intent sendBuffered = new Intent(); final String ignoredData = ""; sendBuffered.putExtra(AndroidChannelConstants.MESSAGE_SENDER_SVC_GCM_REGID_CHANGE, ignoredData); sendBuffered.setClass(this, AndroidMessageSenderService.class); startService(sendBuffered); // Inform the Ticl service that the registration id has changed. This will cause it to send // a message to the data center and update the GCM registration id stored at the data center. Intent updateServer = ProtocolIntents.InternalDowncalls.newNetworkAddrChangeIntent(); updateServer.setClassName(this, new AndroidTiclManifest(this).getTiclServiceClass()); startService(updateServer); } @Override protected void onUnregistered(String registrationId) { // Nothing to do. } @Override protected void onDeletedMessages(int total) { // This method must be implemented if we start using non-collapsable messages with GCM. For // now, there is nothing to do. } /** * Initializes GCM as a convenience method for tests. In production, applications should handle * this. */ public static void initializeGcmForTest(Context context, Logger logger, String senderId) { // Initialize GCM. GCMRegistrar.checkDevice(context); GCMRegistrar.checkManifest(context); String regId = GCMRegistrar.getRegistrationId(context); if (regId.equals("")) { logger.info("Not registered with GCM; registering"); GCMRegistrar.register(context, senderId); } else { logger.fine("Already registered with GCM: %s", regId); } } }