ServiceProxy.java revision 0d4fc55861ed4393aa82f124f2865695ef564641
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 { 450d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private static final boolean DEBUG_PROXY = true; // STOPSHIP DO NOT CHECK THIS IN SET TO TRUE 460d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private static final String TAG = "ServiceProxy"; 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; 560d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private boolean mDead = false; 570d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 580d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public abstract void onConnected(IBinder binder); 590d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 600d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public ServiceProxy(Context _context, Intent _intent) { 610d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mContext = _context; 620d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mIntent = _intent; 630d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (Debug.isDebuggerConnected()) { 640d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mTimeout <<= 2; 650d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 660d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 670d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 680d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private class ProxyConnection implements ServiceConnection { 690d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void onServiceConnected(ComponentName name, IBinder binder) { 700d4fc55861ed4393aa82f124f2865695ef564641Marc Blank onConnected(binder); 710d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 720d4fc55861ed4393aa82f124f2865695ef564641Marc Blank Log.v(TAG, "Connected: " + name.getShortClassName()); 730d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 740d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // Run our task on a new thread 750d4fc55861ed4393aa82f124f2865695ef564641Marc Blank new Thread(new Runnable() { 760d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void run() { 770d4fc55861ed4393aa82f124f2865695ef564641Marc Blank runTask(); 780d4fc55861ed4393aa82f124f2865695ef564641Marc Blank }}).start(); 790d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 800d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 810d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void onServiceDisconnected(ComponentName name) { 820d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 830d4fc55861ed4393aa82f124f2865695ef564641Marc Blank Log.v(TAG, "Disconnected: " + name.getShortClassName()); 840d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 850d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 860d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 870d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 880d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public interface ProxyTask { 890d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void run() throws RemoteException; 900d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 910d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 920d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private class ProxyRunnable implements Runnable { 930d4fc55861ed4393aa82f124f2865695ef564641Marc Blank @Override 940d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void run() { 950d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 960d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mTask.run(); 970d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (RemoteException e) { 980d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 990d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1000d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1010d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1020d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public ServiceProxy setTimeout(int secs) { 1030d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mTimeout = secs; 1040d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return this; 1050d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1060d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1070d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public int getTimeout() { 1080d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return mTimeout; 1090d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1100d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1110d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void endTask() { 1120d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 1130d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mContext.unbindService(mConnection); 1140d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (IllegalArgumentException e) { 1150d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // This can happen if the user ended the activity that was using the service 1160d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // This is harmless, but we've got to catch it 1170d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1180d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1190d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mDead = true; 1200d4fc55861ed4393aa82f124f2865695ef564641Marc Blank synchronized(mConnection) { 1210d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 1220d4fc55861ed4393aa82f124f2865695ef564641Marc Blank Log.v(TAG, "Task " + mName + " completed; disconnecting"); 1230d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1240d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mConnection.notify(); 1250d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1260d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1270d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1280d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private void runTask() { 1290d4fc55861ed4393aa82f124f2865695ef564641Marc Blank Thread thread = new Thread(mRunnable); 1300d4fc55861ed4393aa82f124f2865695ef564641Marc Blank thread.start(); 1310d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 1320d4fc55861ed4393aa82f124f2865695ef564641Marc Blank thread.join(); 1330d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (InterruptedException e) { 1340d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1350d4fc55861ed4393aa82f124f2865695ef564641Marc Blank endTask(); 1360d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1370d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1380d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public boolean setTask(ProxyTask task, String name) { 1390d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mName = name; 1400d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return setTask(task); 1410d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1420d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1430d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public boolean setTask(ProxyTask task) throws IllegalStateException { 1440d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (mDead) { 1450d4fc55861ed4393aa82f124f2865695ef564641Marc Blank throw new IllegalStateException(); 1460d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1470d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mTask = task; 1480d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 1490d4fc55861ed4393aa82f124f2865695ef564641Marc Blank Log.v(TAG, "Bind requested for task " + mName); 1500d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1510d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return mContext.bindService(mIntent, mConnection, Context.BIND_AUTO_CREATE); 1520d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1530d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1540d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void waitForCompletion() { 1550d4fc55861ed4393aa82f124f2865695ef564641Marc Blank synchronized (mConnection) { 1560d4fc55861ed4393aa82f124f2865695ef564641Marc Blank long time = System.currentTimeMillis(); 1570d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 1580d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 1590d4fc55861ed4393aa82f124f2865695ef564641Marc Blank Log.v(TAG, "Waiting for task " + mName + " to complete..."); 1600d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1610d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mConnection.wait(mTimeout * 1000L); 1620d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (InterruptedException e) { 1630d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // Can be ignored safely 1640d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1650d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 1660d4fc55861ed4393aa82f124f2865695ef564641Marc Blank Log.v(TAG, "Wait finished in " + (System.currentTimeMillis() - time) + "ms"); 1670d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1680d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1690d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1700d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1710d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void close() throws RemoteException { 1720d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (mDead) { 1730d4fc55861ed4393aa82f124f2865695ef564641Marc Blank throw new RemoteException(); 1740d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1750d4fc55861ed4393aa82f124f2865695ef564641Marc Blank endTask(); 1760d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1770d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1780d4fc55861ed4393aa82f124f2865695ef564641Marc Blank /** 1790d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * Connection test; return indicates whether the remote service can be connected to 1800d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * @return the result of trying to connect to the remote service 1810d4fc55861ed4393aa82f124f2865695ef564641Marc Blank */ 1820d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public boolean test() { 1830d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 1840d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return setTask(new ProxyTask() { 1850d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void run() throws RemoteException { 1860d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 1870d4fc55861ed4393aa82f124f2865695ef564641Marc Blank Log.v(TAG, "Connection test succeeded"); 1880d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1890d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1900d4fc55861ed4393aa82f124f2865695ef564641Marc Blank }, "test"); 1910d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (Exception e) { 1920d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // For any failure, return false. 1930d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return false; 1940d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1950d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1960d4fc55861ed4393aa82f124f2865695ef564641Marc Blank} 197