| /* |
| * 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.android; |
| |
| 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.android.service.Event; |
| import com.google.ipc.invalidation.external.client.android.service.InvalidationService; |
| import com.google.ipc.invalidation.external.client.android.service.ListenerService; |
| import com.google.ipc.invalidation.external.client.android.service.Request; |
| import com.google.ipc.invalidation.external.client.android.service.Request.Action; |
| import com.google.ipc.invalidation.external.client.android.service.Response; |
| import com.google.ipc.invalidation.external.client.android.service.Response.Status; |
| |
| import android.app.Service; |
| import android.content.Intent; |
| import android.os.Bundle; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| |
| /** |
| * Abstract base class for implementing the Android invalidation service. The service implements the |
| * set of actions defined in {@link Action}. For each supported action, the service will extract the |
| * action parameters and invoke an abstract methods that will be implemented by subclasses to |
| * provide the action-specific processing. |
| * <p> |
| * This class acquires a lock before calling into the subclass and releases it after the call. |
| * It also ensures that no call into the subclass will be made after the service has been destroyed. |
| * <p> |
| * The class also provides {@code sendEvent} methods that can be used to generate events back to the |
| * client. |
| * |
| */ |
| public abstract class AbstractInvalidationService extends Service { |
| |
| private static final Logger logger = AndroidLogger.forTag("InvService"); |
| |
| /** |
| * Simple service stub that delegates back to methods on the service. |
| */ |
| private final InvalidationService.Stub serviceBinder = new InvalidationService.Stub() { |
| |
| @Override |
| public void handleRequest(Bundle input, Bundle output) { |
| AbstractInvalidationService.this.handleRequest(input, output); |
| } |
| }; |
| |
| /** Lock over all state in this class. */ |
| final Object lock = new Object(); |
| |
| /** Whether the service is in the created state. */ |
| private boolean isCreated = false; |
| |
| @Override |
| public void onCreate() { |
| synchronized (lock) { |
| super.onCreate(); |
| logger.fine("onCreate: %s", this.getClass()); |
| this.isCreated = true; |
| } |
| } |
| |
| @Override |
| public void onDestroy() { |
| synchronized (lock) { |
| logger.fine("onDestroy: %s", this.getClass()); |
| this.isCreated = false; |
| super.onDestroy(); |
| } |
| } |
| |
| @Override |
| public int onStartCommand(Intent intent, int flags, int startId) { |
| return START_NOT_STICKY; |
| } |
| |
| @Override |
| public IBinder onBind(Intent intent) { |
| return serviceBinder; |
| } |
| |
| /** Returns whether the service is started. */ |
| boolean isCreatedForTest() { |
| synchronized (lock) { |
| return isCreated; |
| } |
| } |
| |
| protected void handleRequest(Bundle input, Bundle output) { |
| synchronized (lock) { |
| if (!isCreated) { |
| logger.warning("Dropping bundle since not created: %s", input); |
| return; |
| } |
| Request request = new Request(input); |
| Response.Builder response = Response.newBuilder(request.getActionOrdinal(), output); |
| Action action = request.getAction(); |
| logger.fine("%s request from %s", action, request.getClientKey()); |
| try { |
| switch(action) { |
| case CREATE: |
| create(request, response); |
| break; |
| case RESUME: |
| resume(request, response); |
| break; |
| case START: |
| start(request, response); |
| break; |
| case STOP: |
| stop(request, response); |
| break; |
| case REGISTER: |
| register(request, response); |
| break; |
| case UNREGISTER: |
| unregister(request, response); |
| break; |
| case ACKNOWLEDGE: |
| acknowledge(request, response); |
| break; |
| case DESTROY: |
| destroy(request, response); |
| break; |
| default: |
| throw new IllegalStateException("Unknown action:" + action); |
| } |
| } catch (Exception e) { |
| logger.severe("Client request error", e); |
| response.setStatus(Status.RUNTIME_ERROR); // Subclass might already have set status. |
| response.setException(e); |
| } |
| } |
| } |
| |
| protected abstract void create(Request request, Response.Builder response); |
| |
| protected abstract void resume(Request request, Response.Builder response); |
| |
| protected abstract void start(Request request, Response.Builder response); |
| |
| protected abstract void stop(Request request, Response.Builder response); |
| |
| protected abstract void register(Request request, Response.Builder response); |
| |
| protected abstract void unregister(Request request, Response.Builder response); |
| |
| protected abstract void acknowledge(Request request, Response.Builder response); |
| |
| protected abstract void destroy(Request request, Response.Builder response); |
| |
| /** |
| * Send event messages to application clients and provides common processing |
| * of the response. |
| */ |
| protected void sendEvent(ListenerService listenerService, Event event) { |
| try { |
| logger.fine("Sending %s event", event.getAction()); |
| Bundle responseBundle = new Bundle(); |
| listenerService.handleEvent(event.getBundle(), responseBundle); |
| |
| // Wrap the response bundle and throw on any failure from the client |
| Response response = new Response(responseBundle); |
| response.warnOnFailure(); |
| } catch (RemoteException exception) { |
| logger.severe("Unable to send event", exception); |
| throw new RuntimeException("Unable to send event", exception); |
| } |
| } |
| } |