• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
onConnected(IBinder binder)59     public abstract void onConnected(IBinder binder);
60 
ServiceProxy(Context _context, Intent _intent)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 {
onServiceConnected(ComponentName name, IBinder binder)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 
onServiceDisconnected(ComponentName name)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 {
run()95         public void run() throws RemoteException;
96     }
97 
98     private class ProxyRunnable implements Runnable {
99         @Override
run()100         public void run() {
101             try {
102                 mTask.run();
103             } catch (RemoteException e) {
104             }
105         }
106     }
107 
setTimeout(int secs)108     public ServiceProxy setTimeout(int secs) {
109         mTimeout = secs;
110         return this;
111     }
112 
getTimeout()113     public int getTimeout() {
114         return mTimeout;
115     }
116 
endTask()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 
runTask()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 
setTask(ProxyTask task, String name)143     public boolean setTask(ProxyTask task, String name) {
144         mName = name;
145         return setTask(task);
146     }
147 
setTask(ProxyTask task)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 
waitForCompletion()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 
close()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      */
test()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