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; 24bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Leeimport android.content.pm.ProviderInfo; 254da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Huimport android.os.AsyncTask; 260d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport android.os.Debug; 270d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport android.os.IBinder; 28b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedyimport android.os.Looper; 290d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport android.os.RemoteException; 30560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy 31fb9deb96c3af56bf422e28e8ae3b7b838f343155Tony Mantlerimport com.android.emailcommon.provider.EmailContent; 32560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogUtils; 330d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 340d4fc55861ed4393aa82f124f2865695ef564641Marc Blank/** 354da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * ServiceProxy is a superclass for proxy objects which make a single call to a service. It handles 364da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * connecting to the service, running a task supplied by the subclass when the connection is ready, 374da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * and disconnecting from the service afterwards. ServiceProxy objects cannot be reused (trying to 384da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * do so generates an {@link IllegalStateException}). 390d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * 404da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * Subclasses must override {@link #onConnected} to store the binder. Then, when the subclass wants 414da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * to make a service call, it should call {@link #setTask}, supplying the {@link ProxyTask} that 424da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * should run when the connection is ready. {@link ProxyTask#run} should implement the necessary 434da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * logic to make the call on the service. 440d4fc55861ed4393aa82f124f2865695ef564641Marc Blank */ 450d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 460d4fc55861ed4393aa82f124f2865695ef564641Marc Blankpublic abstract class ServiceProxy { 4724bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon public static final String EXTRA_FORCE_SHUTDOWN = "ServiceProxy.FORCE_SHUTDOWN"; 4824bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon 49da3c4b8261825063ddf081e9335823569b813bd3Marc Blank private static final boolean DEBUG_PROXY = false; // DO NOT CHECK THIS IN SET TO TRUE 50fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank private final String mTag; 510d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 520d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private final Context mContext; 530d4fc55861ed4393aa82f124f2865695ef564641Marc Blank protected final Intent mIntent; 540d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private ProxyTask mTask; 550d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private String mName = " unnamed"; 560d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private final ServiceConnection mConnection = new ProxyConnection(); 570d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // Service call timeout (in seconds) 580d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private int mTimeout = 45; 59fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank private long mStartTime; 604da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu private boolean mTaskSet = false; 614da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu private boolean mTaskCompleted = false; 620d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 63e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank public static Intent getIntentForEmailPackage(Context context, String actionName) { 64bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee /** 65bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee * We want to scope the intent so that only the Email app will handle it. Unfortunately 66bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee * we found that there are many instances where the package name of the Email app is 67bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee * not what we expect. The easiest way to find the package of the correct app is to 68bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee * see who is the EmailContent.AUTHORITY as there is only one app that can implement 69bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee * the content provider for this authority and this is the right app to handle this intent. 70bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee */ 71fb9deb96c3af56bf422e28e8ae3b7b838f343155Tony Mantler final Intent intent = new Intent(EmailContent.EMAIL_PACKAGE_NAME + "." + actionName); 72bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee final ProviderInfo info = context.getPackageManager().resolveContentProvider( 73bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee EmailContent.AUTHORITY, 0); 74bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee if (info != null) { 75bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee final String packageName = info.packageName; 76bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee intent.setPackage(packageName); 77bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee } else { 78bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee LogUtils.e(LogUtils.TAG, "Could not find the Email Content Provider"); 79bca4f9fcfb2301cd4bfdb9a4785f299f90e4263cAnthony Lee } 80fb9deb96c3af56bf422e28e8ae3b7b838f343155Tony Mantler return intent; 81e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank } 82e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank 834da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu /** 844da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * This function is called after the proxy connects to the service but before it runs its task. 854da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * Subclasses must override this to store the binder correctly. 864da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * @param binder The service IBinder. 874da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu */ 880d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public abstract void onConnected(IBinder binder); 890d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 900d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public ServiceProxy(Context _context, Intent _intent) { 910d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mContext = _context; 920d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mIntent = _intent; 93fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank mTag = getClass().getSimpleName(); 940d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (Debug.isDebuggerConnected()) { 950d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mTimeout <<= 2; 960d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 970d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 980d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 990d4fc55861ed4393aa82f124f2865695ef564641Marc Blank private class ProxyConnection implements ServiceConnection { 1004da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu @Override 1010d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void onServiceConnected(ComponentName name, IBinder binder) { 1020d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 103560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.v(mTag, "Connected: " + name.getShortClassName() + " at " + 1040b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank (System.currentTimeMillis() - mStartTime) + "ms"); 1050d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1064da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu 1074da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu // Let subclasses handle the binder. 1084da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu onConnected(binder); 1094da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu 1104da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu // Do our work in another thread. 1114da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu new AsyncTask<Void, Void, Void>() { 1124da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu @Override 1134da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu protected Void doInBackground(Void... params) { 1144da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu try { 1154da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu mTask.run(); 1164da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu } catch (RemoteException e) { 1174da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu } 1188ba8c1648c38cd339b09501a057fc2f5b0658b10Marc Blank try { 1194da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu // Each ServiceProxy handles just one task, so we unbind after we're 1204da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu // done with our work. 1214da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu mContext.unbindService(mConnection); 1226ef1621f444538337253f3aeae63dfe0098e238bAnthony Lee } catch (RuntimeException e) { 1236ef1621f444538337253f3aeae63dfe0098e238bAnthony Lee // The exceptions that are thrown here look like IllegalStateException, 1246ef1621f444538337253f3aeae63dfe0098e238bAnthony Lee // IllegalArgumentException and RuntimeException. Catching RuntimeException 1256ef1621f444538337253f3aeae63dfe0098e238bAnthony Lee // which get them all. Reasons for these exceptions include services that 1266ef1621f444538337253f3aeae63dfe0098e238bAnthony Lee // have already been stopped or unbound. This can happen if the user ended 1276ef1621f444538337253f3aeae63dfe0098e238bAnthony Lee // the activity that was using the service. This is harmless, but we've got 1286ef1621f444538337253f3aeae63dfe0098e238bAnthony Lee // to catch it. 1296ef1621f444538337253f3aeae63dfe0098e238bAnthony Lee LogUtils.e(mTag, e, "RuntimeException when trying to unbind from service"); 1304da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu } 1314da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu mTaskCompleted = true; 1324da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu synchronized(mConnection) { 1334da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu if (DEBUG_PROXY) { 134560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.v(mTag, "Task " + mName + " completed; disconnecting"); 1354da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu } 1364da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu mConnection.notify(); 1378ba8c1648c38cd339b09501a057fc2f5b0658b10Marc Blank } 1384da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu return null; 1394da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu } 1404da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu }.execute(); 1410d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1420d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1434da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu @Override 1440d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void onServiceDisconnected(ComponentName name) { 1450d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 146560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.v(mTag, "Disconnected: " + name.getShortClassName() + " at " + 1470b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank (System.currentTimeMillis() - mStartTime) + "ms"); 1480d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1490d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1500d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1510d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1524da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu protected interface ProxyTask { 1530d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void run() throws RemoteException; 1540d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1550d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1560d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public ServiceProxy setTimeout(int secs) { 1570d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mTimeout = secs; 1580d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return this; 1590d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1600d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1610d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public int getTimeout() { 1620d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return mTimeout; 1630d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1640d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1654da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu protected boolean setTask(ProxyTask task, String name) throws IllegalStateException { 1664da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu if (mTaskSet) { 1674da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu throw new IllegalStateException("Cannot call setTask twice on the same ServiceProxy."); 1680d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1694da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu mTaskSet = true; 1700d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mName = name; 1710d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mTask = task; 172fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank mStartTime = System.currentTimeMillis(); 1730d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 174560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.v(mTag, "Bind requested for task " + mName); 1750d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1760d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return mContext.bindService(mIntent, mConnection, Context.BIND_AUTO_CREATE); 1770d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 1780d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 1794da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu /** 1804da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * Callers that want to wait on the {@link ProxyTask} should call this immediately after calling 1814da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * {@link #setTask}. This will wait until the task completes, up to the timeout (which can be 1824da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu * set with {@link #setTimeout}). 1834da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu */ 1844da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu protected void waitForCompletion() { 185b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy /* 186b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy * onServiceConnected() is always called on the main thread, and we block the current thread 187b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy * for up to 10 seconds as a timeout. If we're currently on the main thread, 188b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy * onServiceConnected() is not called until our timeout elapses (and the UI is frozen for 189b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy * the duration). 190b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy */ 191b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy if (Looper.myLooper() == Looper.getMainLooper()) { 192b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy throw new IllegalStateException("This cannot be called on the main thread."); 193b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy } 194b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy 1950d4fc55861ed4393aa82f124f2865695ef564641Marc Blank synchronized (mConnection) { 1960d4fc55861ed4393aa82f124f2865695ef564641Marc Blank long time = System.currentTimeMillis(); 1970d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 1980d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 199560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.v(mTag, "Waiting for task " + mName + " to complete..."); 2000d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 2010d4fc55861ed4393aa82f124f2865695ef564641Marc Blank mConnection.wait(mTimeout * 1000L); 2020d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (InterruptedException e) { 2030d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // Can be ignored safely 2040d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 2050d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 206560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.v(mTag, "Wait for " + mName + 2074da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu (mTaskCompleted ? " finished in " : " timed out in ") + 208fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank (System.currentTimeMillis() - time) + "ms"); 2090d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 2100d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 2110d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 2120d4fc55861ed4393aa82f124f2865695ef564641Marc Blank 2130d4fc55861ed4393aa82f124f2865695ef564641Marc Blank /** 2140d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * Connection test; return indicates whether the remote service can be connected to 2150d4fc55861ed4393aa82f124f2865695ef564641Marc Blank * @return the result of trying to connect to the remote service 2160d4fc55861ed4393aa82f124f2865695ef564641Marc Blank */ 2170d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public boolean test() { 2180d4fc55861ed4393aa82f124f2865695ef564641Marc Blank try { 2190d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return setTask(new ProxyTask() { 2204da36412922f19e63638c593537a2bf64ab57bd8Yu Ping Hu @Override 2210d4fc55861ed4393aa82f124f2865695ef564641Marc Blank public void run() throws RemoteException { 2220d4fc55861ed4393aa82f124f2865695ef564641Marc Blank if (DEBUG_PROXY) { 223560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.v(mTag, "Connection test succeeded in " + 224fdec974c93817988a00248b7d223ec9f7f20ecb8Marc Blank (System.currentTimeMillis() - mStartTime) + "ms"); 2250d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 2260d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 2270d4fc55861ed4393aa82f124f2865695ef564641Marc Blank }, "test"); 2280d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } catch (Exception e) { 2290d4fc55861ed4393aa82f124f2865695ef564641Marc Blank // For any failure, return false. 2300d4fc55861ed4393aa82f124f2865695ef564641Marc Blank return false; 2310d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 2320d4fc55861ed4393aa82f124f2865695ef564641Marc Blank } 2330d4fc55861ed4393aa82f124f2865695ef564641Marc Blank} 234