10d4fc55861ed4393aa82f124f2865695ef564641Marc Blank/* 20d4fc55861ed4393aa82f124f2865695ef564641Marc Blank /* 30d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * Copyright (C) 2011 The Android Open Source Project 40d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * 50d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 60d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * you may not use this file except in compliance with the License. 70d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * You may obtain a copy of the License at 80d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * 90d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * http://www.apache.org/licenses/LICENSE-2.0 100d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * 110d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * Unless required by applicable law or agreed to in writing, software 120d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * distributed under the License is distributed on an "AS IS" BASIS, 130d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 140d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * See the License for the specific language governing permissions and 150d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * limitations under the License. 160d4fc55861ed4393aa82f124f2865695ef564641Marc Blank */ 170d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 180d4fc55861ed4393aa82f124f2865695ef564641Marc Blankpackage com.android.emailcommon.service; 190d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 200d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport android.content.ComponentName; 210d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport android.content.Context; 220d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport android.content.Intent; 230d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport android.content.ServiceConnection; 240d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport android.os.Debug; 250d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport android.os.IBinder; 260d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport android.os.RemoteException; 270d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport android.util.Log; 280d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 290d4fc55861ed4393aa82f124f2865695ef564641Marc Blank/** 300d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * The EmailServiceProxy class provides a simple interface for the UI to call into the various 310d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * EmailService classes (e.g. ExchangeService for EAS). It wraps the service connect/disconnect 320d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * process so that the caller need not be concerned with it. 330d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * 340d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * Use the class like this: 350d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * new EmailServiceClass(context, class).loadAttachment(attachmentId, callback) 360d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * 370d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * Methods without a return value return immediately (i.e. are asynchronous); methods with a 380d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * return value wait for a result from the Service (i.e. they should not be called from the UI 390d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * thread) with a default timeout of 30 seconds (settable) 400d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * 410d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * An EmailServiceProxy object cannot be reused (trying to do so generates a RemoteException) 420d4fc55861ed4393aa82f124f2865695ef564641Marc Blank */ 430d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 440d4fc55861ed4393aa82f124f2865695ef564641Marc Blankpublic abstract class ServiceProxy { 45da3c4b8261825063ddf081e9335823569b813bd3Marc Blank private static final boolean DEBUG_PROXY = false; // DO NOT CHECK THIS IN SET TO TRUE 46fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank private final String mTag; 470d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 480d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private final Context mContext; 490d4fc55861ed4393aa82f124f2865695ef564641Marc Blank protected final Intent mIntent; 500d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private Runnable mRunnable = new ProxyRunnable(); 510d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private ProxyTask mTask; 520d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private String mName = " unnamed"; 530d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private final ServiceConnection mConnection = new ProxyConnection(); 540d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // Service call timeout (in seconds) 550d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private int mTimeout = 45; 56fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank private long mStartTime; 570d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private boolean mDead = false; 580d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 590d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public abstract void onConnected(IBinder binder); 600d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 610d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public ServiceProxy(Context _context, Intent _intent) { 620d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mContext = _context; 630d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mIntent = _intent; 64fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank mTag = getClass().getSimpleName(); 650d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (Debug.isDebuggerConnected()) { 660d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mTimeout <<= 2; 670d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 680d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 690d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 700d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private class ProxyConnection implements ServiceConnection { 710d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void onServiceConnected(ComponentName name, IBinder binder) { 720d4fc55861ed4393aa82f124f2865695ef564641Marc Blank onConnected(binder); 730d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 74bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.v(mTag, "Connected: " + name.getShortClassName()); 750d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 760d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // Run our task on a new thread 770d4fc55861ed4393aa82f124f2865695ef564641Marc Blank new Thread(new Runnable() { 780d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void run() { 798ba8c1648c38cd339b09501a057fc2f5b0658b10Marc Blank try { 808ba8c1648c38cd339b09501a057fc2f5b0658b10Marc Blank runTask(); 818ba8c1648c38cd339b09501a057fc2f5b0658b10Marc Blank } finally { 828ba8c1648c38cd339b09501a057fc2f5b0658b10Marc Blank endTask(); 838ba8c1648c38cd339b09501a057fc2f5b0658b10Marc Blank } 840d4fc55861ed4393aa82f124f2865695ef564641Marc Blank }}).start(); 850d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 860d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 870d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void onServiceDisconnected(ComponentName name) { 880d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 89bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.v(mTag, "Disconnected: " + name.getShortClassName()); 900d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 910d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 920d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 930d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 940d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public interface ProxyTask { 950d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void run() throws RemoteException; 960d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 970d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 980d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private class ProxyRunnable implements Runnable { 990d4fc55861ed4393aa82f124f2865695ef564641Marc Blank @Override 1000d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void run() { 1010d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 1020d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mTask.run(); 1030d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (RemoteException e) { 1040d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1050d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1060d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1070d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1080d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public ServiceProxy setTimeout(int secs) { 1090d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mTimeout = secs; 1100d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return this; 1110d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1120d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1130d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public int getTimeout() { 1140d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return mTimeout; 1150d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1160d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1170d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void endTask() { 1180d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 1190d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mContext.unbindService(mConnection); 1200d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (IllegalArgumentException e) { 1210d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // This can happen if the user ended the activity that was using the service 1220d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // This is harmless, but we've got to catch it 1230d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1240d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1250d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mDead = true; 1260d4fc55861ed4393aa82f124f2865695ef564641Marc Blank synchronized(mConnection) { 1270d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 128fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank Log.v(mTag, "Task " + mName + " completed; disconnecting"); 1290d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1300d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mConnection.notify(); 1310d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1320d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1330d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1340d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private void runTask() { 1350d4fc55861ed4393aa82f124f2865695ef564641Marc Blank Thread thread = new Thread(mRunnable); 1360d4fc55861ed4393aa82f124f2865695ef564641Marc Blank thread.start(); 1370d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 1380d4fc55861ed4393aa82f124f2865695ef564641Marc Blank thread.join(); 1390d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (InterruptedException e) { 1400d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1410d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1420d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1430d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public boolean setTask(ProxyTask task, String name) { 1440d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mName = name; 1450d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return setTask(task); 1460d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1470d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1480d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public boolean setTask(ProxyTask task) throws IllegalStateException { 1490d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (mDead) { 1500d4fc55861ed4393aa82f124f2865695ef564641Marc Blank throw new IllegalStateException(); 1510d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1520d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mTask = task; 153fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank mStartTime = System.currentTimeMillis(); 1540d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 155fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank Log.v(mTag, "Bind requested for task " + mName); 1560d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1570d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return mContext.bindService(mIntent, mConnection, Context.BIND_AUTO_CREATE); 1580d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1590d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1600d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void waitForCompletion() { 1610d4fc55861ed4393aa82f124f2865695ef564641Marc Blank synchronized (mConnection) { 1620d4fc55861ed4393aa82f124f2865695ef564641Marc Blank long time = System.currentTimeMillis(); 1630d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 1640d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 165fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank Log.v(mTag, "Waiting for task " + mName + " to complete..."); 1660d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1670d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mConnection.wait(mTimeout * 1000L); 1680d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (InterruptedException e) { 1690d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // Can be ignored safely 1700d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1710d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 172bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.v(mTag, "Wait for " + mName + " finished in " + 173fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank (System.currentTimeMillis() - time) + "ms"); 1740d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1750d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1760d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1770d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1780d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void close() throws RemoteException { 1790d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (mDead) { 1800d4fc55861ed4393aa82f124f2865695ef564641Marc Blank throw new RemoteException(); 1810d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1820d4fc55861ed4393aa82f124f2865695ef564641Marc Blank endTask(); 1830d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1840d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1850d4fc55861ed4393aa82f124f2865695ef564641Marc Blank /** 1860d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * Connection test; return indicates whether the remote service can be connected to 1870d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * @return the result of trying to connect to the remote service 1880d4fc55861ed4393aa82f124f2865695ef564641Marc Blank */ 1890d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public boolean test() { 1900d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 1910d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return setTask(new ProxyTask() { 1920d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void run() throws RemoteException { 1930d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 194fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank Log.v(mTag, "Connection test succeeded in " + 195fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank (System.currentTimeMillis() - mStartTime) + "ms"); 1960d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1970d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1980d4fc55861ed4393aa82f124f2865695ef564641Marc Blank }, "test"); 1990d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (Exception e) { 2000d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // For any failure, return false. 2010d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return false; 2020d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 2030d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 2040d4fc55861ed4393aa82f124f2865695ef564641Marc Blank} 205