Home | History | Annotate | Download | only in service
      1 /*
      2  /*
      3  * Copyright (C) 2011 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.emailcommon.service;
     19 
     20 import android.content.ComponentName;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.ServiceConnection;
     24 import android.os.Debug;
     25 import android.os.IBinder;
     26 import android.os.RemoteException;
     27 import android.util.Log;
     28 
     29 /**
     30  * The EmailServiceProxy class provides a simple interface for the UI to call into the various
     31  * EmailService classes (e.g. ExchangeService for EAS).  It wraps the service connect/disconnect
     32  * process so that the caller need not be concerned with it.
     33  *
     34  * Use the class like this:
     35  *   new EmailServiceClass(context, class).loadAttachment(attachmentId, callback)
     36  *
     37  * Methods without a return value return immediately (i.e. are asynchronous); methods with a
     38  * return value wait for a result from the Service (i.e. they should not be called from the UI
     39  * thread) with a default timeout of 30 seconds (settable)
     40  *
     41  * An EmailServiceProxy object cannot be reused (trying to do so generates a RemoteException)
     42  */
     43 
     44 public abstract class ServiceProxy {
     45     private static final boolean DEBUG_PROXY = false; // DO NOT CHECK THIS IN SET TO TRUE
     46     private final String mTag;
     47 
     48     private final Context mContext;
     49     protected final Intent mIntent;
     50     private Runnable mRunnable = new ProxyRunnable();
     51     private ProxyTask mTask;
     52     private String mName = " unnamed";
     53     private final ServiceConnection mConnection = new ProxyConnection();
     54     // Service call timeout (in seconds)
     55     private int mTimeout = 45;
     56     private long mStartTime;
     57     private boolean mDead = false;
     58 
     59     public abstract void onConnected(IBinder binder);
     60 
     61     public ServiceProxy(Context _context, Intent _intent) {
     62         mContext = _context;
     63         mIntent = _intent;
     64         mTag = getClass().getSimpleName();
     65         if (Debug.isDebuggerConnected()) {
     66             mTimeout <<= 2;
     67         }
     68     }
     69 
     70     private class ProxyConnection implements ServiceConnection {
     71         public void onServiceConnected(ComponentName name, IBinder binder) {
     72             onConnected(binder);
     73             if (DEBUG_PROXY) {
     74                 Log.v(mTag, "Connected: " + name.getShortClassName());
     75             }
     76             // Run our task on a new thread
     77             new Thread(new Runnable() {
     78                 public void run() {
     79                     try {
     80                         runTask();
     81                     } finally {
     82                         endTask();
     83                     }
     84                 }}).start();
     85         }
     86 
     87         public void onServiceDisconnected(ComponentName name) {
     88             if (DEBUG_PROXY) {
     89                 Log.v(mTag, "Disconnected: " + name.getShortClassName());
     90             }
     91         }
     92     }
     93 
     94     public interface ProxyTask {
     95         public void run() throws RemoteException;
     96     }
     97 
     98     private class ProxyRunnable implements Runnable {
     99         @Override
    100         public void run() {
    101             try {
    102                 mTask.run();
    103             } catch (RemoteException e) {
    104             }
    105         }
    106     }
    107 
    108     public ServiceProxy setTimeout(int secs) {
    109         mTimeout = secs;
    110         return this;
    111     }
    112 
    113     public int getTimeout() {
    114         return mTimeout;
    115     }
    116 
    117     public void endTask() {
    118         try {
    119             mContext.unbindService(mConnection);
    120         } catch (IllegalArgumentException e) {
    121             // This can happen if the user ended the activity that was using the service
    122             // This is harmless, but we've got to catch it
    123         }
    124 
    125         mDead = true;
    126         synchronized(mConnection) {
    127             if (DEBUG_PROXY) {
    128                 Log.v(mTag, "Task " + mName + " completed; disconnecting");
    129             }
    130             mConnection.notify();
    131         }
    132     }
    133 
    134     private void runTask() {
    135         Thread thread = new Thread(mRunnable);
    136         thread.start();
    137         try {
    138             thread.join();
    139         } catch (InterruptedException e) {
    140         }
    141     }
    142 
    143     public boolean setTask(ProxyTask task, String name) {
    144         mName = name;
    145         return setTask(task);
    146     }
    147 
    148     public boolean setTask(ProxyTask task) throws IllegalStateException {
    149         if (mDead) {
    150             throw new IllegalStateException();
    151         }
    152         mTask = task;
    153         mStartTime = System.currentTimeMillis();
    154         if (DEBUG_PROXY) {
    155             Log.v(mTag, "Bind requested for task " + mName);
    156         }
    157         return mContext.bindService(mIntent, mConnection, Context.BIND_AUTO_CREATE);
    158     }
    159 
    160     public void waitForCompletion() {
    161         synchronized (mConnection) {
    162             long time = System.currentTimeMillis();
    163             try {
    164                 if (DEBUG_PROXY) {
    165                     Log.v(mTag, "Waiting for task " + mName + " to complete...");
    166                 }
    167                 mConnection.wait(mTimeout * 1000L);
    168             } catch (InterruptedException e) {
    169                 // Can be ignored safely
    170             }
    171             if (DEBUG_PROXY) {
    172                 Log.v(mTag, "Wait for " + mName + " finished in " +
    173                         (System.currentTimeMillis() - time) + "ms");
    174             }
    175         }
    176     }
    177 
    178     public void close() throws RemoteException {
    179         if (mDead) {
    180             throw new RemoteException();
    181         }
    182         endTask();
    183     }
    184 
    185     /**
    186      * Connection test; return indicates whether the remote service can be connected to
    187      * @return the result of trying to connect to the remote service
    188      */
    189     public boolean test() {
    190         try {
    191             return setTask(new ProxyTask() {
    192                 public void run() throws RemoteException {
    193                     if (DEBUG_PROXY) {
    194                         Log.v(mTag, "Connection test succeeded in " +
    195                                 (System.currentTimeMillis() - mStartTime) + "ms");
    196                     }
    197                 }
    198             }, "test");
    199         } catch (Exception e) {
    200             // For any failure, return false.
    201             return false;
    202         }
    203     }
    204 }
    205