blob: add2259d30d55b963272422ba7f3358254c3cd65 [file] [log] [blame]
/*
* 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.ipc.invalidation.ticl.proto.AndroidChannel.AddressedAndroidMessage;
import com.google.ipc.invalidation.util.ProtoWrapper.ValidationException;
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 (ValidationException exception) {
logger.warning("Failed parsing inbound message: %s", exception);
} catch (IllegalStateException exception) {
logger.warning("Unable to handle 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(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);
try {
startService(sendBuffered);
} catch (IllegalStateException exception) {
logger.warning("Unable to send buffered message(s): %s", exception);
}
// 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());
try {
startService(updateServer);
} catch (IllegalStateException exception) {
logger.warning("Unable to handle new registration id: %s", exception);
}
}
@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.isEmpty()) {
logger.info("Not registered with GCM; registering");
GCMRegistrar.register(context, senderId);
} else {
logger.fine("Already registered with GCM: %s", regId);
}
}
}