Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(749)

Unified Diff: third_party/cacheinvalidation/src/java/com/google/ipc/invalidation/external/client/contrib/AndroidListener.java

Issue 1162033004: Pull cacheinvalidations code directory into chromium repo. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/cacheinvalidation/src/java/com/google/ipc/invalidation/external/client/contrib/AndroidListener.java
diff --git a/third_party/cacheinvalidation/src/java/com/google/ipc/invalidation/external/client/contrib/AndroidListener.java b/third_party/cacheinvalidation/src/java/com/google/ipc/invalidation/external/client/contrib/AndroidListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9a4e9ad807d4758d51400c99ef872c385c9a1c8
--- /dev/null
+++ b/third_party/cacheinvalidation/src/java/com/google/ipc/invalidation/external/client/contrib/AndroidListener.java
@@ -0,0 +1,661 @@
+/*
+ * 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.external.client.contrib;
+
+import com.google.ipc.invalidation.external.client.InvalidationClient;
+import com.google.ipc.invalidation.external.client.InvalidationClientConfig;
+import com.google.ipc.invalidation.external.client.InvalidationListener;
+import com.google.ipc.invalidation.external.client.InvalidationListener.RegistrationState;
+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.types.AckHandle;
+import com.google.ipc.invalidation.external.client.types.ErrorInfo;
+import com.google.ipc.invalidation.external.client.types.Invalidation;
+import com.google.ipc.invalidation.external.client.types.ObjectId;
+import com.google.ipc.invalidation.ticl.InvalidationClientCore;
+import com.google.ipc.invalidation.ticl.ProtoWrapperConverter;
+import com.google.ipc.invalidation.ticl.android2.AndroidClock;
+import com.google.ipc.invalidation.ticl.android2.AndroidInvalidationListenerIntentMapper;
+import com.google.ipc.invalidation.ticl.android2.ProtocolIntents;
+import com.google.ipc.invalidation.ticl.android2.channel.AndroidChannelConstants.AuthTokenConstants;
+import com.google.ipc.invalidation.ticl.proto.AndroidListenerProtocol;
+import com.google.ipc.invalidation.ticl.proto.AndroidListenerProtocol.RegistrationCommand;
+import com.google.ipc.invalidation.ticl.proto.AndroidListenerProtocol.StartCommand;
+import com.google.ipc.invalidation.ticl.proto.ClientProtocol.ClientConfigP;
+import com.google.ipc.invalidation.ticl.proto.ClientProtocol.InvalidationMessage;
+import com.google.ipc.invalidation.ticl.proto.ClientProtocol.InvalidationP;
+import com.google.ipc.invalidation.ticl.proto.ClientProtocol.ObjectIdP;
+import com.google.ipc.invalidation.util.Bytes;
+import com.google.ipc.invalidation.util.Preconditions;
+import com.google.ipc.invalidation.util.ProtoWrapper.ValidationException;
+
+import android.app.IntentService;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Simplified listener contract for Android clients. Takes care of exponential back-off when
+ * register or unregister are called for an object after a failure has occurred. Also suppresses
+ * redundant register requests.
+ *
+ * <p>A sample implementation of an {@link AndroidListener} is shown below:
+ *
+ * <p><code>
+ * class ExampleListener extends AndroidListener {
+ * @Override
+ * public void reissueRegistrations(byte[] clientId) {
+ * List<ObjectId> desiredRegistrations = ...;
+ * register(clientId, desiredRegistrations);
+ * }
+ *
+ * @Override
+ * public void invalidate(Invalidation invalidation, final byte[] ackHandle) {
+ * // Track the most recent version of the object (application-specific) and then acknowledge
+ * // the invalidation.
+ * ...
+ * acknowledge(ackHandle);
+ * }
+ *
+ * @Override
+ * public void informRegistrationFailure(byte[] clientId, ObjectId objectId,
+ * boolean isTransient, String errorMessage) {
+ * // Try again if there is a transient failure and we still care whether the object is
+ * // registered or not.
+ * if (isTransient) {
+ * boolean shouldRetry = ...;
+ * if (shouldRetry) {
+ * boolean shouldBeRegistered = ...;
+ * if (shouldBeRegistered) {
+ * register(clientId, ImmutableList.of(objectId));
+ * } else {
+ * unregister(clientId, ImmutableList.of(objectId));
+ * }
+ * }
+ * }
+ * }
+ *
+ * ...
+ * }
+ * </code>
+ *
+ * <p>See {@link com.google.ipc.invalidation.examples.android2} for a complete sample.
+ *
+ */
+public abstract class AndroidListener extends IntentService {
+
+ /** External alarm receiver that allows the listener to respond to alarm intents. */
+ public static final class AlarmReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(intent);
+ if (intent.hasExtra(AndroidListenerIntents.EXTRA_REGISTRATION)) {
+ AndroidListenerIntents.issueAndroidListenerIntent(context, intent);
+ }
+ }
+ }
+
+ /** The logger. */
+ private static final Logger logger = AndroidLogger.forPrefix("");
+
+ /** Initial retry delay for exponential backoff (1 minute). */
+
+ static int initialMaxDelayMs = (int) TimeUnit.SECONDS.toMillis(60);
+
+ /** Maximum delay factor for exponential backoff (6 hours). */
+
+ static int maxDelayFactor = 6 * 60;
+
+ /** The last client ID passed to the ready up-call. */
+
+ static Bytes lastClientIdForTest;
+
+ /**
+ * Invalidation listener implementation. We implement the interface on a private field rather
+ * than directly to avoid leaking methods that should not be directly called by the client
+ * application. The listener must be called only on intent service thread.
+ */
+ private final InvalidationListener invalidationListener = new InvalidationListener() {
+ @Override
+ public final void ready(final InvalidationClient client) {
+ Bytes clientId = state.getClientId();
+ AndroidListener.lastClientIdForTest = clientId;
+ AndroidListener.this.ready(clientId.getByteArray());
+ }
+
+ @Override
+ public final void reissueRegistrations(final InvalidationClient client, byte[] prefix,
+ int prefixLength) {
+ AndroidListener.this.reissueRegistrations(state.getClientId().getByteArray());
+ }
+
+ @Override
+ public final void informRegistrationStatus(final InvalidationClient client,
+ final ObjectId objectId, final RegistrationState regState) {
+ state.informRegistrationSuccess(objectId);
+ AndroidListener.this.informRegistrationStatus(state.getClientId().getByteArray(), objectId,
+ regState);
+ }
+
+ @Override
+ public final void informRegistrationFailure(final InvalidationClient client,
+ final ObjectId objectId, final boolean isTransient, final String errorMessage) {
+ state.informRegistrationFailure(objectId, isTransient);
+ AndroidListener.this.informRegistrationFailure(state.getClientId().getByteArray(), objectId,
+ isTransient, errorMessage);
+ }
+
+ @Override
+ public void invalidate(InvalidationClient client, Invalidation invalidation,
+ AckHandle ackHandle) {
+ AndroidListener.this.invalidate(invalidation, ackHandle.getHandleData());
+ }
+
+ @Override
+ public void invalidateUnknownVersion(InvalidationClient client, ObjectId objectId,
+ AckHandle ackHandle) {
+ AndroidListener.this.invalidateUnknownVersion(objectId, ackHandle.getHandleData());
+ }
+
+ @Override
+ public void invalidateAll(InvalidationClient client, AckHandle ackHandle) {
+ AndroidListener.this.invalidateAll(ackHandle.getHandleData());
+ }
+
+ @Override
+ public void informError(InvalidationClient client, ErrorInfo errorInfo) {
+ AndroidListener.this.informError(errorInfo);
+ }
+ };
+
+ /**
+ * The internal state of the listener. Lazy initialization, triggered by {@link #onHandleIntent}.
+ */
+ private AndroidListenerState state;
+
+ /** The clock to use when scheduling retry call-backs. */
+ private final AndroidClock clock = new AndroidClock.SystemClock();
+
+ /**
+ * The mapper used to route intents to the invalidation listener. Lazy initialization triggered
+ * by {@link #onCreate}.
+ */
+ private AndroidInvalidationListenerIntentMapper intentMapper;
+
+ /** Initializes {@link AndroidListener}. */
+ protected AndroidListener() {
+ super("");
+
+ // If the process dies before an intent is handled, setIntentRedelivery(true) ensures that the
+ // last intent is redelivered. This optimization is not necessary for correctness: on restart,
+ // all registrations will be reissued and unacked invalidations will be resent anyways.
+ setIntentRedelivery(true);
+ }
+
+ /** See specs for {@link InvalidationClient#start}. */
+ public static Intent createStartIntent(Context context, InvalidationClientConfig config) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(config);
+ Preconditions.checkNotNull(config.clientName);
+
+ return AndroidListenerIntents.createStartIntent(context, config.clientType,
+ Bytes.fromByteArray(config.clientName), config.allowSuppression);
+ }
+
+ /** See specs for {@link InvalidationClient#start}. */
+ public static Intent createStartIntent(Context context, int clientType, byte[] clientName) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(clientName);
+
+ final boolean allowSuppression = true;
+ return AndroidListenerIntents.createStartIntent(context, clientType,
+ Bytes.fromByteArray(clientName), allowSuppression);
+ }
+
+ /** See specs for {@link InvalidationClient#stop}. */
+ public static Intent createStopIntent(Context context) {
+ Preconditions.checkNotNull(context);
+
+ return AndroidListenerIntents.createStopIntent(context);
+ }
+
+ /**
+ * See specs for {@link InvalidationClient#register}.
+ *
+ * @param context the context
+ * @param clientId identifier for the client service for which we are registering
+ * @param objectIds the object ids being registered
+ */
+ public static Intent createRegisterIntent(Context context, byte[] clientId,
+ Iterable<ObjectId> objectIds) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(clientId);
+ Preconditions.checkNotNull(objectIds);
+
+ final boolean isRegister = true;
+ return AndroidListenerIntents.createRegistrationIntent(context, Bytes.fromByteArray(clientId),
+ objectIds, isRegister);
+ }
+
+ /**
+ * See specs for {@link InvalidationClient#register}.
+ *
+ * @param clientId identifier for the client service for which we are registering
+ * @param objectIds the object ids being registered
+ */
+ public void register(byte[] clientId, Iterable<ObjectId> objectIds) {
+ Preconditions.checkNotNull(clientId);
+ Preconditions.checkNotNull(objectIds);
+
+ Context context = getApplicationContext();
+ context.startService(createRegisterIntent(context, clientId, objectIds));
+ }
+
+ /**
+ * See specs for {@link InvalidationClient#unregister}.
+ *
+ * @param context the context
+ * @param clientId identifier for the client service for which we are unregistering
+ * @param objectIds the object ids being unregistered
+ */
+ public static Intent createUnregisterIntent(Context context, byte[] clientId,
+ Iterable<ObjectId> objectIds) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(clientId);
+ Preconditions.checkNotNull(objectIds);
+
+ final boolean isRegister = false;
+ return AndroidListenerIntents.createRegistrationIntent(context, Bytes.fromByteArray(clientId),
+ objectIds, isRegister);
+ }
+
+ /**
+ * Sets the authorization token and type used by the invalidation client. Call in response to
+ * {@link #requestAuthToken} calls.
+ *
+ * @param pendingIntent pending intent passed to {@link #requestAuthToken}
+ * @param authToken authorization token
+ * @param authType authorization token typo
+ */
+ public static void setAuthToken(Context context, PendingIntent pendingIntent, String authToken,
+ String authType) {
+ Preconditions.checkNotNull(pendingIntent);
+ Preconditions.checkNotNull(authToken);
+ Preconditions.checkNotNull(authType);
+
+ AndroidListenerIntents.issueAuthTokenResponse(context, pendingIntent, authToken, authType);
+ }
+
+ /**
+ * See specs for {@link InvalidationClient#unregister}.
+ *
+ * @param clientId identifier for the client service for which we are registering
+ * @param objectIds the object ids being unregistered
+ */
+ public void unregister(byte[] clientId, Iterable<ObjectId> objectIds) {
+ Preconditions.checkNotNull(clientId);
+ Preconditions.checkNotNull(objectIds);
+
+ Context context = getApplicationContext();
+ context.startService(createUnregisterIntent(context, clientId, objectIds));
+ }
+
+ /** See specs for {@link InvalidationClient#acknowledge}. */
+ public static Intent createAcknowledgeIntent(Context context, byte[] ackHandle) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(ackHandle);
+
+ return AndroidListenerIntents.createAckIntent(context, ackHandle);
+ }
+
+ /** See specs for {@link InvalidationClient#acknowledge}. */
+ public void acknowledge(byte[] ackHandle) {
+ Preconditions.checkNotNull(ackHandle);
+
+ Context context = getApplicationContext();
+ context.startService(createAcknowledgeIntent(context, ackHandle));
+ }
+
+ /**
+ * See specs for {@link InvalidationListener#ready}.
+ *
+ * @param clientId the client identifier that must be passed to {@link #createRegisterIntent}
+ * and {@link #createUnregisterIntent}
+ */
+ public abstract void ready(byte[] clientId);
+
+ /**
+ * See specs for {@link InvalidationListener#reissueRegistrations}.
+ *
+ * @param clientId the client identifier that must be passed to {@link #createRegisterIntent}
+ * and {@link #createUnregisterIntent}
+ */
+ public abstract void reissueRegistrations(byte[] clientId);
+
+ /**
+ * See specs for {@link InvalidationListener#informError}.
+ */
+ public abstract void informError(ErrorInfo errorInfo);
+
+ /**
+ * See specs for {@link InvalidationListener#invalidate}.
+ *
+ * @param invalidation the invalidation
+ * @param ackHandle event acknowledgment handle
+ */
+ public abstract void invalidate(Invalidation invalidation, byte[] ackHandle);
+
+ /**
+ * See specs for {@link InvalidationListener#invalidateUnknownVersion}.
+ *
+ * @param objectId identifier for the object with unknown version
+ * @param ackHandle event acknowledgment handle
+ */
+ public abstract void invalidateUnknownVersion(ObjectId objectId, byte[] ackHandle);
+
+ /**
+ * See specs for {@link InvalidationListener#invalidateAll}.
+ *
+ * @param ackHandle event acknowledgment handle
+ */
+ public abstract void invalidateAll(byte[] ackHandle);
+
+ /**
+ * Read listener state.
+ *
+ * @return serialized state or {@code null} if it is not available
+ */
+ public abstract byte[] readState();
+
+ /** Write listener state to some location. */
+ public abstract void writeState(byte[] data);
+
+ /**
+ * See specs for {@link InvalidationListener#informRegistrationFailure}.
+ */
+ public abstract void informRegistrationFailure(byte[] clientId, ObjectId objectId,
+ boolean isTransient, String errorMessage);
+
+ /**
+ * See specs for (@link InvalidationListener#informRegistrationStatus}.
+ */
+ public abstract void informRegistrationStatus(byte[] clientId, ObjectId objectId,
+ RegistrationState regState);
+
+ /**
+ * Called when an authorization token is needed. Respond by calling {@link #setAuthToken}.
+ *
+ * @param pendingIntent pending intent that must be used in {@link #setAuthToken} response.
+ * @param invalidAuthToken the existing invalid token or null if none exists. Implementation
+ * should invalidate the token.
+ */
+ public abstract void requestAuthToken(PendingIntent pendingIntent,
+ String invalidAuthToken);
+
+ /**
+ * Handles invalidations received while the client is stopped. An implementation may choose to
+ * do work in response to these invalidations (delivered best-effort by the invalidation system).
+ * Not intended for use by most client implementations.
+ */
+ protected void backgroundInvalidateForInternalUse(
+ @SuppressWarnings("unused") Iterable<Invalidation> invalidations) {
+ // Ignore background invalidations by default.
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ // Initialize the intent mapper (now that context is available).
+ intentMapper = new AndroidInvalidationListenerIntentMapper(invalidationListener, this);
+ }
+
+ /**
+ * Derived classes may override this method to handle custom intents. This is a recommended
+ * pattern for invalidation-related intents, e.g. for registration and unregistration. Derived
+ * classes should call {@code super.onHandleIntent(intent)} for any intents they did not
+ * handle on their own.
+ */
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ if (intent == null) {
+ return;
+ }
+
+ // We lazily initialize state in calls to onHandleIntent rather than initializing in onCreate
+ // because onCreate runs on the UI thread and initializeState performs I/O.
+ if (state == null) {
+ initializeState();
+ }
+
+ // Handle any intents specific to the AndroidListener. For other intents, defer to the
+ // intentMapper, which handles listener upcalls corresponding to the InvalidationListener
+ // methods.
+ if (!tryHandleAuthTokenRequestIntent(intent) &&
+ !tryHandleRegistrationIntent(intent) &&
+ !tryHandleStartIntent(intent) &&
+ !tryHandleStopIntent(intent) &&
+ !tryHandleAckIntent(intent) &&
+ !tryHandleBackgroundInvalidationsIntent(intent)) {
+ intentMapper.handleIntent(intent);
+ }
+
+ // Always check to see if we need to persist state changes after handling an intent.
+ if (state.getIsDirty()) {
+ writeState(state.marshal().toByteArray());
+ state.resetIsDirty();
+ }
+ }
+
+ /** Returns invalidation client that can be used to trigger intents against the TICL service. */
+ private InvalidationClient getClient() {
+ return intentMapper.client;
+ }
+
+ /**
+ * Initializes listener state either from persistent proto (if available) or from scratch.
+ */
+ private void initializeState() {
+ AndroidListenerProtocol.AndroidListenerState proto = getPersistentState();
+ if (proto != null) {
+ state = new AndroidListenerState(initialMaxDelayMs, maxDelayFactor, proto);
+ } else {
+ state = new AndroidListenerState(initialMaxDelayMs, maxDelayFactor);
+ }
+ }
+
+ /**
+ * Reads and parses persistent state for the listener. Returns {@code null} if the state does not
+ * exist or is invalid.
+ */
+ private AndroidListenerProtocol.AndroidListenerState getPersistentState() {
+ // Defer to application code to read the blob containing the state proto.
+ byte[] stateData = readState();
+ try {
+ if (null != stateData) {
+ AndroidListenerProtocol.AndroidListenerState state =
+ AndroidListenerProtocol.AndroidListenerState.parseFrom(stateData);
+ if (!AndroidListenerProtos.isValidAndroidListenerState(state)) {
+ logger.warning("Invalid listener state.");
+ return null;
+ }
+ return state;
+ }
+ } catch (ValidationException exception) {
+ logger.warning("Failed to parse listener state: %s", exception);
+ }
+ return null;
+ }
+
+ /**
+ * Tries to handle a request for an authorization token. Returns {@code true} iff the intent is
+ * an auth token request.
+ */
+ private boolean tryHandleAuthTokenRequestIntent(Intent intent) {
+ if (!AndroidListenerIntents.isAuthTokenRequest(intent)) {
+ return false;
+ }
+
+ // Check for invalid auth token. Subclass may have to invalidate it if it exists in the call
+ // to getNewAuthToken.
+ String invalidAuthToken = intent.getStringExtra(
+ AuthTokenConstants.EXTRA_INVALIDATE_AUTH_TOKEN);
+ // Intent also includes a pending intent that we can use to pass back our response.
+ PendingIntent pendingIntent = intent.getParcelableExtra(
+ AuthTokenConstants.EXTRA_PENDING_INTENT);
+ if (pendingIntent == null) {
+ logger.warning("Authorization request without pending intent extra.");
+ } else {
+ // Delegate to client application to figure out what the new token should be and the auth
+ // type.
+ requestAuthToken(pendingIntent, invalidAuthToken);
+ }
+ return true;
+ }
+
+ /** Tries to handle a stop intent. Returns {@code true} iff the intent is a stop intent. */
+ private boolean tryHandleStopIntent(Intent intent) {
+ if (!AndroidListenerIntents.isStopIntent(intent)) {
+ return false;
+ }
+ getClient().stop();
+ return true;
+ }
+
+ /**
+ * Tries to handle a registration intent. Returns {@code true} iff the intent is a registration
+ * intent.
+ */
+ private boolean tryHandleRegistrationIntent(Intent intent) {
+ RegistrationCommand command = AndroidListenerIntents.findRegistrationCommand(intent);
+ if ((command == null) || !AndroidListenerProtos.isValidRegistrationCommand(command)) {
+ return false;
+ }
+ // Make sure the registration is intended for this client. If not, we ignore it (suggests
+ // there is a new client now).
+ if (!command.getClientId().equals(state.getClientId())) {
+ logger.warning("Ignoring registration request for old client. Old ID = %s, New ID = %s",
+ command.getClientId(), state.getClientId());
+ return true;
+ }
+ boolean isRegister = command.getIsRegister();
+ for (ObjectIdP objectIdP : command.getObjectId()) {
+ ObjectId objectId = ProtoWrapperConverter.convertFromObjectIdProto(objectIdP);
+ // We may need to delay the registration command (if it is not already delayed).
+ int delayMs = 0;
+ if (!command.getIsDelayed()) {
+ delayMs = state.getNextDelay(objectId);
+ }
+ if (delayMs == 0) {
+ issueRegistration(objectId, isRegister);
+ } else {
+ AndroidListenerIntents.issueDelayedRegistrationIntent(getApplicationContext(), clock,
+ state.getClientId(), objectId, isRegister, delayMs, state.getNextRequestCode());
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Called when the client application requests a new registration. If a redundant register request
+ * is made -- i.e. when the application attempts to register an object that is already in the
+ * {@code AndroidListenerState#desiredRegistrations} collection -- the method returns immediately.
+ * Unregister requests are never ignored since we can't reliably determine whether an unregister
+ * request is redundant: our policy on failures of any kind is to remove the registration from
+ * the {@code AndroidListenerState#desiredRegistrations} collection.
+ */
+ private void issueRegistration(ObjectId objectId, boolean isRegister) {
+ if (isRegister) {
+ if (state.addDesiredRegistration(objectId)) {
+ // Don't bother if we think it's already registered. Note that we remove the object from the
+ // collection when there is a failure.
+ getClient().register(objectId);
+ }
+ } else {
+ // Remove the object ID from the desired registration collection so that subsequent attempts
+ // to re-register are not ignored.
+ state.removeDesiredRegistration(objectId);
+ getClient().unregister(objectId);
+ }
+ }
+
+ /** Tries to handle a start intent. Returns {@code true} iff the intent is a start intent. */
+ private boolean tryHandleStartIntent(Intent intent) {
+ StartCommand command = AndroidListenerIntents.findStartCommand(intent);
+ if ((command == null) || !AndroidListenerProtos.isValidStartCommand(command)) {
+ return false;
+ }
+ // Reset the state so that we make no assumptions about desired registrations and can ignore
+ // messages directed at the wrong instance.
+ state = new AndroidListenerState(initialMaxDelayMs, maxDelayFactor);
+ boolean skipStartForTest = false;
+ ClientConfigP clientConfig = InvalidationClientCore.createConfig();
+ if (command.getAllowSuppression() != clientConfig.getAllowSuppression()) {
+ ClientConfigP.Builder clientConfigBuilder = clientConfig.toBuilder();
+ clientConfigBuilder.allowSuppression = command.getAllowSuppression();
+ clientConfig = clientConfigBuilder.build();
+ }
+ Intent startIntent = ProtocolIntents.InternalDowncalls.newCreateClientIntent(
+ command.getClientType(), command.getClientName(), clientConfig, skipStartForTest);
+ AndroidListenerIntents.issueTiclIntent(getApplicationContext(), startIntent);
+ return true;
+ }
+
+ /** Tries to handle an ack intent. Returns {@code true} iff the intent is an ack intent. */
+ private boolean tryHandleAckIntent(Intent intent) {
+ byte[] data = AndroidListenerIntents.findAckHandle(intent);
+ if (data == null) {
+ return false;
+ }
+ getClient().acknowledge(AckHandle.newInstance(data));
+ return true;
+ }
+
+ /**
+ * Tries to handle a background invalidation intent. Returns {@code true} iff the intent is a
+ * background invalidation intent.
+ */
+ private boolean tryHandleBackgroundInvalidationsIntent(Intent intent) {
+ byte[] data = intent.getByteArrayExtra(ProtocolIntents.BACKGROUND_INVALIDATION_KEY);
+ if (data == null) {
+ return false;
+ }
+ try {
+ InvalidationMessage invalidationMessage = InvalidationMessage.parseFrom(data);
+ List<Invalidation> invalidations = new ArrayList<Invalidation>();
+ for (InvalidationP invalidation : invalidationMessage.getInvalidation()) {
+ invalidations.add(ProtoWrapperConverter.convertFromInvalidationProto(invalidation));
+ }
+ backgroundInvalidateForInternalUse(invalidations);
+ } catch (ValidationException exception) {
+ logger.info("Failed to parse background invalidation intent payload: %s",
+ exception.getMessage());
+ }
+ return false;
+ }
+
+ /** Returns the current state of the listener, for tests. */
+ AndroidListenerState getStateForTest() {
+ return state;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698