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.
17 package com.google.ipc.invalidation.external.client.android;
19 import com.google.ipc.invalidation.external.client.InvalidationListener;
20 import com.google.ipc.invalidation.external.client.SystemResources.Logger;
21 import com.google.ipc.invalidation.external.client.android.service.AndroidLogger;
22 import com.google.ipc.invalidation.external.client.android.service.Event;
23 import com.google.ipc.invalidation.external.client.android.service.Event.Action;
24 import com.google.ipc.invalidation.external.client.android.service.ListenerService;
25 import com.google.ipc.invalidation.external.client.android.service.Response;
26 import com.google.ipc.invalidation.external.client.types.AckHandle;
27 import com.google.ipc.invalidation.external.client.types.ErrorInfo;
28 import com.google.ipc.invalidation.external.client.types.Invalidation;
29 import com.google.ipc.invalidation.external.client.types.ObjectId;
31 import android.app.Service;
32 import android.content.Intent;
33 import android.os.Bundle;
34 import android.os.IBinder;
37 * An abstract base class for implementing a {@link Service} component
38 * that handles events from the invalidation service. This class should be
39 * subclassed and concrete implementations of the {@link InvalidationListener}
40 * methods added to provide application-specific handling of invalidation
43 * This implementing subclass should be registered in {@code
44 * AndroidManifest.xml} as a service of the invalidation
45 * listener binding intent, as in the following sample fragment:
52 * service android:name="com.myco.example.AppListenerService" ...>
54 * <action android:name="com.google.ipc.invalidation.LISTENER"/>
65 public abstract class AndroidInvalidationListener extends Service
66 implements InvalidationListener {
69 private static final Logger logger = AndroidLogger.forTag("InvListener");
72 * Simple service stub that delegates back to methods on the service.
74 private final ListenerService.Stub listenerBinder = new ListenerService.Stub() {
77 public void handleEvent(Bundle input, Bundle output) {
78 AndroidInvalidationListener.this.handleEvent(input, output);
82 /** Lock over all state in this class. */
83 private final Object lock = new Object();
85 /** Whether the service is in the created state. */
86 private boolean isCreated = false;
89 public void onCreate() {
92 logger.fine("onCreate: %s", this.getClass());
93 this.isCreated = true;
98 public void onDestroy() {
100 logger.fine("onDestroy: %s", this.getClass());
101 this.isCreated = false;
107 public IBinder onBind(Intent arg0) {
108 synchronized (lock) {
109 logger.fine("Binding: %s", arg0);
110 return listenerBinder;
115 * Handles a {@link ListenerService#handleEvent} call received by the
118 * @param input bundle containing event parameters.
119 * @param output bundled used to return response to the invalidation service.
121 protected void handleEvent(Bundle input, Bundle output) {
122 synchronized (lock) {
124 logger.warning("Dropping bundle since not created: %s", input);
127 Event event = new Event(input);
128 Response.Builder response = Response.newBuilder(event.getActionOrdinal(), output);
129 // All events should contain an action and client id
130 Action action = event.getAction();
131 String clientKey = event.getClientKey();
132 logger.fine("Received %s event for %s", action, clientKey);
134 AndroidInvalidationClient client = null;
136 if (clientKey == null) {
137 throw new IllegalStateException("Missing client id:" + event);
140 // Obtain the client instance for the client receiving the event. Do not attempt to load it
141 // at the service: if a Ticl has been unloaded, the listener shouldn't resurrect it, because
142 // that can lead to a zombie client.
143 client = AndroidClientFactory.resume(this, clientKey, false);
145 // Determine the event type based upon the request action, extract parameters
146 // from extras, and invoke the listener event handler method.
147 logger.fine("%s event for %s", action, clientKey);
156 Invalidation invalidation = event.getInvalidation();
157 AckHandle ackHandle = event.getAckHandle();
158 invalidate(client, invalidation, ackHandle);
161 case INVALIDATE_UNKNOWN:
163 ObjectId objectId = event.getObjectId();
164 AckHandle ackHandle = event.getAckHandle();
165 invalidateUnknownVersion(client, objectId, ackHandle);
170 AckHandle ackHandle = event.getAckHandle();
171 invalidateAll(client, ackHandle);
174 case INFORM_REGISTRATION_STATUS:
176 ObjectId objectId = event.getObjectId();
177 RegistrationState state = event.getRegistrationState();
178 informRegistrationStatus(client, objectId, state);
181 case INFORM_REGISTRATION_FAILURE:
183 ObjectId objectId = event.getObjectId();
184 String errorMsg = event.getError();
185 boolean isTransient = event.getIsTransient();
186 informRegistrationFailure(client, objectId, isTransient, errorMsg);
189 case REISSUE_REGISTRATIONS:
191 byte[] prefix = event.getPrefix();
192 int prefixLength = event.getPrefixLength();
193 reissueRegistrations(client, prefix, prefixLength);
198 ErrorInfo errorInfo = event.getErrorInfo();
199 informError(client, errorInfo);
203 logger.warning("Urecognized event: %s", event);
205 response.setStatus(Response.Status.SUCCESS);
206 } catch (RuntimeException re) {
207 // If an exception occurs during processing, log it, and store the
208 // result in the response sent back to the service.
209 logger.severe("Failure in handleEvent", re);
210 response.setError(re.getMessage());
212 // Listeners will only use a client reference for the life of the event and release
213 // it immediately since there is no way to know if additional events are coming.
214 if (client != null) {