ServiceProxy.java revision c28bf353190eb576072a8fd2f98821424144876e
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 18package com.android.emailcommon.service; 19 20import android.content.ComponentName; 21import android.content.Context; 22import android.content.Intent; 23import android.content.ServiceConnection; 24import android.os.Debug; 25import android.os.IBinder; 26import android.os.RemoteException; 27import 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 44public abstract class ServiceProxy { 45 private static final boolean DEBUG_PROXY = true; // STOPSHIP 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 runTask(); 80 }}).start(); 81 } 82 83 public void onServiceDisconnected(ComponentName name) { 84 if (DEBUG_PROXY) { 85 Log.v(mTag, "Disconnected: " + name.getShortClassName()); 86 } 87 } 88 } 89 90 public interface ProxyTask { 91 public void run() throws RemoteException; 92 } 93 94 private class ProxyRunnable implements Runnable { 95 @Override 96 public void run() { 97 try { 98 mTask.run(); 99 } catch (RemoteException e) { 100 } 101 } 102 } 103 104 public ServiceProxy setTimeout(int secs) { 105 mTimeout = secs; 106 return this; 107 } 108 109 public int getTimeout() { 110 return mTimeout; 111 } 112 113 public void endTask() { 114 try { 115 mContext.unbindService(mConnection); 116 } catch (IllegalArgumentException e) { 117 // This can happen if the user ended the activity that was using the service 118 // This is harmless, but we've got to catch it 119 } 120 121 mDead = true; 122 synchronized(mConnection) { 123 if (DEBUG_PROXY) { 124 Log.v(mTag, "Task " + mName + " completed; disconnecting"); 125 } 126 mConnection.notify(); 127 } 128 } 129 130 private void runTask() { 131 Thread thread = new Thread(mRunnable); 132 thread.start(); 133 try { 134 thread.join(); 135 } catch (InterruptedException e) { 136 } 137 endTask(); 138 } 139 140 public boolean setTask(ProxyTask task, String name) { 141 mName = name; 142 return setTask(task); 143 } 144 145 public boolean setTask(ProxyTask task) throws IllegalStateException { 146 if (mDead) { 147 throw new IllegalStateException(); 148 } 149 mTask = task; 150 mStartTime = System.currentTimeMillis(); 151 if (DEBUG_PROXY) { 152 Log.v(mTag, "Bind requested for task " + mName); 153 } 154 return mContext.bindService(mIntent, mConnection, Context.BIND_AUTO_CREATE); 155 } 156 157 public void waitForCompletion() { 158 synchronized (mConnection) { 159 long time = System.currentTimeMillis(); 160 try { 161 if (DEBUG_PROXY) { 162 Log.v(mTag, "Waiting for task " + mName + " to complete..."); 163 } 164 mConnection.wait(mTimeout * 1000L); 165 } catch (InterruptedException e) { 166 // Can be ignored safely 167 } 168 if (DEBUG_PROXY) { 169 Log.v(mTag, "Wait for " + mName + " finished in " + 170 (System.currentTimeMillis() - time) + "ms"); 171 } 172 } 173 } 174 175 public void close() throws RemoteException { 176 if (mDead) { 177 throw new RemoteException(); 178 } 179 endTask(); 180 } 181 182 /** 183 * Connection test; return indicates whether the remote service can be connected to 184 * @return the result of trying to connect to the remote service 185 */ 186 public boolean test() { 187 try { 188 return setTask(new ProxyTask() { 189 public void run() throws RemoteException { 190 if (DEBUG_PROXY) { 191 Log.v(mTag, "Connection test succeeded in " + 192 (System.currentTimeMillis() - mStartTime) + "ms"); 193 } 194 } 195 }, "test"); 196 } catch (Exception e) { 197 // For any failure, return false. 198 return false; 199 } 200 } 201} 202