/* * 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; import com.google.ipc.invalidation.external.client.InvalidationClient; import com.google.ipc.invalidation.external.client.InvalidationListener; import com.google.ipc.invalidation.external.client.InvalidationListener.RegistrationState; import com.google.ipc.invalidation.external.client.android.service.AndroidLogger; import com.google.ipc.invalidation.external.client.types.AckHandle; import com.google.ipc.invalidation.external.client.types.ErrorInfo; import com.google.ipc.invalidation.ticl.ProtoConverter; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall; import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall.ErrorUpcall; import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall.InvalidateUpcall; import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall.RegistrationFailureUpcall; import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall.RegistrationStatusUpcall; import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall.ReissueRegistrationsUpcall; import android.content.Context; import android.content.Intent; import java.util.Arrays; /** * Routes intents to the appropriate methods in {@link InvalidationListener}. Typically, an instance * of the mapper should be created in {@code IntentService#onCreate} and the {@link #handleIntent} * method called in {@code IntentService#onHandleIntent}. * */ public final class AndroidInvalidationListenerIntentMapper { /** The logger. */ private final AndroidLogger logger = AndroidLogger.forPrefix(""); private final AndroidIntentProtocolValidator validator = new AndroidIntentProtocolValidator(logger); /** Client passed to the listener (supports downcalls). */ public final InvalidationClient client; /** Listener to which intents are routed. */ private final InvalidationListener listener; /** * Initializes * * @param listener the listener to which intents should be routed * @param context the context used by the listener to issue downcalls to the TICL */ public AndroidInvalidationListenerIntentMapper(InvalidationListener listener, Context context) { client = new AndroidInvalidationClientStub(context, logger); this.listener = listener; } /** * Handles a listener upcall by decoding the protocol buffer in {@code intent} and dispatching * to the appropriate method on the {@link #listener}. */ public void handleIntent(Intent intent) { // TODO: use wakelocks // Unmarshall the arguments from the Intent and make the appropriate call on the listener. ListenerUpcall upcall = tryParseIntent(intent); if (upcall == null) { return; } if (upcall.hasReady()) { listener.ready(client); } else if (upcall.hasInvalidate()) { // Handle all invalidation-related upcalls on a common path, since they require creating // an AckHandleP. onInvalidateUpcall(upcall, listener); } else if (upcall.hasRegistrationStatus()) { RegistrationStatusUpcall regStatus = upcall.getRegistrationStatus(); listener.informRegistrationStatus(client, ProtoConverter.convertFromObjectIdProto(regStatus.getObjectId()), regStatus.getIsRegistered() ? RegistrationState.REGISTERED : RegistrationState.UNREGISTERED); } else if (upcall.hasRegistrationFailure()) { RegistrationFailureUpcall failure = upcall.getRegistrationFailure(); listener.informRegistrationFailure(client, ProtoConverter.convertFromObjectIdProto(failure.getObjectId()), failure.getTransient(), failure.getMessage()); } else if (upcall.hasReissueRegistrations()) { ReissueRegistrationsUpcall reissueRegs = upcall.getReissueRegistrations(); listener.reissueRegistrations(client, reissueRegs.getPrefix().toByteArray(), reissueRegs.getLength()); } else if (upcall.hasError()) { ErrorUpcall error = upcall.getError(); ErrorInfo errorInfo = ErrorInfo.newInstance(error.getErrorCode(), error.getIsTransient(), error.getErrorMessage(), null); listener.informError(client, errorInfo); } else { logger.warning("Dropping listener Intent with unknown call: %s", upcall); } } /** * Handles an invalidation-related listener {@code upcall} by dispatching to the appropriate * method on an instance of {@link #listenerClass}. */ private void onInvalidateUpcall(ListenerUpcall upcall, InvalidationListener listener) { InvalidateUpcall invalidate = upcall.getInvalidate(); AckHandle ackHandle = AckHandle.newInstance(invalidate.getAckHandle().toByteArray()); if (invalidate.hasInvalidation()) { listener.invalidate(client, ProtoConverter.convertFromInvalidationProto(invalidate.getInvalidation()), ackHandle); } else if (invalidate.hasInvalidateAll()) { listener.invalidateAll(client, ackHandle); } else if (invalidate.hasInvalidateUnknown()) { listener.invalidateUnknownVersion(client, ProtoConverter.convertFromObjectIdProto(invalidate.getInvalidateUnknown()), ackHandle); } else { throw new RuntimeException("Invalid invalidate upcall: " + invalidate); } } /** * Returns a valid {@link ListenerUpcall} from {@code intent}, or {@code null} if one * could not be parsed. */ private ListenerUpcall tryParseIntent(Intent intent) { if (intent == null) { return null; } byte[] upcallBytes = intent.getByteArrayExtra(ProtocolIntents.LISTENER_UPCALL_KEY); if (upcallBytes == null) { return null; } try { ListenerUpcall upcall = ListenerUpcall.parseFrom(upcallBytes); if (!validator.isListenerUpcallValid(upcall)) { logger.warning("Ignoring invalid listener upcall: %s", upcall); return null; } return upcall; } catch (InvalidProtocolBufferException exception) { logger.severe("Could not parse listener upcall from %s", Arrays.toString(upcallBytes)); return null; } } }